[FIX] handling of focus on m2o fields (in editable list row)
* Throw out focusin/focusout: the m2o widget's completion list is created at the page top (body) so the editable listview can't generically handle arbitrary widgets via mere focusin/focusout. * Move responsibility of focus/blur events to the form view and its widgets. * Events could not just be named ``focus`` and ``blur`` due to usage of jquery's event system: jquery will automatically call a method of the event's name if it exists on the object, which conflicts with .web.form.Field#focus and leads to an infinite loop (as Field#focus focuses the field's root element, which triggers the focus event, which calls the focus method,...) => form-* and widget-*, can be switched back in trunk * m2o mess kind-of complex, basically: - the core input and the menu button behave as blur/focus triggers - when the autocompletion list is clicked, it will temporarily remove the focus from the input (blurring it), and put it back later... on a timer. Issue is the timer, we don't want to rely on having a bigger timer (as later revisions of the library may change our timings and it's iffy to rely on timers conserving order perfectly); on the other hand we know the focus *will* come back to the input eventually. So we can just avoid propagating blur iif the blur is the consequence of having clicked on the completion list. - roughly the same thing happens when clicking on $drop_down (after fixing the handling of its final focus to be consistent, as $drop_down would not re-focus the input if it was *closing* the completion list) - pretty sure the menu thing does *not work at all*, but I don't have the courage of fixing it before committing this part. Date/datetime widget remains to be handled, basically the core focus handling is the same as in e.g. a charfield *but* needs to handle (and ignore) loss of focus from clicking inside the picker widget. Expecting the level of suck to reach unknown heights. bzr revid: xmo@openerp.com-20120619072518-lsrhzij5asxt2aea
This commit is contained in:
parent
70b0af1375
commit
0516533c16
|
@ -80,6 +80,7 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView#
|
||||||
this.sidebar.stop();
|
this.sidebar.stop();
|
||||||
}
|
}
|
||||||
_.each(this.widgets, function(w) {
|
_.each(this.widgets, function(w) {
|
||||||
|
$(w).unbind('.formBlur');
|
||||||
w.stop();
|
w.stop();
|
||||||
});
|
});
|
||||||
this._super();
|
this._super();
|
||||||
|
@ -100,6 +101,8 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView#
|
||||||
this.$element.html(this.rendered);
|
this.$element.html(this.rendered);
|
||||||
_.each(this.widgets, function(w) {
|
_.each(this.widgets, function(w) {
|
||||||
w.start();
|
w.start();
|
||||||
|
$(w).bind('widget-focus.formBlur', self.proxy('widgetFocused'))
|
||||||
|
.bind('widget-blur.formBlur', self.proxy('widgetBlurred'));
|
||||||
});
|
});
|
||||||
this.$form_header = this.$element.find('.oe_form_header:first');
|
this.$form_header = this.$element.find('.oe_form_header:first');
|
||||||
this.$form_header.find('div.oe_form_pager button[data-pager-action]').click(function() {
|
this.$form_header.find('div.oe_form_pager button[data-pager-action]').click(function() {
|
||||||
|
@ -130,6 +133,21 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView#
|
||||||
this.has_been_loaded.resolve();
|
this.has_been_loaded.resolve();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
widgetFocused: function() {
|
||||||
|
if (this.__blur_timeout) {
|
||||||
|
clearTimeout(this.__blur_timeout);
|
||||||
|
delete this.__blur_timeout;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
widgetBlurred: function() {
|
||||||
|
var self = this;
|
||||||
|
// clear timeout, if any
|
||||||
|
this.widgetFocused();
|
||||||
|
this.__blur_timeout = setTimeout(function () {
|
||||||
|
$(self).trigger('form-blur');
|
||||||
|
}, 0);
|
||||||
|
},
|
||||||
|
|
||||||
do_load_state: function(state, warm) {
|
do_load_state: function(state, warm) {
|
||||||
if (state.id && this.datarecord.id != state.id) {
|
if (state.id && this.datarecord.id != state.id) {
|
||||||
if (!this.dataset.get_id_index(state.id)) {
|
if (!this.dataset.get_id_index(state.id)) {
|
||||||
|
@ -926,6 +944,13 @@ openerp.web.form.Widget = openerp.web.OldWidget.extend(/** @lends openerp.web.fo
|
||||||
|
|
||||||
this.width = this.node.attrs.width;
|
this.width = this.node.attrs.width;
|
||||||
},
|
},
|
||||||
|
setupFocus: function ($e) {
|
||||||
|
var self = this;
|
||||||
|
$e.bind({
|
||||||
|
focus: function () { $(self).trigger('widget-focus'); },
|
||||||
|
blur: function () { $(self).trigger('widget-blur'); }
|
||||||
|
});
|
||||||
|
},
|
||||||
start: function() {
|
start: function() {
|
||||||
this.$element = this.view.$element.find(
|
this.$element = this.view.$element.find(
|
||||||
'.' + this.element_class.replace(/[^\r\n\f0-9A-Za-z_-]/g, "\\$&"));
|
'.' + this.element_class.replace(/[^\r\n\f0-9A-Za-z_-]/g, "\\$&"));
|
||||||
|
@ -1212,10 +1237,12 @@ openerp.web.form.WidgetButton = openerp.web.form.Widget.extend({
|
||||||
},
|
},
|
||||||
start: function() {
|
start: function() {
|
||||||
this._super.apply(this, arguments);
|
this._super.apply(this, arguments);
|
||||||
this.$element.find("button").click(this.on_click);
|
var $button = this.$element.find('button');
|
||||||
|
$button.click(this.on_click);
|
||||||
if (this.help || openerp.connection.debug) {
|
if (this.help || openerp.connection.debug) {
|
||||||
this.do_attach_tooltip();
|
this.do_attach_tooltip();
|
||||||
}
|
}
|
||||||
|
this.setupFocus($button);
|
||||||
},
|
},
|
||||||
on_click: function() {
|
on_click: function() {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
@ -1329,11 +1356,13 @@ openerp.web.form.WidgetLabel = openerp.web.form.Widget.extend({
|
||||||
if (this['for'] && (this['for'].help || openerp.connection.debug)) {
|
if (this['for'] && (this['for'].help || openerp.connection.debug)) {
|
||||||
this.do_attach_tooltip(self['for']);
|
this.do_attach_tooltip(self['for']);
|
||||||
}
|
}
|
||||||
this.$element.find("label").dblclick(function() {
|
var $label = this.$element.find('label');
|
||||||
|
$label.dblclick(function() {
|
||||||
var widget = self['for'] || self;
|
var widget = self['for'] || self;
|
||||||
openerp.log(widget.element_class , widget);
|
openerp.log(widget.element_class , widget);
|
||||||
window.w = widget;
|
window.w = widget;
|
||||||
});
|
});
|
||||||
|
this.setupFocus($label);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1460,7 +1489,9 @@ openerp.web.form.FieldChar = openerp.web.form.Field.extend({
|
||||||
},
|
},
|
||||||
start: function() {
|
start: function() {
|
||||||
this._super.apply(this, arguments);
|
this._super.apply(this, arguments);
|
||||||
this.$element.find('input').change(this.on_ui_change);
|
var $input = this.$element.find('input');
|
||||||
|
$input.change(this.on_ui_change);
|
||||||
|
this.setupFocus($input);
|
||||||
},
|
},
|
||||||
set_value: function(value) {
|
set_value: function(value) {
|
||||||
this._super.apply(this, arguments);
|
this._super.apply(this, arguments);
|
||||||
|
@ -1501,7 +1532,9 @@ openerp.web.form.FieldEmail = openerp.web.form.FieldChar.extend({
|
||||||
template: 'FieldEmail',
|
template: 'FieldEmail',
|
||||||
start: function() {
|
start: function() {
|
||||||
this._super.apply(this, arguments);
|
this._super.apply(this, arguments);
|
||||||
this.$element.find('button').click(this.on_button_clicked);
|
var $button = this.$element.find('button');
|
||||||
|
$button.click(this.on_button_clicked);
|
||||||
|
this.setupFocus($button);
|
||||||
},
|
},
|
||||||
on_button_clicked: function() {
|
on_button_clicked: function() {
|
||||||
if (!this.value || !this.is_valid()) {
|
if (!this.value || !this.is_valid()) {
|
||||||
|
@ -1516,7 +1549,9 @@ openerp.web.form.FieldUrl = openerp.web.form.FieldChar.extend({
|
||||||
template: 'FieldUrl',
|
template: 'FieldUrl',
|
||||||
start: function() {
|
start: function() {
|
||||||
this._super.apply(this, arguments);
|
this._super.apply(this, arguments);
|
||||||
this.$element.find('button').click(this.on_button_clicked);
|
var $button = this.$element.find('button');
|
||||||
|
$button.click(this.on_button_clicked);
|
||||||
|
this.setupFocus($button);
|
||||||
},
|
},
|
||||||
on_button_clicked: function() {
|
on_button_clicked: function() {
|
||||||
if (!this.value) {
|
if (!this.value) {
|
||||||
|
@ -1641,6 +1676,7 @@ openerp.web.form.FieldDatetime = openerp.web.form.Field.extend({
|
||||||
this.datewidget = this.build_widget();
|
this.datewidget = this.build_widget();
|
||||||
this.datewidget.on_change.add_last(this.on_ui_change);
|
this.datewidget.on_change.add_last(this.on_ui_change);
|
||||||
this.datewidget.appendTo(this.$element);
|
this.datewidget.appendTo(this.$element);
|
||||||
|
// FIXME: handle focus on datetime field
|
||||||
},
|
},
|
||||||
set_value: function(value) {
|
set_value: function(value) {
|
||||||
this._super(value);
|
this._super(value);
|
||||||
|
@ -1671,8 +1707,10 @@ openerp.web.form.FieldText = openerp.web.form.Field.extend({
|
||||||
template: 'FieldText',
|
template: 'FieldText',
|
||||||
start: function() {
|
start: function() {
|
||||||
this._super.apply(this, arguments);
|
this._super.apply(this, arguments);
|
||||||
this.$element.find('textarea').change(this.on_ui_change);
|
var $textarea = this.$element.find('textarea');
|
||||||
|
$textarea.change(this.on_ui_change);
|
||||||
this.resized = false;
|
this.resized = false;
|
||||||
|
this.setupFocus($textarea);
|
||||||
},
|
},
|
||||||
set_value: function(value) {
|
set_value: function(value) {
|
||||||
this._super.apply(this, arguments);
|
this._super.apply(this, arguments);
|
||||||
|
@ -1733,7 +1771,9 @@ openerp.web.form.FieldBoolean = openerp.web.form.Field.extend({
|
||||||
start: function() {
|
start: function() {
|
||||||
var self = this;
|
var self = this;
|
||||||
this._super.apply(this, arguments);
|
this._super.apply(this, arguments);
|
||||||
this.$element.find('input').click(self.on_ui_change);
|
var $input = this.$element.find('input');
|
||||||
|
$input.click(self.on_ui_change);
|
||||||
|
this.setupFocus($input);
|
||||||
},
|
},
|
||||||
set_value: function(value) {
|
set_value: function(value) {
|
||||||
this._super.apply(this, arguments);
|
this._super.apply(this, arguments);
|
||||||
|
@ -1803,7 +1843,8 @@ openerp.web.form.FieldSelection = openerp.web.form.Field.extend({
|
||||||
// row
|
// row
|
||||||
var ischanging = false;
|
var ischanging = false;
|
||||||
this._super.apply(this, arguments);
|
this._super.apply(this, arguments);
|
||||||
this.$element.find('select')
|
var $select = this.$element.find('select');
|
||||||
|
$select
|
||||||
.change(this.on_ui_change)
|
.change(this.on_ui_change)
|
||||||
.change(function () { ischanging = true; })
|
.change(function () { ischanging = true; })
|
||||||
.click(function () { ischanging = false; })
|
.click(function () { ischanging = false; })
|
||||||
|
@ -1812,6 +1853,7 @@ openerp.web.form.FieldSelection = openerp.web.form.Field.extend({
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
ischanging = false;
|
ischanging = false;
|
||||||
});
|
});
|
||||||
|
this.setupFocus($select);
|
||||||
},
|
},
|
||||||
set_value: function(value) {
|
set_value: function(value) {
|
||||||
value = value === null ? false : value;
|
value = value === null ? false : value;
|
||||||
|
@ -1983,8 +2025,8 @@ openerp.web.form.FieldMany2One = openerp.web.form.Field.extend({
|
||||||
} else {
|
} else {
|
||||||
self.$input.autocomplete("search");
|
self.$input.autocomplete("search");
|
||||||
}
|
}
|
||||||
self.$input.focus();
|
|
||||||
}
|
}
|
||||||
|
self.$input.focus();
|
||||||
});
|
});
|
||||||
var anyoneLoosesFocus = function() {
|
var anyoneLoosesFocus = function() {
|
||||||
if (!self.$input.is(":focus") &&
|
if (!self.$input.is(":focus") &&
|
||||||
|
@ -2022,6 +2064,7 @@ openerp.web.form.FieldMany2One = openerp.web.form.Field.extend({
|
||||||
minLength: 0,
|
minLength: 0,
|
||||||
delay: 0
|
delay: 0
|
||||||
});
|
});
|
||||||
|
|
||||||
// used to correct a bug when selecting an element by pushing 'enter' in an editable list
|
// used to correct a bug when selecting an element by pushing 'enter' in an editable list
|
||||||
this.$input.keyup(function(e) {
|
this.$input.keyup(function(e) {
|
||||||
if (e.which === 13) {
|
if (e.which === 13) {
|
||||||
|
@ -2030,6 +2073,23 @@ openerp.web.form.FieldMany2One = openerp.web.form.Field.extend({
|
||||||
}
|
}
|
||||||
isSelecting = false;
|
isSelecting = false;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
var picking_completion = false;
|
||||||
|
this.$input.autocomplete('widget').add(this.$drop_down)
|
||||||
|
.mousedown(function () {
|
||||||
|
picking_completion = true;
|
||||||
|
});
|
||||||
|
this.$input.add(this.$menu_btn).bind({
|
||||||
|
blur: function () {
|
||||||
|
if (!picking_completion) {
|
||||||
|
$(self).trigger('widget-blur');
|
||||||
|
}
|
||||||
|
picking_completion = false;
|
||||||
|
},
|
||||||
|
focus: function () {
|
||||||
|
$(self).trigger('widget-focus');
|
||||||
|
}
|
||||||
|
})
|
||||||
},
|
},
|
||||||
// autocomplete component content handling
|
// autocomplete component content handling
|
||||||
get_search_result: function(request, response) {
|
get_search_result: function(request, response) {
|
||||||
|
@ -2666,28 +2726,13 @@ openerp.web.form.One2ManyList = openerp.web.ListView.List.extend({
|
||||||
render_row_as_form: function () {
|
render_row_as_form: function () {
|
||||||
var self = this;
|
var self = this;
|
||||||
return this._super.apply(this, arguments).then(function () {
|
return this._super.apply(this, arguments).then(function () {
|
||||||
self.setup_save_on_row_blur();
|
$(self.edition_form).bind('form-blur', function () {
|
||||||
|
if (!self.edition_form.widget_is_stopped) {
|
||||||
|
self.view.ensure_saved();
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
setup_save_on_row_blur: function () {
|
|
||||||
var self = this;
|
|
||||||
var form = this.edition_form;
|
|
||||||
this.edition_form.$element.bind({
|
|
||||||
focusout: function () {
|
|
||||||
self._save_row_timeout = setTimeout(function () {
|
|
||||||
if (form.widget_is_stopped) {
|
|
||||||
// Saved or cancelled already, maybe?
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
self.view.ensure_saved();
|
|
||||||
}, 0);
|
|
||||||
},
|
|
||||||
focusin: function () {
|
|
||||||
clearTimeout(self._save_row_timeout);
|
|
||||||
delete self._save_row_timeout;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
openerp.web.form.One2ManyFormView = openerp.web.FormView.extend({
|
openerp.web.form.One2ManyFormView = openerp.web.FormView.extend({
|
||||||
|
@ -3207,9 +3252,14 @@ openerp.web.form.FieldReference = openerp.web.form.Field.extend({
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
start: function() {
|
start: function() {
|
||||||
|
var self = this;
|
||||||
this._super();
|
this._super();
|
||||||
this.selection.start();
|
this.selection.start();
|
||||||
this.m2o.start();
|
this.m2o.start();
|
||||||
|
$(this.selection).add($(this.m2o)).bind({
|
||||||
|
'focus': function () { $(self).trigger('widget-focus'); },
|
||||||
|
'blur': function () { $(self).trigger('widget-blur'); }
|
||||||
|
})
|
||||||
},
|
},
|
||||||
is_valid: function() {
|
is_valid: function() {
|
||||||
return this.required === false || typeof(this.get_value()) === 'string';
|
return this.required === false || typeof(this.get_value()) === 'string';
|
||||||
|
|
Loading…
Reference in New Issue