diff options
| author | Géry Debongnie <ged@odoo.com> | 2018-12-06 12:39:06 +0000 |
|---|---|---|
| committer | Géry Debongnie <ged@odoo.com> | 2018-12-07 13:16:06 +0000 |
| commit | 40dd121938552d3f88289d2e5fcd1d57a0dd01bb (patch) | |
| tree | 4830c8ffdea1e10d35c7ddf096f536bd8999240f /addons/web/static/src/js/chrome/action_manager.js | |
| parent | 48a50b4bc011fc6dfc82a673f921fad941dc79cf (diff) | |
[REF] web: move ControlPanel inside controllers/actions
This branch introduces a large-scale reorganization of the
component tree generated by the web client. The short version is
that now, the control panel is a child of the view controller and
no longer a sibling. Graphically (and simplified), we go from
this:
webClient
/ | \
... ... actionManager
/ \
controlPanel viewController
/ \
to this:
webClient
/ | \
... ... actionManager
|
viewController
/ | \
ControlPanelController
/ \
CPRenderer CPModel
The motivation is that this work moves the code where it should be.
Before this commit, it was kind of weird to have code in the
controllers to render buttons outside of their root node (in
renderButtons). Also, the action manager had to take care of
coordinating search view states between view transitions.
So, this work simplifies the code. It also makes it easier to
extend. We see day after day that Odoo needs to take care of more
complex UI needs, and in many cases, these needs were quite
difficult to implement (we prefer spaghettis in our plates, not in
our code). The changes in this branch should open the way to
implement these features.
For example,
- it will now be easy to add the possibility of views (for example,
the search view or a new ControlPanel view) to add custom buttons
(of type action or object) in the control panel.
- Another need will be to serialize/ restore the state of the
search view across action boundaries (needed by the dashboard).
- Another example is the possibility for views to customize easily
the presence/absence of sub menus (filters/groupbys/favorites/
time range/...)
- Another need is an easier way for views/client action to customize
their control panel.
This commit also contains a large rewrite of the search view (so it
is more inline with our architecture and easier to maintain).
Part of task 1893568
Co-authored-by: Aaron Bohy <aab@odoo.com>
Co-authored-by: Mathieu Duckerts-Antoine <dam@odoo.com>
Co-authored-by: Martin Geubelle <mge@odoo.com>
Diffstat (limited to 'addons/web/static/src/js/chrome/action_manager.js')
| -rw-r--r-- | addons/web/static/src/js/chrome/action_manager.js | 169 |
1 files changed, 63 insertions, 106 deletions
diff --git a/addons/web/static/src/js/chrome/action_manager.js b/addons/web/static/src/js/chrome/action_manager.js index 18bbb6a3681..c798189c0a0 100644 --- a/addons/web/static/src/js/chrome/action_manager.js +++ b/addons/web/static/src/js/chrome/action_manager.js @@ -10,10 +10,8 @@ odoo.define('web.ActionManager', function (require) { */ var AbstractAction = require('web.AbstractAction'); -var Bus = require('web.Bus'); var concurrency = require('web.concurrency'); var Context = require('web.Context'); -var ControlPanel = require('web.ControlPanel'); var config = require('web.config'); var core = require('web.core'); var Dialog = require('web.Dialog'); @@ -24,7 +22,7 @@ var Widget = require('web.Widget'); var _t = core._t; var ActionManager = Widget.extend({ - className: 'o_content', + className: 'o_action_manager', custom_events: { breadcrumb_clicked: '_onBreadcrumbClicked', history_back: '_onHistoryBack', @@ -64,17 +62,6 @@ var ActionManager = Widget.extend({ this.currentDialogController = null; }, /** - * @override - */ - start: function () { - // AAB: temporarily instantiate a unique main ControlPanel used by - // controllers in the controllerStack - this.controlPanel = new ControlPanel(this); - var def = this.controlPanel.insertBefore(this.$el); - - return $.when(def, this._super.apply(this, arguments)); - }, - /** * Called each time the action manager is attached into the DOM. */ on_attach_callback: function () { @@ -266,14 +253,6 @@ var ActionManager = Widget.extend({ if (controller.scrollPosition) { this.trigger_up('scrollTo', controller.scrollPosition); } - - if (!controller.widget.need_control_panel) { - this.controlPanel.do_hide(); - } else { - this.controlPanel.update({ - breadcrumbs: this._getBreadcrumbs(), - }, {clear: false}); - } }, /** * Closes the current dialog, if any. Because we listen to the 'closed' @@ -330,19 +309,12 @@ var ActionManager = Widget.extend({ return this._executeActionInDialog(action, options); } + var controller = self.controllers[action.controllerID]; return this.clearUncommittedChanges() .then(function () { - var controller = self.controllers[action.controllerID]; - var widget = controller.widget; - // AAB: this will be moved to the Controller - if (widget.need_control_panel) { - // set the ControlPanel bus on the controller to allow it to - // communicate its status - widget.set_cp_bus(self.controlPanel.get_bus()); - } return self.dp.add(self._startController(controller)); }) - .then(function (controller) { + .then(function () { if (self.currentDialogController) { self._closeDialog({ silent: true }); } @@ -358,7 +330,7 @@ var ActionManager = Widget.extend({ } // update the internal state and the DOM - self._pushController(controller, options); + self._pushController(controller); // toggle the fullscreen mode for actions in target='fullscreen' self._toggleFullscreen(); @@ -387,12 +359,6 @@ var ActionManager = Widget.extend({ var self = this; var controller = this.controllers[action.controllerID]; var widget = controller.widget; - // AAB: this will be moved to the Controller - if (widget.need_control_panel) { - // set the ControlPanel bus on the controller to allow it to - // communicate its status - widget.set_cp_bus(new Bus()); - } return this._startController(controller).then(function (controller) { var prevDialogOnClose; @@ -472,20 +438,18 @@ var ActionManager = Widget.extend({ } var controllerID = _.uniqueId('controller_'); + + var index = this._getControllerStackIndex(options); + options.breadcrumbs = this._getBreadcrumbs(this.controllerStack.slice(0, index)); options.controllerID = controllerID; + var widget = new ClientAction(this, action, options); var controller = { actionID: action.jsID, + index: index, jsID: controllerID, - widget: new ClientAction(this, action, options), + title: widget.getTitle(), + widget: widget, }; - // AAB: TODO: simplify this with AbstractAction (implement a getTitle - // function that returns action.name by default, and that can be - // overriden in client actions and view controllers) - Object.defineProperty(controller, 'title', { - get: function () { - return controller.widget.get('title') || action.display_name || action.name; - }, - }); this.controllers[controllerID] = controller; action.controllerID = controllerID; return this._executeAction(action, options).done(function () { @@ -586,34 +550,52 @@ var ActionManager = Widget.extend({ return $.when(); }, /** - * Returns a description of the current stack of controllers, used to render - * the breadcrumbs. It is an array of Objects with keys 'title' (what to - * display in the breadcrumbs) and 'controllerID' (the ID of the - * corresponding controller, used to restore it when this part of the + * Returns a description of the controllers in the given controller stack. + * It is used to render the breadcrumbs. It is an array of Objects with keys + * 'title' (what to display in the breadcrumbs) and 'controllerID' (the ID + * of the corresponding controller, used to restore it when this part of the * breadcrumbs is clicked). - * Ignores the content of the stack of controllers if the action of the - * last controller of the stack is flagged with 'no_breadcrumbs', indicating - * that the breadcrumbs should not be displayed for that action. * * @private + * @param {string[]} controllerStack * @returns {Object[]} */ - _getBreadcrumbs: function () { + _getBreadcrumbs: function (controllerStack) { var self = this; - var currentController = this.getCurrentController(); - var noBreadcrumbs = !currentController || - this.actions[currentController.actionID].context.no_breadcrumbs; - if (noBreadcrumbs) { - return []; - } - return _.map(this.controllerStack, function (controllerID) { + return _.map(controllerStack, function (controllerID) { return { - title: self.controllers[controllerID].title, controllerID: controllerID, + title: self.controllers[controllerID].title, }; }); }, /** + * Returns the index where a controller should be inserted in the controller + * stack according to the given options. By default, a controller is pushed + * on the top of the stack. + * + * @private + * @param {options} [options.clear_breadcrumbs=false] if true, insert at + * index 0 and remove all other controllers + * @param {options} [options.index=null] if given, that index is returned + * @param {options} [options.replace_last_action=false] if true, replace the + * last controller of the stack + * @returns {integer} index + */ + _getControllerStackIndex: function (options) { + var index; + if ('index' in options) { + index = options.index; + } else if (options.clear_breadcrumbs) { + index = 0; + } else if (options.replace_last_action) { + index = this.controllerStack.length - 1; + } else { + index = this.controllerStack.length; + } + return index; + }, + /** * Returns an object containing information about the given controller, like * its title, its action's id, the active_id and active_ids of the action... * @@ -625,7 +607,7 @@ var ActionManager = Widget.extend({ var controller = this.controllers[controllerID]; var action = this.actions[controller.actionID]; var state = { - title: controller.title, + title: controller.widget.getTitle(), }; if (action.id) { state.action = action.id; @@ -706,41 +688,28 @@ var ActionManager = Widget.extend({ * @param {Object} controller * @param {string} controller.jsID * @param {Widget} controller.widget - * @param {Object} [options] - * @param {Object} [options.clear_breadcrumbs=false] if true, destroys all - * controllers from the controller stack before adding the given one - * @param {Object} [options.replace_last_action=false] if true, replaces the - * last controller of the controller stack by the given one - * @param {integer} [options.index] if given, pushes the controller at that - * position in the controller stack, and destroys the controllers with an - * higher index - */ - _pushController: function (controller, options) { - options = options || {}; + * @param {integer} controller.index the controller is pushed at that + * position in the controller stack and controllers with an higher index + * are destroyed + */ + _pushController: function (controller) { var self = this; // detach the current controller this._detachCurrentController(); - // empty the controller stack or replace the last controller as requested, - // destroy the removed controllers and push the new controller to the stack - var toDestroy; - if (options.clear_breadcrumbs) { - toDestroy = this.controllerStack; - this.controllerStack = []; - } else if (options.replace_last_action && this.controllerStack.length > 0) { - toDestroy = [this.controllerStack.pop()]; - } else if (options.index !== undefined) { - toDestroy = this.controllerStack.splice(options.index); - // reject from the list of controllers to destroy the one that we are - // currently pushing, or those linked to the same action as the one - // linked to the controller that we are pushing - toDestroy = _.reject(toDestroy, function (controllerID) { - return controllerID === controller.jsID || - self.controllers[controllerID].actionID === controller.actionID; - }); - } + // push the new controller to the stack at the given position, and + // destroy controllers with an higher index + var toDestroy = this.controllerStack.slice(controller.index); + // reject from the list of controllers to destroy the one that we are + // currently pushing, or those linked to the same action as the one + // linked to the controller that we are pushing + toDestroy = _.reject(toDestroy, function (controllerID) { + return controllerID === controller.jsID || + self.controllers[controllerID].actionID === controller.actionID; + }); this._removeControllers(toDestroy); + this.controllerStack = this.controllerStack.slice(0, controller.index); this.controllerStack.push(controller.jsID); // append the new controller to the DOM @@ -861,7 +830,7 @@ var ActionManager = Widget.extend({ return $.when(def).then(function () { return $.when(controller.widget.do_show()).then(function () { var index = _.indexOf(self.controllerStack, controllerID); - self._pushController(controller, {index: index}); + self._pushController(controller, index); }); }); }, @@ -878,19 +847,7 @@ var ActionManager = Widget.extend({ * @returns {Deferred<Object>} resolved with the controller when it is ready */ _startController: function (controller) { - var self = this; var fragment = document.createDocumentFragment(); - // AAB: change this logic to stop using the properties mixin - controller.widget.on("change:title", this, function () { - if (self.getCurrentController() !== controller) { - return; - } - var action = self.actions[controller.actionID]; - if (!action.flags || !action.flags.headless) { - var breadcrumbs = self._getBreadcrumbs(); - self.controlPanel.update({breadcrumbs: breadcrumbs}, {clear: false}); - } - }); return controller.widget.appendTo(fragment).then(function () { return controller; }); |
