diff --git a/sao/src/screen.js b/sao/src/screen.js index aa0d0456615..1ef77274f2b 100644 --- a/sao/src/screen.js +++ b/sao/src/screen.js @@ -17,6 +17,7 @@ this.el = jQuery('
', { 'class': 'screen-container' }); + this.context_container = null; this.filter_box = jQuery('
', { 'class': 'filter-box hidden-xs' }).submit(e => { @@ -400,6 +401,19 @@ jQuery.isEmptyObject(this.bookmarks())); this.bookmark_match(); }, + set_context_screen: function(screen) { + this.context_container = jQuery('
'); + this.context_container.append(screen.screen_container.el); + jQuery('
', { + 'class': 'row', + }).append(jQuery('
', { + 'class': 'col-md-12', + }).append(this.context_container)) + .prependTo(this.filter_box); + }, + set_context_active: function(active) { + this.context_container.prop('disabled', !active); + }, show_filter: function() { this.filter_box.show(); if (this.tab) { @@ -792,6 +806,7 @@ init: function(model_name, attributes) { this.model_name = model_name; this.windows = []; + this.context_screen = null; this.model = new Sao.Model(model_name, attributes); this.attributes = jQuery.extend({}, attributes); this.view_ids = jQuery.extend([], attributes.view_ids); @@ -825,7 +840,6 @@ attributes.tab_domain, attributes.show_filter); this.breadcrumb = attributes.breadcrumb || []; - this.context_screen = null; if (attributes.context_model) { this.context_screen = new Sao.Screen( attributes.context_model, { @@ -835,12 +849,14 @@ this.context_screen_prm = this.context_screen.switch_view() .then(() => { - jQuery('
', { - 'class': 'row' - }).append(jQuery('
', { - 'class': 'col-md-12' - }).append(this.context_screen.screen_container.el)) - .prependTo(this.screen_container.filter_box); + this.screen_container.set_context_screen( + this.context_screen); + for (let field_widgets of + Object.values(this.context_screen.current_view.widgets)) { + for (let widget of field_widgets) { + widget.connect(() => this.screen_container.do_search()); + } + } return this.context_screen.new_(false).then( // Set manually default to get context_screen_prm // resolved when default is set. @@ -1267,6 +1283,10 @@ window_.record_modified(); } } + if (this.context_screen) { + this.screen_container.set_context_active( + !this.current_record || !this.modified()); + } if (display) { return this.display(); } @@ -1296,6 +1316,10 @@ window_.record_saved(); } } + if (this.context_screen) { + this.screen_container.set_context_active( + !this.current_record || !this.modified()); + } }, update_resources: function(resources) { for (const window_ of this.windows) { diff --git a/sao/src/view/form.js b/sao/src/view/form.js index af1be3634ce..c2f786fc819 100644 --- a/sao/src/view/form.js +++ b/sao/src/view/form.js @@ -1453,6 +1453,8 @@ function hide_x2m_body(widget) { focus: function() { this.el.focus(); }, + connect: function(callback) { + } }); // [Coog] widget Source (engine) @@ -2175,6 +2177,9 @@ function hide_x2m_body(widget) { 'readonly': 'readonly', 'name': this.attributes.name, }); + }, + connect: function(callback) { + this.el.change(callback); } }); @@ -2379,6 +2384,9 @@ function hide_x2m_body(widget) { } return value; }, + connect: function(callback) { + this.input.change(callback); + } }); Sao.View.Form.DateTime = Sao.class_(Sao.View.Form.Date, { @@ -2471,6 +2479,9 @@ function hide_x2m_body(widget) { set_readonly: function(readonly) { Sao.View.Form.TimeDelta._super.set_readonly.call(this, readonly); this.input.prop('readonly', readonly); + }, + connect: function(callback) { + this.el.change(callback); } }); @@ -2757,7 +2768,10 @@ function hide_x2m_body(widget) { set_readonly: function(readonly) { Sao.View.Form.Selection._super.set_readonly.call(this, readonly); this.select.prop('disabled', readonly); - } + }, + connect: function(callback) { + this.select.change(callback); + }, }); Sao.View.Form.Boolean = Sao.class_(Sao.View.Form.Widget, { @@ -2799,6 +2813,9 @@ function hide_x2m_body(widget) { set_readonly: function(readonly) { Sao.View.Form.Boolean._super.set_readonly.call(this, readonly); this.input.prop('readonly', readonly); + }, + connect: function(callback) { + this.input.change(callback); } }); @@ -2880,6 +2897,9 @@ function hide_x2m_body(widget) { widget.css('min-height', this.el.height()); widget.css('max-height', this.el.height()); return widget; + }, + connect: function(callback) { + this.input.change(callback); } }); @@ -3012,6 +3032,9 @@ function hide_x2m_body(widget) { translate_widget_get: function(el) { return this._normalize_markup( el.find('div[contenteditable]').html()); + }, + connect: function(callback) { + this.group.focusout(callback); } }); @@ -3485,7 +3508,10 @@ function hide_x2m_body(widget) { } else if (action == 'create') { this.new_(); } - } + }, + connect: function(callback) { + this.el.change(callback); + }, }); Sao.View.Form.One2One = Sao.class_(Sao.View.Form.Many2One, { diff --git a/tryton/tryton/gui/window/view_form/screen/screen.py b/tryton/tryton/gui/window/view_form/screen/screen.py index c7931a8cbb1..368d05de0b2 100644 --- a/tryton/tryton/gui/window/view_form/screen/screen.py +++ b/tryton/tryton/gui/window/view_form/screen/screen.py @@ -10,6 +10,7 @@ import logging import urllib.parse import xml.dom.minidom +from itertools import chain from operator import itemgetter from gi.repository import GLib, Gtk @@ -48,6 +49,7 @@ def __init__(self, model_name, **attributes): self.position = 0 self.offset = 0 self.windows = [] + self.context_screen = None self.readonly = attributes.get('readonly', False) if not (MODELACCESS[model_name]['write'] @@ -85,29 +87,14 @@ def __init__(self, model_name, **attributes): self.widget = self.screen_container.widget_get() self.breadcrumb = attributes.get('breadcrumb') or [] - self.context_screen = None if attributes.get('context_model'): self.context_screen = Screen( attributes['context_model'], mode=['form'], context=context) self.context_screen.parent_screen = self self.context_screen.new() - context_widget = self.context_screen.widget - - def walk_descendants(widget): - yield widget - if not hasattr(widget, 'get_children'): - return - for child in widget.get_children(): - for widget in walk_descendants(child): - yield widget - - for widget in reversed(list(walk_descendants(context_widget))): - if isinstance(widget, Gtk.Entry): - widget.connect_after( - 'activate', self.screen_container.activate) - elif isinstance(widget, Gtk.CheckButton): - widget.connect_after( - 'toggled', self.screen_container.activate) + ctx_widgets = self.context_screen.current_view.widgets.values() + for ctx_widget in chain.from_iterable(ctx_widgets): + ctx_widget.connect(self.screen_container.activate) def remove_bin(widget): assert isinstance(widget, (Gtk.ScrolledWindow, Gtk.Viewport)) @@ -126,10 +113,7 @@ def remove_bin(widget): remove_bin( self.context_screen.current_view.widget.get_children()[0]) - self.screen_container.filter_vbox.pack_start( - context_widget, expand=False, fill=True, padding=0) - self.screen_container.filter_vbox.reorder_child( - context_widget, 0) + self.screen_container.set_context_screen(self.context_screen) self.context_screen.widget.show() self.__current_view = 0 @@ -454,6 +438,9 @@ def record_modified(self, display=True): for window in self.windows: if hasattr(window, 'record_modified'): window.record_modified() + if self.context_screen: + self.screen_container.set_context_active( + not self.current_record or not self.modified()) if display: self.display() @@ -478,6 +465,9 @@ def record_saved(self): for window in self.windows: if hasattr(window, 'record_saved'): window.record_saved() + if self.context_screen: + self.screen_container.set_context_active( + not self.current_record or not self.modified()) def update_resources(self, resources): for window in self.windows: diff --git a/tryton/tryton/gui/window/view_form/view/form_gtk/binary.py b/tryton/tryton/gui/window/view_form/view/form_gtk/binary.py index 22b575881c8..13158fad1e0 100644 --- a/tryton/tryton/gui/window/view_form/view/form_gtk/binary.py +++ b/tryton/tryton/gui/window/view_form/view/form_gtk/binary.py @@ -197,6 +197,10 @@ def sig_icon_press(self, widget, icon_pos, event): if icon_pos == Gtk.EntryIconPosition.PRIMARY: self.open_() + def callback(self, callback): + if self.mnemonic_widget: + self.mnemonic_widget.connect('focus-out-event', callback) + def display(self): super(Binary, self).display() if not self.field: diff --git a/tryton/tryton/gui/window/view_form/view/form_gtk/calendar_.py b/tryton/tryton/gui/window/view_form/view/form_gtk/calendar_.py index 3558c724dce..5aacba005b3 100644 --- a/tryton/tryton/gui/window/view_form/view/form_gtk/calendar_.py +++ b/tryton/tryton/gui/window/view_form/view/form_gtk/calendar_.py @@ -85,6 +85,9 @@ def set_format(self): self.view.screen.context.get('date_format')) self.entry.props.format = format_ + def connect(self, callback): + self.entry.connect(self._changed_signal, callback) + def display(self): super(Date, self).display() if self.field and self.record: diff --git a/tryton/tryton/gui/window/view_form/view/form_gtk/char.py b/tryton/tryton/gui/window/view_form/view/form_gtk/char.py index f86fd4e59f1..9d312f6092f 100644 --- a/tryton/tryton/gui/window/view_form/view/form_gtk/char.py +++ b/tryton/tryton/gui/window/view_form/view/form_gtk/char.py @@ -109,6 +109,13 @@ def get_client_value(self): value = self.field.get_client(self.record) return value + def connect(self, callback): + if self.autocomplete: + focus_entry = self.entry.get_child() + else: + focus_entry = self.entry + focus_entry.connect('focus-out-event', callback) + def display(self): super(Char, self).display() if self.autocomplete: diff --git a/tryton/tryton/gui/window/view_form/view/form_gtk/checkbox.py b/tryton/tryton/gui/window/view_form/view/form_gtk/checkbox.py index 3498c6ee8ea..65d4d8dfda8 100644 --- a/tryton/tryton/gui/window/view_form/view/form_gtk/checkbox.py +++ b/tryton/tryton/gui/window/view_form/view/form_gtk/checkbox.py @@ -25,6 +25,9 @@ def _readonly_set(self, value): def set_value(self): self.field.set_client(self.record, self.widget.get_active()) + def connect(self, callback): + self.widget.connect('toggled', callback) + def display(self): super(CheckBox, self).display() if not self.field: diff --git a/tryton/tryton/gui/window/view_form/view/form_gtk/image.py b/tryton/tryton/gui/window/view_form/view/form_gtk/image.py index 1cf1b92b523..8cfa84fa820 100644 --- a/tryton/tryton/gui/window/view_form/view/form_gtk/image.py +++ b/tryton/tryton/gui/window/view_form/view/form_gtk/image.py @@ -109,6 +109,9 @@ def update_img(self): self.image.set_from_pixbuf(pixbuf) return bool(value) + def connect(self, callback): + self.event.connect('drag_data_received', callback) + def display(self): super(Image, self).display() value = self.update_img() diff --git a/tryton/tryton/gui/window/view_form/view/form_gtk/integer.py b/tryton/tryton/gui/window/view_form/view/form_gtk/integer.py index 945b424766f..7810520bd1e 100644 --- a/tryton/tryton/gui/window/view_form/view/form_gtk/integer.py +++ b/tryton/tryton/gui/window/view_form/view/form_gtk/integer.py @@ -59,6 +59,9 @@ def get_client_value(self): def width(self): return self.attrs.get('width', 8) + def connect(self, callback): + self.entry.connect('focus-out-event', callback) + def display(self): def set_symbol(entry, text): if text: diff --git a/tryton/tryton/gui/window/view_form/view/form_gtk/many2one.py b/tryton/tryton/gui/window/view_form/view/form_gtk/many2one.py index ceadf51f441..8354b94d833 100644 --- a/tryton/tryton/gui/window/view_form/view/form_gtk/many2one.py +++ b/tryton/tryton/gui/window/view_form/view/form_gtk/many2one.py @@ -321,6 +321,9 @@ def set_text(self, value): self.wid_text.set_text(value) reset_position(self.wid_text) + def connect(self, callback): + self.wid_text.connect('focus-out-event', callback) + def display(self): self.changed = False super(Many2One, self).display() diff --git a/tryton/tryton/gui/window/view_form/view/form_gtk/multiselection.py b/tryton/tryton/gui/window/view_form/view/form_gtk/multiselection.py index 648698c066b..4378c9c830a 100644 --- a/tryton/tryton/gui/window/view_form/view/form_gtk/multiselection.py +++ b/tryton/tryton/gui/window/view_form/view/form_gtk/multiselection.py @@ -73,6 +73,10 @@ def get_value(self): def set_value(self): self.field.set_client(self.record, self.get_value()) + def connect(self, callback): + selection = self.tree.get_selection() + selection.connect('changed', callback) + def display(self): selection = self.tree.get_selection() selection.handler_block_by_func(self.changed) diff --git a/tryton/tryton/gui/window/view_form/view/form_gtk/reference.py b/tryton/tryton/gui/window/view_form/view/form_gtk/reference.py index 885a6c81d99..2e91d279aac 100644 --- a/tryton/tryton/gui/window/view_form/view/form_gtk/reference.py +++ b/tryton/tryton/gui/window/view_form/view/form_gtk/reference.py @@ -138,6 +138,11 @@ def set_text(self, value): self.set_popdown_value(self.widget_combo, value) self.widget_combo.handler_unblock_by_func(self.sig_changed_combo) + def connect(self, callback): + super().connect(callback) + child = self.widget_combo.get_child() + child.connect('focus-out-event', callback) + def display(self): self.update_selection(self.record, self.field) self.set_popdown(self.selection, self.widget_combo) diff --git a/tryton/tryton/gui/window/view_form/view/form_gtk/selection.py b/tryton/tryton/gui/window/view_form/view/form_gtk/selection.py index 117578dc54c..485dc83ac63 100644 --- a/tryton/tryton/gui/window/view_form/view/form_gtk/selection.py +++ b/tryton/tryton/gui/window/view_form/view/form_gtk/selection.py @@ -72,6 +72,9 @@ def set_value(self): value = (value, self.get_popdown_text(self.entry)) self.field.set_client(self.record, value) + def connect(self, callback): + self.entry.connect('changed', callback) + def display(self): self.update_selection(self.record, self.field) self.set_popdown(self.selection, self.entry) diff --git a/tryton/tryton/gui/window/view_form/view/form_gtk/textbox.py b/tryton/tryton/gui/window/view_form/view/form_gtk/textbox.py index 9fd21a54e25..1e871d8e971 100644 --- a/tryton/tryton/gui/window/view_form/view/form_gtk/textbox.py +++ b/tryton/tryton/gui/window/view_form/view/form_gtk/textbox.py @@ -122,6 +122,9 @@ def get_buffer(self, textview): iter_end = buf.get_end_iter() return buf.get_text(iter_start, iter_end, False) + def connect(self, callback): + self.textview.connect('focus-out-event', callback) + def display(self): super(TextBox, self).display() value = self.field and self.field.get(self.record) diff --git a/tryton/tryton/gui/window/view_form/view/form_gtk/timedelta.py b/tryton/tryton/gui/window/view_form/view/form_gtk/timedelta.py index 40d8a8291df..fd8e1dc704b 100644 --- a/tryton/tryton/gui/window/view_form/view/form_gtk/timedelta.py +++ b/tryton/tryton/gui/window/view_form/view/form_gtk/timedelta.py @@ -39,6 +39,9 @@ def set_value(self): def get_value(self): return self.entry.get_text() + def connect(self, callback): + self.entry.connect('focus-out-event', callback) + def display(self): super(TimeDelta, self).display() if not self.field: diff --git a/tryton/tryton/gui/window/view_form/view/form_gtk/widget.py b/tryton/tryton/gui/window/view_form/view/form_gtk/widget.py index 7fa75581ffc..935fa9161ab 100644 --- a/tryton/tryton/gui/window/view_form/view/form_gtk/widget.py +++ b/tryton/tryton/gui/window/view_form/view/form_gtk/widget.py @@ -151,6 +151,9 @@ def _format_set(self): raise ValueError(FORMAT_ERROR + attr) functions[key[0]](key[1]) + def connect(self, callback): + pass + def display(self): if not self.field: self._readonly_set(self.attrs.get('readonly', True)) diff --git a/tryton/tryton/gui/window/view_form/view/list_gtk/editabletree.py b/tryton/tryton/gui/window/view_form/view/list_gtk/editabletree.py index e1baec61b91..fc3b5b109c2 100644 --- a/tryton/tryton/gui/window/view_form/view/list_gtk/editabletree.py +++ b/tryton/tryton/gui/window/view_form/view/list_gtk/editabletree.py @@ -245,6 +245,7 @@ def callback(): GLib.idle_add( self.set_cursor, path, column, None, True) return + self.view.screen.record_saved() entry.handler_block(entry.editing_done_id) if keyval == Gdk.KEY_Up: self._key_up(path, model, column) diff --git a/tryton/tryton/gui/window/view_form/view/screen_container.py b/tryton/tryton/gui/window/view_form/view/screen_container.py index fab128de9f7..f18d7e8f783 100644 --- a/tryton/tryton/gui/window/view_form/view/screen_container.py +++ b/tryton/tryton/gui/window/view_form/view/screen_container.py @@ -383,6 +383,15 @@ def set_screen(self, screen): self.but_bookmark.set_sensitive(bool(list(self.bookmarks()))) self.bookmark_match() + def set_context_screen(self, screen): + self.context_screen = screen + self.filter_vbox.pack_start( + screen.widget, expand=False, fill=True, padding=0) + self.filter_vbox.reorder_child(screen.widget, 0) + + def set_context_active(self, active): + self.context_screen.widget.set_sensitive(active) + def show_filter(self): if self.filter_vbox: self.filter_vbox.show()