From eb83b58e328ce3ca8743237fbf757e4c1ac1feb0 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Tue, 12 Jun 2012 17:07:08 +0200 Subject: [PATCH 01/23] [IMP] Deferred usage in editable listview bzr revid: xmo@openerp.com-20120612150708-0z5dsclcrtj29st3 --- .../web/static/src/js/view_list_editable.js | 25 ++++++++----------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/addons/web/static/src/js/view_list_editable.js b/addons/web/static/src/js/view_list_editable.js index cc6b61c8951..57ac2bfa1de 100644 --- a/addons/web/static/src/js/view_list_editable.js +++ b/addons/web/static/src/js/view_list_editable.js @@ -124,18 +124,15 @@ openerp.web.list_editable = function (openerp) { * Checks if a record is being edited, and if so cancels it */ cancel_pending_edition: function () { - var self = this, cancelled = $.Deferred(); + var self = this, cancelled; if (!this.edition) { - cancelled.resolve(); - return cancelled.promise(); + return $.when(); } - if (this.edition_id != null) { - this.reload_record(self.records.get(this.edition_id)).then(function () { - cancelled.resolve(); - }); + if (this.edition_id) { + cancelled = this.reload_record(this.records.get(this.edition_id)); } else { - cancelled.resolve(); + cancelled = $.when(); } cancelled.then(function () { self.view.unpad_columns(); @@ -146,7 +143,7 @@ openerp.web.list_editable = function (openerp) { delete self.edition; }); this.pad_table_to(5); - return cancelled.promise(); + return cancelled; }, /** * Adapts this list's view description to be suitable to the inner form @@ -242,8 +239,8 @@ openerp.web.list_editable = function (openerp) { }); // HA HA self.edition_form.appendTo(); + // put in $.when just in case FormView.on_loaded becomes asynchronous $.when(self.edition_form.on_loaded(self.get_form_fields_view())).then(function () { - // put in $.when just in case FormView.on_loaded becomes asynchronous $new_row.find('> td') .addClass('oe-field-cell') .removeAttr('width') @@ -307,7 +304,7 @@ openerp.web.list_editable = function (openerp) { */ save_row: function () { //noinspection JSPotentiallyInvalidConstructorUsage - var self = this, done = $.Deferred(); + var self = this; return this.edition_form .do_save(null, this.options.editable === 'top') .pipe(function (result) { @@ -327,8 +324,8 @@ openerp.web.list_editable = function (openerp) { created: result.created || false, edited_record: edited_record }; - }, null); - }, null); + }); + }); }, /** * If the current list is being edited, ensures it's saved @@ -338,7 +335,7 @@ openerp.web.list_editable = function (openerp) { return this.save_row(); } //noinspection JSPotentiallyInvalidConstructorUsage - return $.Deferred().resolve().promise(); + return $.when(); }, /** * Cancels the edition of the row for the current dataset index From 0240ab1682e423c7b74bb594a2213591b5f1e3ff Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Tue, 12 Jun 2012 17:08:20 +0200 Subject: [PATCH 02/23] [CHG] try saving any row under edition when starting an (other) row edition editable lists should not lose data unless explicitly asked to forget about it by user action bzr revid: xmo@openerp.com-20120612150820-z97jxme1vcu3x3jd --- addons/web/static/src/js/view_list_editable.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/web/static/src/js/view_list_editable.js b/addons/web/static/src/js/view_list_editable.js index 57ac2bfa1de..07feed91bbd 100644 --- a/addons/web/static/src/js/view_list_editable.js +++ b/addons/web/static/src/js/view_list_editable.js @@ -194,7 +194,7 @@ openerp.web.list_editable = function (openerp) { }, render_row_as_form: function (row) { var self = this; - this.cancel_pending_edition().then(function () { + this.ensure_saved().then(function () { var record_id = $(row).data('id'); var $new_row = $('', { id: _.uniqueId('oe-editable-row-'), From 632a4dee0c31754713bf5dd4cf8cde974542fc97 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Wed, 13 Jun 2012 11:40:37 +0200 Subject: [PATCH 03/23] [FIX] correctly forward arguments in _super() call of editable listview's form widgets override bzr revid: xmo@openerp.com-20120613094037-3iny9dsdhmp7qspi --- addons/web/static/src/js/view_list_editable.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/addons/web/static/src/js/view_list_editable.js b/addons/web/static/src/js/view_list_editable.js index 07feed91bbd..728652edc5f 100644 --- a/addons/web/static/src/js/view_list_editable.js +++ b/addons/web/static/src/js/view_list_editable.js @@ -402,12 +402,12 @@ openerp.web.list_editable = function (openerp) { if (this.modifiers.tree_invisible) { var old_invisible = this.invisible; this.invisible = true; - this._super(); + this._super.apply(this, arguments); this.invisible = old_invisible; } else if (this.invisible) { this.$element.children().css('visibility', 'hidden'); } else { - this._super(); + this._super.apply(this, arguments); } } }); From 5ec4ecf85d1b2645f43b7dbbb8d7df88a0ec042d Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Wed, 13 Jun 2012 12:08:19 +0200 Subject: [PATCH 04/23] [FIX] O2M validation: correctly proxy validation from/to edited row of the list view, if any bzr revid: xmo@openerp.com-20120613100819-sw93kst4dpwhnu1p --- addons/web/static/src/js/view_form.js | 57 ++++++++++++++++++++------- 1 file changed, 42 insertions(+), 15 deletions(-) diff --git a/addons/web/static/src/js/view_form.js b/addons/web/static/src/js/view_form.js index fe8448f4dcc..e82a0f1818a 100644 --- a/addons/web/static/src/js/view_form.js +++ b/addons/web/static/src/js/view_form.js @@ -2508,23 +2508,20 @@ openerp.web.form.FieldOne2Many = openerp.web.form.Field.extend({ }, this)); }, is_valid: function() { - this.validate(); - return this._super(); - }, - validate: function() { - this.invalid = false; if (!this.viewmanager.views[this.viewmanager.active_view]) - return; + return true; var view = this.viewmanager.views[this.viewmanager.active_view].controller; - if (this.viewmanager.active_view === "form") { - for (var f in view.fields) { - f = view.fields[f]; - if (!f.is_valid()) { - this.invalid = true; - return; - } - } + switch (this.viewmanager.active_view) { + case 'form': + return _(view.fields).chain() + .invoke('is_valid') + .all(_.identity) + .value(); + break; + case 'list': + return view.is_valid(); } + return true; }, is_dirty: function() { this.save_any_view(); @@ -2556,6 +2553,36 @@ openerp.web.form.One2ManyDataSet = openerp.web.BufferedDataSet.extend({ openerp.web.form.One2ManyListView = openerp.web.ListView.extend({ _template: 'One2Many.listview', + is_valid: function () { + var form; + // A list not being edited is always valid + if (!(form = this.first_edition_form())) { + return true; + } + + // Otherwise validate internal form + return _(form.fields).chain() + .invoke(function () { + this.validate(); + this.update_dom(true); + return this.is_valid(); + }) + .all(_.identity) + .value(); + }, + first_edition_form: function () { + var get_form = function (group_or_list) { + if (group_or_list.edition) { + return group_or_list.edition_form; + } + return _(group_or_list.children).chain() + .map(get_form) + .compact() + .first() + .value(); + }; + return get_form(this.groups); + }, do_add_record: function () { if (this.options.editable) { this._super.apply(this, arguments); @@ -2609,7 +2636,7 @@ openerp.web.form.One2ManyListView = openerp.web.ListView.extend({ var button_result = self.o2m.dataset.call_button.apply(self.o2m.dataset, arguments); self.o2m.reload_current_view(); return button_result; - } + }; pop.on_write.add(function(id, data) { self.o2m.dataset.write(id, data, {}, function(r) { self.o2m.reload_current_view(); From e475bc7a2aaf16e41e5dc0a148e3ddc5b389caf2 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Wed, 13 Jun 2012 17:34:12 +0200 Subject: [PATCH 05/23] [IMP] don;t needlessly wrap values into deferreds in pipes bzr revid: xmo@openerp.com-20120613153412-c2byqrc66jcn0e97 --- addons/web/static/src/js/view_form.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/addons/web/static/src/js/view_form.js b/addons/web/static/src/js/view_form.js index e82a0f1818a..8d0fd141cdb 100644 --- a/addons/web/static/src/js/view_form.js +++ b/addons/web/static/src/js/view_form.js @@ -549,7 +549,8 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView# return $.Deferred().reject(); } else { return $.when(this.reload()).pipe(function () { - return $.when(r).then(success); }, null); + return r; }) + .then(success); } }, /** @@ -583,8 +584,9 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView# this.sidebar.attachments.do_update(); } //openerp.log("The record has been created with id #" + this.datarecord.id); - this.reload(); - return $.when(_.extend(r, {created: true})).then(success); + return $.when(this.reload()).pipe(function () { + return _.extend(r, {created: true}); }) + .then(success); } }, on_action: function (action) { From 533f17231c1aee913b8806c1e39339851514d15b Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Wed, 13 Jun 2012 17:35:55 +0200 Subject: [PATCH 06/23] [FIX] if a form is not dirty, always consider the corresponding o2m[list] to be valid the user has not modified the editable line after opening it (whether to create a new record or to edit an existing one), so shouldn't be invalid bzr revid: xmo@openerp.com-20120613153555-fq0moy1q464l92yj --- addons/web/static/src/js/view_form.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/addons/web/static/src/js/view_form.js b/addons/web/static/src/js/view_form.js index 8d0fd141cdb..6257633b8d2 100644 --- a/addons/web/static/src/js/view_form.js +++ b/addons/web/static/src/js/view_form.js @@ -2561,6 +2561,12 @@ openerp.web.form.One2ManyListView = openerp.web.ListView.extend({ if (!(form = this.first_edition_form())) { return true; } + // If the form has not been modified, the view can only be valid + // NB: is_dirty will also be set on defaults/onchanges/whatever? + // oe_form_dirty seems to only be set on actual user actions + if (!form.$element.is('.oe_form_dirty')) { + return true; + } // Otherwise validate internal form return _(form.fields).chain() From 9f9bbcff33a5a3153a22ea6f736b8c668c4f5a81 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Wed, 13 Jun 2012 17:36:33 +0200 Subject: [PATCH 07/23] [FIX] in ensure_saved, discard non-dirty editable rows if the editable row's form isn't dirty, either nothing has been entered in a new row or an existing row (being edited) has not been altered, so can just discard the row (and reload it from cache if it's an edition). bzr revid: xmo@openerp.com-20120613153633-ms7i8t9lvdarxqi3 --- addons/web/static/src/js/view_list_editable.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/addons/web/static/src/js/view_list_editable.js b/addons/web/static/src/js/view_list_editable.js index 728652edc5f..7a0645138bf 100644 --- a/addons/web/static/src/js/view_list_editable.js +++ b/addons/web/static/src/js/view_list_editable.js @@ -332,7 +332,13 @@ openerp.web.list_editable = function (openerp) { */ ensure_saved: function () { if (this.edition) { - return this.save_row(); + // kinda-hack-ish: if the user has entered data in a field, + // oe_form_dirty will be set on the form so save, otherwise + // discard the current (entirely empty) line + if (this.edition_form.$element.is('.oe_form_dirty')) { + return this.save_row(); + } + return this.cancel_pending_edition(); } //noinspection JSPotentiallyInvalidConstructorUsage return $.when(); From 49f6ab98539640fa21b83711b71c71bcacec9459 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Wed, 13 Jun 2012 17:38:42 +0200 Subject: [PATCH 08/23] [FIX] do not break dataset index (at the wrong moment) when clicking on [Create] row dataset.index was previously set to ``null`` in handler of [Create] button (to fullfill contract with form view that dataset.index should be ``null`` to indicate the creation of a new record with no id). Issue: after setting the index to ``null``, the list view calls ``render_row_as_form`` which starts out trying to save a row being edited (case: clicking of the [Create] button after having selected a row for edition or after having written in a new record e.g. [Create] -> type -> [Create] type -> ...). This tentative to save the existing form would be performed in the context of a ``null`` dataset.index, which the form view doesn't (and shouldn't, index should be that of record *being edited*) expect. -> first save in whatever dataset state is the current one, and *right before* creating the new form (after having saved and/or discarded the previous one) we have the id of the new record to edit (or ``null``), find the index for *that* and set ``dataset.index`` to that (or ``null``) so the new form view can be created and opened in the right context. bzr revid: xmo@openerp.com-20120613153842-pd6xitjs8n003ogs --- addons/web/static/src/js/view_list_editable.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/addons/web/static/src/js/view_list_editable.js b/addons/web/static/src/js/view_list_editable.js index 7a0645138bf..aaa8adf5e08 100644 --- a/addons/web/static/src/js/view_list_editable.js +++ b/addons/web/static/src/js/view_list_editable.js @@ -139,6 +139,7 @@ openerp.web.list_editable = function (openerp) { self.edition_form.stop(); self.edition_form.$element.remove(); delete self.edition_form; + self.dataset.index = null; delete self.edition_id; delete self.edition; }); @@ -232,6 +233,10 @@ openerp.web.list_editable = function (openerp) { } self.edition = true; self.edition_id = record_id; + self.dataset.index = _(self.dataset.ids).indexOf(record_id) || null; + if (self.dataset.index === -1) { + self.dataset.index = null; + } self.edition_form = _.extend(new openerp.web.ListEditableFormView(self.view, self.dataset, false), { form_template: 'ListView.row.form', registry: openerp.web.list.form.widgets, @@ -360,7 +365,6 @@ openerp.web.list_editable = function (openerp) { [record_id, this.dataset]); }, new_record: function () { - this.dataset.index = null; this.render_row_as_form(); }, render_record: function (record) { From 29584df4208dfdc9f571800feb8ae4150d71cf86 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Thu, 14 Jun 2012 10:44:20 +0200 Subject: [PATCH 09/23] [IMP] hide [disk drive] button in o2m editable lists bzr revid: xmo@openerp.com-20120614084420-0ky3d41kk2r8zlp8 --- addons/web/static/src/css/base.css | 3 +++ 1 file changed, 3 insertions(+) diff --git a/addons/web/static/src/css/base.css b/addons/web/static/src/css/base.css index 99cb4a31dee..13ffefa9acd 100644 --- a/addons/web/static/src/css/base.css +++ b/addons/web/static/src/css/base.css @@ -1346,6 +1346,9 @@ label.error { display: inline; margin: 0 0.5em 0 0; } +.openerp .oe_form_field_one2many .oe-listview .oe-edit-row-save { + visibility: hidden; +} .openerp .oe_forms .oe-listview th.oe-sortable .ui-icon, .openerp .oe_forms .oe-listview th.oe-sortable .ui-icon { From 83eef22856f210b99e06260c863874050ad73fb2 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Thu, 14 Jun 2012 10:50:09 +0200 Subject: [PATCH 10/23] [FIX] don't break the handling of record index 0 in editable list views: index 0 means something different than index = null bzr revid: xmo@openerp.com-20120614085009-xuo1ifu2vnlcic72 --- addons/web/static/src/js/view_list_editable.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/web/static/src/js/view_list_editable.js b/addons/web/static/src/js/view_list_editable.js index aaa8adf5e08..c43ec9d10fc 100644 --- a/addons/web/static/src/js/view_list_editable.js +++ b/addons/web/static/src/js/view_list_editable.js @@ -233,7 +233,7 @@ openerp.web.list_editable = function (openerp) { } self.edition = true; self.edition_id = record_id; - self.dataset.index = _(self.dataset.ids).indexOf(record_id) || null; + self.dataset.index = _(self.dataset.ids).indexOf(record_id); if (self.dataset.index === -1) { self.dataset.index = null; } From 05238cd9482875c56a7249f6374d9b69fef9b0cd Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Thu, 14 Jun 2012 14:30:24 +0200 Subject: [PATCH 11/23] [FIX] don't require an extended class to have a method yet to allow it to be overloaded bzr revid: xmo@openerp.com-20120614123024-ngby1srdwojv5rmb --- addons/web/static/src/js/core.js | 1 - 1 file changed, 1 deletion(-) diff --git a/addons/web/static/src/js/core.js b/addons/web/static/src/js/core.js index 896ba7d3061..dacf734e24e 100644 --- a/addons/web/static/src/js/core.js +++ b/addons/web/static/src/js/core.js @@ -42,7 +42,6 @@ openerp.web.core = function(openerp) { for (var name in prop) { // Check if we're overwriting an existing function prototype[name] = typeof prop[name] == "function" && - typeof _super[name] == "function" && fnTest.test(prop[name]) ? (function(name, fn) { return function() { From 7516028068cbf9bae06077bbe216bc860b06631c Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Thu, 14 Jun 2012 14:30:56 +0200 Subject: [PATCH 12/23] [IMP] allow overloading a listview's List and Groups types bzr revid: xmo@openerp.com-20120614123056-cu3q34m4ysjxzqtt --- addons/web/static/src/js/view_list.js | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/addons/web/static/src/js/view_list.js b/addons/web/static/src/js/view_list.js index 6d613f3892d..36317d870a6 100644 --- a/addons/web/static/src/js/view_list.js +++ b/addons/web/static/src/js/view_list.js @@ -61,7 +61,7 @@ openerp.web.ListView = openerp.web.View.extend( /** @lends openerp.web.ListView# this.records = new Collection(); - this.set_groups(new openerp.web.ListView.Groups(this)); + this.set_groups(new (this.options.GroupsType)(this)); if (this.dataset instanceof openerp.web.DataSetStatic) { this.groups.datagroup = new openerp.web.StaticDataGroup(this.dataset); @@ -85,6 +85,14 @@ openerp.web.ListView = openerp.web.View.extend( /** @lends openerp.web.ListView# this.no_leaf = false; }, + set_default_options: function (options) { + this._super(options); + _.defaults(this.options, { + GroupsType: openerp.web.ListView.Groups, + ListType: openerp.web.ListView.List + }); + }, + /** * Retrieves the view's number of records per page (|| section) * @@ -1191,7 +1199,7 @@ openerp.web.ListView.Groups = openerp.web.Class.extend( /** @lends openerp.web.L self.records.proxy(group.value).reset(); delete self.children[group.value]; } - var child = self.children[group.value] = new openerp.web.ListView.Groups(self.view, { + var child = self.children[group.value] = new (this.view.options.GroupsType)(self.view, { records: self.records.proxy(group.value), options: self.options, columns: self.columns @@ -1297,7 +1305,7 @@ openerp.web.ListView.Groups = openerp.web.Class.extend( /** @lends openerp.web.L }, render_dataset: function (dataset) { var self = this, - list = new openerp.web.ListView.List(this, { + list = new (this.view.options.ListType)(this, { options: this.options, columns: this.columns, dataset: dataset, From acfff868cb7900210e9f602eaf98cd539fbfac76 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Thu, 14 Jun 2012 14:31:30 +0200 Subject: [PATCH 13/23] [IMP] return deferred from ListView#render_row_as_form indicating *when* the row has been rendered as a form bzr revid: xmo@openerp.com-20120614123130-1z1vvt62rnr21bpz --- addons/web/static/src/js/view_list_editable.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/addons/web/static/src/js/view_list_editable.js b/addons/web/static/src/js/view_list_editable.js index c43ec9d10fc..bf4bf205c02 100644 --- a/addons/web/static/src/js/view_list_editable.js +++ b/addons/web/static/src/js/view_list_editable.js @@ -195,7 +195,7 @@ openerp.web.list_editable = function (openerp) { }, render_row_as_form: function (row) { var self = this; - this.ensure_saved().then(function () { + return this.ensure_saved().pipe(function () { var record_id = $(row).data('id'); var $new_row = $('', { id: _.uniqueId('oe-editable-row-'), @@ -212,6 +212,7 @@ openerp.web.list_editable = function (openerp) { .keyup(function () { return self.on_row_keyup.apply(self, arguments); }) .keydown(function (e) { e.stopPropagation(); }); + if (row) { $new_row.replaceAll(row); } else if (self.options.editable) { @@ -245,7 +246,7 @@ openerp.web.list_editable = function (openerp) { // HA HA self.edition_form.appendTo(); // put in $.when just in case FormView.on_loaded becomes asynchronous - $.when(self.edition_form.on_loaded(self.get_form_fields_view())).then(function () { + return $.when(self.edition_form.on_loaded(self.get_form_fields_view())).then(function () { $new_row.find('> td') .addClass('oe-field-cell') .removeAttr('width') From 055edd779038e2bbbc71718c3cf33254eb58e293 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Thu, 14 Jun 2012 14:37:31 +0200 Subject: [PATCH 14/23] [ADD] automatic save of o2m rows being created/edited when clicking/focusing outside bzr revid: xmo@openerp.com-20120614123731-x2owyp4nc1yi1ocj --- addons/web/static/src/js/view_form.js | 38 +++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/addons/web/static/src/js/view_form.js b/addons/web/static/src/js/view_form.js index 6257633b8d2..d1dd352bae3 100644 --- a/addons/web/static/src/js/view_form.js +++ b/addons/web/static/src/js/view_form.js @@ -2555,6 +2555,11 @@ openerp.web.form.One2ManyDataSet = openerp.web.BufferedDataSet.extend({ openerp.web.form.One2ManyListView = openerp.web.ListView.extend({ _template: 'One2Many.listview', + init: function (parent, dataset, view_id, options) { + this._super(parent, dataset, view_id, _.extend(options || {}, { + ListType: openerp.web.form.One2ManyList + })); + }, is_valid: function () { var form; // A list not being edited is always valid @@ -2657,6 +2662,39 @@ openerp.web.form.One2ManyListView = openerp.web.ListView.extend({ return this._super(name, id, _.bind(def.resolve, def)); } }); +openerp.web.form.One2ManyList = openerp.web.ListView.List.extend({ + render_row_as_form: function () { + var self = this; + return this._super.apply(this, arguments).then(function () { + self.setup_save_on_row_blur(); + }); + }, + bind_blur_focus: function (new_row, onblur, onfocus) { + if (new_row.addEventListener) { + new_row.addEventListener('blur', onblur, true); + new_row.addEventListener('focus', onfocus, true); + } else { + new_row.onfocusout = onblur; + new_row.onfocusin = onfocus; + } + }, + setup_save_on_row_blur: function () { + var self = this; + var form = this.edition_form; + this.bind_blur_focus(this.edition_form.$element[0], function () { + self._save_row_timeout = setTimeout(function () { + if (form.widget_is_stopped) { + // Saved or cancelled already, maybe? + return; + } + self.view.ensure_saved(); + }, 0); + }, function () { + clearTimeout(self._save_row_timeout); + delete self._save_row_timeout; + }); + } +}); openerp.web.form.One2ManyFormView = openerp.web.FormView.extend({ form_template: 'One2Many.formview', From 3e2cab2aada3a99838736fb7e887c6d43b1719b3 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Fri, 15 Jun 2012 07:54:40 +0200 Subject: [PATCH 15/23] [FIX] handling of [Return] key to save records being edited in MSIE * DOM events sequence screwed up, requiring an explicit blur on the current target to try and ensure the corresponding form widget would register its own change event * Requirement to stop the default behavior of keypress and keydown on [Return] as they would somehow trigger the contextual menu of m2o fields on the row, at the wrong place, when in a char field (I don't even...) * Delaying of the actual saving of the form (via `setTimeout`) to try and ensure the blur/change event has had the time to propagate correctly (and be handled) before we actually save bzr revid: xmo@openerp.com-20120615055440-yn00uv4q8y29nboq --- .../web/static/src/js/view_list_editable.js | 46 +++++++++++-------- 1 file changed, 28 insertions(+), 18 deletions(-) diff --git a/addons/web/static/src/js/view_list_editable.js b/addons/web/static/src/js/view_list_editable.js index bf4bf205c02..c52bb08536e 100644 --- a/addons/web/static/src/js/view_list_editable.js +++ b/addons/web/static/src/js/view_list_editable.js @@ -168,24 +168,29 @@ openerp.web.list_editable = function (openerp) { var self = this; switch (e.which) { case KEY_RETURN: - this.save_row().then(function (result) { - if (result.created) { - self.new_record(); - return; - } + $(e.target).blur(); + e.preventDefault(); + //e.stopImmediatePropagation(); + setTimeout(function () { + self.save_row().then(function (result) { + if (result.created) { + self.new_record(); + return; + } - var next_record_id, - next_record = self.records.at( - self.records.indexOf(result.edited_record) + 1); - if (next_record) { - next_record_id = next_record.get('id'); - self.dataset.index = _(self.dataset.ids) - .indexOf(next_record_id); - } else { - self.dataset.index = 0; - next_record_id = self.records.at(0).get('id'); - } - self.edit_record(next_record_id); + var next_record_id, + next_record = self.records.at( + self.records.indexOf(result.edited_record) + 1); + if (next_record) { + next_record_id = next_record.get('id'); + self.dataset.index = _(self.dataset.ids) + .indexOf(next_record_id); + } else { + self.dataset.index = 0; + next_record_id = self.records.at(0).get('id'); + } + self.edit_record(next_record_id); + }, 0); }); break; case KEY_ESCAPE: @@ -211,7 +216,12 @@ openerp.web.list_editable = function (openerp) { }) .keyup(function () { return self.on_row_keyup.apply(self, arguments); }) - .keydown(function (e) { e.stopPropagation(); }); + .keydown(function (e) { e.stopPropagation(); }) + .keypress(function (e) { + if (e.which === KEY_RETURN) { + return false; + } + }); if (row) { $new_row.replaceAll(row); From 70b0af1375d137f06bc4d9d47ab8dbcaf24180fa Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Mon, 18 Jun 2012 09:34:31 +0200 Subject: [PATCH 16/23] [IMP] jQuery exposes focusin and focusout, use that instead of its manual implementation bzr revid: xmo@openerp.com-20120618073431-gmeo349oskbld53t --- addons/web/static/src/js/view_form.js | 34 +++++++++++---------------- 1 file changed, 14 insertions(+), 20 deletions(-) diff --git a/addons/web/static/src/js/view_form.js b/addons/web/static/src/js/view_form.js index d1dd352bae3..b9aa00e4825 100644 --- a/addons/web/static/src/js/view_form.js +++ b/addons/web/static/src/js/view_form.js @@ -2669,29 +2669,23 @@ openerp.web.form.One2ManyList = openerp.web.ListView.List.extend({ self.setup_save_on_row_blur(); }); }, - bind_blur_focus: function (new_row, onblur, onfocus) { - if (new_row.addEventListener) { - new_row.addEventListener('blur', onblur, true); - new_row.addEventListener('focus', onfocus, true); - } else { - new_row.onfocusout = onblur; - new_row.onfocusin = onfocus; - } - }, setup_save_on_row_blur: function () { var self = this; var form = this.edition_form; - this.bind_blur_focus(this.edition_form.$element[0], function () { - self._save_row_timeout = setTimeout(function () { - if (form.widget_is_stopped) { - // Saved or cancelled already, maybe? - return; - } - self.view.ensure_saved(); - }, 0); - }, function () { - clearTimeout(self._save_row_timeout); - delete self._save_row_timeout; + 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; + } }); } }); From 0516533c16376bfd849dba6b5e08df27b4db472b Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Tue, 19 Jun 2012 09:25:18 +0200 Subject: [PATCH 17/23] [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 --- addons/web/static/src/js/view_form.js | 108 +++++++++++++++++++------- 1 file changed, 79 insertions(+), 29 deletions(-) diff --git a/addons/web/static/src/js/view_form.js b/addons/web/static/src/js/view_form.js index b9aa00e4825..7bf1f4a1c32 100644 --- a/addons/web/static/src/js/view_form.js +++ b/addons/web/static/src/js/view_form.js @@ -80,6 +80,7 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView# this.sidebar.stop(); } _.each(this.widgets, function(w) { + $(w).unbind('.formBlur'); w.stop(); }); this._super(); @@ -100,6 +101,8 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView# this.$element.html(this.rendered); _.each(this.widgets, function(w) { 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.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(); }, + 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) { if (state.id && this.datarecord.id != 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; }, + setupFocus: function ($e) { + var self = this; + $e.bind({ + focus: function () { $(self).trigger('widget-focus'); }, + blur: function () { $(self).trigger('widget-blur'); } + }); + }, start: function() { this.$element = this.view.$element.find( '.' + 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() { 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) { this.do_attach_tooltip(); } + this.setupFocus($button); }, on_click: function() { 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)) { 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; openerp.log(widget.element_class , widget); window.w = widget; }); + this.setupFocus($label); } }); @@ -1460,7 +1489,9 @@ openerp.web.form.FieldChar = openerp.web.form.Field.extend({ }, start: function() { 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) { this._super.apply(this, arguments); @@ -1501,7 +1532,9 @@ openerp.web.form.FieldEmail = openerp.web.form.FieldChar.extend({ template: 'FieldEmail', start: function() { 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() { if (!this.value || !this.is_valid()) { @@ -1516,7 +1549,9 @@ openerp.web.form.FieldUrl = openerp.web.form.FieldChar.extend({ template: 'FieldUrl', start: function() { 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() { if (!this.value) { @@ -1641,6 +1676,7 @@ openerp.web.form.FieldDatetime = openerp.web.form.Field.extend({ this.datewidget = this.build_widget(); this.datewidget.on_change.add_last(this.on_ui_change); this.datewidget.appendTo(this.$element); + // FIXME: handle focus on datetime field }, set_value: function(value) { this._super(value); @@ -1671,8 +1707,10 @@ openerp.web.form.FieldText = openerp.web.form.Field.extend({ template: 'FieldText', start: function() { 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.setupFocus($textarea); }, set_value: function(value) { this._super.apply(this, arguments); @@ -1733,7 +1771,9 @@ openerp.web.form.FieldBoolean = openerp.web.form.Field.extend({ start: function() { var self = this; 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) { this._super.apply(this, arguments); @@ -1803,7 +1843,8 @@ openerp.web.form.FieldSelection = openerp.web.form.Field.extend({ // row var ischanging = false; this._super.apply(this, arguments); - this.$element.find('select') + var $select = this.$element.find('select'); + $select .change(this.on_ui_change) .change(function () { ischanging = true; }) .click(function () { ischanging = false; }) @@ -1812,6 +1853,7 @@ openerp.web.form.FieldSelection = openerp.web.form.Field.extend({ e.stopPropagation(); ischanging = false; }); + this.setupFocus($select); }, set_value: function(value) { value = value === null ? false : value; @@ -1983,8 +2025,8 @@ openerp.web.form.FieldMany2One = openerp.web.form.Field.extend({ } else { self.$input.autocomplete("search"); } - self.$input.focus(); } + self.$input.focus(); }); var anyoneLoosesFocus = function() { if (!self.$input.is(":focus") && @@ -2022,6 +2064,7 @@ openerp.web.form.FieldMany2One = openerp.web.form.Field.extend({ minLength: 0, delay: 0 }); + // used to correct a bug when selecting an element by pushing 'enter' in an editable list this.$input.keyup(function(e) { if (e.which === 13) { @@ -2030,6 +2073,23 @@ openerp.web.form.FieldMany2One = openerp.web.form.Field.extend({ } 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 get_search_result: function(request, response) { @@ -2666,28 +2726,13 @@ openerp.web.form.One2ManyList = openerp.web.ListView.List.extend({ render_row_as_form: function () { var self = this; 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({ @@ -3207,9 +3252,14 @@ openerp.web.form.FieldReference = openerp.web.form.Field.extend({ } }, start: function() { + var self = this; this._super(); this.selection.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() { return this.required === false || typeof(this.get_value()) === 'string'; From 125eead80acd077c90cd702a417fca6c41ff43f6 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Tue, 19 Jun 2012 11:36:51 +0200 Subject: [PATCH 18/23] [FIX] focus issues with m2o contextual menu (and popup operations) bzr revid: xmo@openerp.com-20120619093651-7rfe1pp7ucuj0ajh --- addons/web/static/src/js/view_form.js | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/addons/web/static/src/js/view_form.js b/addons/web/static/src/js/view_form.js index 7bf1f4a1c32..fde415347f0 100644 --- a/addons/web/static/src/js/view_form.js +++ b/addons/web/static/src/js/view_form.js @@ -1964,6 +1964,7 @@ openerp.web.form.FieldMany2One = openerp.web.form.Field.extend({ }; bindings[self.cm_id + "_open"] = function() { if (!self.value) { + self.focus(); return; } var pop = new openerp.web.form.FormOpenPopup(self.view); @@ -1977,6 +1978,7 @@ openerp.web.form.FieldMany2One = openerp.web.form.Field.extend({ ); pop.on_write_completed.add_last(function() { self.set_value(self.value[0]); + self.focus(); }); }; _.each(_.range(self.related_entries.length), function(i) { @@ -2074,8 +2076,12 @@ openerp.web.form.FieldMany2One = openerp.web.form.Field.extend({ isSelecting = false; }); + // avoid triggering blur on the widget when it comes from clicking on + // a completion of the list, on the dropdown or on the m2o menu thing var picking_completion = false; - this.$input.autocomplete('widget').add(this.$drop_down) + this.$input.autocomplete('widget') + .add(this.$drop_down) + .add(this.$menu_btn) .mousedown(function () { picking_completion = true; }); @@ -2191,6 +2197,7 @@ openerp.web.form.FieldMany2One = openerp.web.form.Field.extend({ var dataset = new openerp.web.DataSetStatic(self, self.field.relation, self.build_context()); dataset.name_get([element_ids[0]], function(data) { self._change_int_ext_value(data[0]); + self.focus(); }); }); }, From 19d5cb01c7664c6e642cdbef94f1095a8fa92167 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Tue, 19 Jun 2012 13:03:50 +0200 Subject: [PATCH 19/23] [FIX] error caused by autosave-on-blur when vaidating a row with [Return] bzr revid: xmo@openerp.com-20120619110350-64sb4uy2t1eydiou --- addons/web/static/src/js/view_form.js | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/addons/web/static/src/js/view_form.js b/addons/web/static/src/js/view_form.js index fde415347f0..920316c6f35 100644 --- a/addons/web/static/src/js/view_form.js +++ b/addons/web/static/src/js/view_form.js @@ -2730,16 +2730,30 @@ openerp.web.form.One2ManyListView = openerp.web.ListView.extend({ } }); openerp.web.form.One2ManyList = openerp.web.ListView.List.extend({ + KEY_RETURN: 13, + // blurring caused by hitting the [Return] key, should skip the + // autosave-on-blur and let the handler for [Return] do its thing + __return_blur: false, render_row_as_form: function () { var self = this; return this._super.apply(this, arguments).then(function () { $(self.edition_form).bind('form-blur', function () { + if (self.__return_blur) { + delete self.__return_blur; + return; + } if (!self.edition_form.widget_is_stopped) { self.view.ensure_saved(); } }); }); }, + on_row_keyup: function (e) { + if (e.which === this.KEY_RETURN) { + this.__return_blur = true; + } + this._super(e); + } }); openerp.web.form.One2ManyFormView = openerp.web.FormView.extend({ From f5e53a1e09577becdad9824e8f6e525b426696af Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Tue, 19 Jun 2012 15:36:18 +0200 Subject: [PATCH 20/23] [FIX] stupid groupby bug bzr revid: xmo@openerp.com-20120619133618-sxgs18sn48x902hv --- addons/web/static/src/js/view_list.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/web/static/src/js/view_list.js b/addons/web/static/src/js/view_list.js index 36317d870a6..cbd3854e601 100644 --- a/addons/web/static/src/js/view_list.js +++ b/addons/web/static/src/js/view_list.js @@ -1199,7 +1199,7 @@ openerp.web.ListView.Groups = openerp.web.Class.extend( /** @lends openerp.web.L self.records.proxy(group.value).reset(); delete self.children[group.value]; } - var child = self.children[group.value] = new (this.view.options.GroupsType)(self.view, { + var child = self.children[group.value] = new (self.view.options.GroupsType)(self.view, { records: self.records.proxy(group.value), options: self.options, columns: self.columns From 6a4adf2a3bc1f1e1cf3a05051e01b772fadbfbc5 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Wed, 20 Jun 2012 13:15:27 +0200 Subject: [PATCH 21/23] [FIX] blur/focus handling in most (if not all) form widgets bzr revid: xmo@openerp.com-20120620111527-2j23k13zc6td2deo --- addons/web/static/src/css/base.css | 3 +- addons/web/static/src/js/view_form.js | 47 ++++++++++++++------------- 2 files changed, 26 insertions(+), 24 deletions(-) diff --git a/addons/web/static/src/css/base.css b/addons/web/static/src/css/base.css index 13ffefa9acd..220a7d9e4c6 100644 --- a/addons/web/static/src/css/base.css +++ b/addons/web/static/src/css/base.css @@ -1346,7 +1346,8 @@ label.error { display: inline; margin: 0 0.5em 0 0; } -.openerp .oe_form_field_one2many .oe-listview .oe-edit-row-save { +.openerp .oe_form_field_one2many .oe-listview .oe-edit-row-save, +.openerp .oe_form_field_one2many_list .oe-listview .oe-edit-row-save { visibility: hidden; } diff --git a/addons/web/static/src/js/view_form.js b/addons/web/static/src/js/view_form.js index 920316c6f35..26f95233bce 100644 --- a/addons/web/static/src/js/view_form.js +++ b/addons/web/static/src/js/view_form.js @@ -52,6 +52,9 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView# this.mutating_mutex = new $.Mutex(); this.on_change_mutex = new $.Mutex(); this.reload_mutex = new $.Mutex(); + + this.__clicked_inside = false; + this.__blur_timeout = null; }, start: function() { this._super(); @@ -83,6 +86,7 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView# $(w).unbind('.formBlur'); w.stop(); }); + this.$element.unbind('.formBlur'); this._super(); }, reposition: function ($e) { @@ -99,6 +103,9 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView# this.rendered = QWeb.render(this.form_template, { 'frame': frame, 'widget': this }); } this.$element.html(this.rendered); + this.$element.bind('mousedown.formBlur', function () { + self.__clicked_inside = true; + }); _.each(this.widgets, function(w) { w.start(); $(w).bind('widget-focus.formBlur', self.proxy('widgetFocused')) @@ -134,12 +141,20 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView# }, widgetFocused: function() { + // Clear click flag if used to focus a widget + this.__clicked_inside = false; if (this.__blur_timeout) { clearTimeout(this.__blur_timeout); - delete this.__blur_timeout; + this.__blur_timeout = null; } }, widgetBlurred: function() { + if (this.__clicked_inside) { + // clicked in an other section of the form (than the currently + // focused widget) => just ignore the blurring entirely? + this.__clicked_inside = false; + return; + } var self = this; // clear timeout, if any this.widgetFocused(); @@ -944,6 +959,11 @@ openerp.web.form.Widget = openerp.web.OldWidget.extend(/** @lends openerp.web.fo this.width = this.node.attrs.width; }, + /** + * Sets up blur/focus forwarding from DOM elements to a widget (`this`) + * + * @param {jQuery} $e jQuery object of elements to bind focus/blur on + */ setupFocus: function ($e) { var self = this; $e.bind({ @@ -1676,7 +1696,7 @@ openerp.web.form.FieldDatetime = openerp.web.form.Field.extend({ this.datewidget = this.build_widget(); this.datewidget.on_change.add_last(this.on_ui_change); this.datewidget.appendTo(this.$element); - // FIXME: handle focus on datetime field + this.setupFocus(this.datewidget.$input); }, set_value: function(value) { this._super(value); @@ -2021,6 +2041,7 @@ openerp.web.form.FieldMany2One = openerp.web.form.Field.extend({ return; if (self.$input.autocomplete("widget").is(":visible")) { self.$input.autocomplete("close"); + self.$input.focus(); } else { if (self.value) { self.$input.autocomplete("search", ""); @@ -2028,7 +2049,6 @@ openerp.web.form.FieldMany2One = openerp.web.form.Field.extend({ self.$input.autocomplete("search"); } } - self.$input.focus(); }); var anyoneLoosesFocus = function() { if (!self.$input.is(":focus") && @@ -2076,26 +2096,7 @@ openerp.web.form.FieldMany2One = openerp.web.form.Field.extend({ isSelecting = false; }); - // avoid triggering blur on the widget when it comes from clicking on - // a completion of the list, on the dropdown or on the m2o menu thing - var picking_completion = false; - this.$input.autocomplete('widget') - .add(this.$drop_down) - .add(this.$menu_btn) - .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'); - } - }) + this.setupFocus(this.$input.add(this.$menu_btn)); }, // autocomplete component content handling get_search_result: function(request, response) { From ed0a94f0d7ca73da2892c84a72a481e913642ca1 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Wed, 20 Jun 2012 13:34:02 +0200 Subject: [PATCH 22/23] [IMP] focus management in date field bzr revid: xmo@openerp.com-20120620113402-lacdu8fvq9bwrcl6 --- addons/web/static/src/js/view_form.js | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/addons/web/static/src/js/view_form.js b/addons/web/static/src/js/view_form.js index 26f95233bce..f83ffa94072 100644 --- a/addons/web/static/src/js/view_form.js +++ b/addons/web/static/src/js/view_form.js @@ -1621,12 +1621,14 @@ openerp.web.DateTimeWidget = openerp.web.OldWidget.extend({ showButtonPanel: true }); this.$element.find('img.oe_datepicker_trigger').click(function() { - if (!self.readonly && !self.picker('widget').is(':visible')) { - self.picker('setDate', self.value ? openerp.web.auto_str_to_date(self.value) : new Date()); - self.$input_picker.show(); - self.picker('show'); - self.$input_picker.hide(); + if (self.readonly || self.picker('widget').is(':visible')) { + self.$input.focus(); + return; } + self.picker('setDate', self.value ? openerp.web.auto_str_to_date(self.value) : new Date()); + self.$input_picker.show(); + self.picker('show'); + self.$input_picker.hide(); }); this.set_readonly(false); this.value = false; @@ -1636,7 +1638,10 @@ openerp.web.DateTimeWidget = openerp.web.OldWidget.extend({ }, on_picker_select: function(text, instance) { var date = this.picker('getDate'); - this.$input.val(date ? this.format_client(date) : '').change(); + this.$input + .val(date ? this.format_client(date) : '') + .change() + .focus(); }, set_value: function(value) { this.value = value; From a227e1e43cb04ed4b9a1430810eab0f38e64695a Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Mon, 25 Jun 2012 08:58:12 +0200 Subject: [PATCH 23/23] [CHG] reintroduce a button at the end of the line, but use the cross as a cancel bzr revid: xmo@openerp.com-20120625065812-6n8lxb0zimzvq1ee --- addons/web/static/src/css/base.css | 2 +- addons/web/static/src/js/view_form.js | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/addons/web/static/src/css/base.css b/addons/web/static/src/css/base.css index 220a7d9e4c6..49752f66c40 100644 --- a/addons/web/static/src/css/base.css +++ b/addons/web/static/src/css/base.css @@ -1348,7 +1348,7 @@ label.error { } .openerp .oe_form_field_one2many .oe-listview .oe-edit-row-save, .openerp .oe_form_field_one2many_list .oe-listview .oe-edit-row-save { - visibility: hidden; + background-image: url("/web/static/src/img/iconset-b-remove.png"); } .openerp .oe_forms .oe-listview th.oe-sortable .ui-icon, diff --git a/addons/web/static/src/js/view_form.js b/addons/web/static/src/js/view_form.js index f83ffa94072..2f30e83b0fa 100644 --- a/addons/web/static/src/js/view_form.js +++ b/addons/web/static/src/js/view_form.js @@ -2743,6 +2743,11 @@ openerp.web.form.One2ManyList = openerp.web.ListView.List.extend({ render_row_as_form: function () { var self = this; return this._super.apply(this, arguments).then(function () { + self.edition_form.$element + .undelegate('button.oe-edit-row-save', 'click') + .delegate('button.oe-edit-row-save', 'click', function () { + self.cancel_pending_edition(); + }); $(self.edition_form).bind('form-blur', function () { if (self.__return_blur) { delete self.__return_blur;