summaryrefslogtreecommitdiff
path: root/addons/web/static/src
diff options
context:
space:
mode:
authorChristophe Matthieu <chm@odoo.com>2016-03-23 16:37:56 +0100
committerChristophe Matthieu <chm@odoo.com>2016-08-23 17:03:56 +0200
commit6d04c70abea6bbbe198c474a00b8a9c91a8ab69a (patch)
tree797cb5f7142cba36d09f817c1e6ee770c7c3e96a /addons/web/static/src
parenta765b3e643f4ad98f319b983dda313e0ebdf931f (diff)
[IMP] web: add ace editor for xml and python edition
* display all lines and don't display the scroll bar into ace editor * lazy read ace lib and add missing lib file * use not minified version of ace lib * Move ace lib from website to web * Added ace widget for webclient * XML mode for view text box * Python mode for server action text box * Removed web_ace_editor.xml, instead added template in base.xml itself * Added widget=ace_editor in compute field of ir.model.fields
Diffstat (limited to 'addons/web/static/src')
-rw-r--r--addons/web/static/src/js/views/form_widgets.js101
-rw-r--r--addons/web/static/src/less/form_view_layout.less9
-rw-r--r--addons/web/static/src/xml/base.xml13
3 files changed, 121 insertions, 2 deletions
diff --git a/addons/web/static/src/js/views/form_widgets.js b/addons/web/static/src/js/views/form_widgets.js
index a72702a2d08..334f0db758c 100644
--- a/addons/web/static/src/js/views/form_widgets.js
+++ b/addons/web/static/src/js/views/form_widgets.js
@@ -1,6 +1,7 @@
odoo.define('web.form_widgets', function (require) {
"use strict";
+var ajax = require('web.ajax');
var core = require('web.core');
var crash_manager = require('web.crash_manager');
var data = require('web.data');
@@ -1579,6 +1580,102 @@ var FieldToggleBoolean = common.AbstractField.extend({
},
});
+/**
+ This widget is intended to be used on Text fields. It will provide Ace Editor for editing XML and Python.
+*/
+
+var AceEditor = common.AbstractField.extend(common.ReinitializeFieldMixin, {
+ template: "AceEditor",
+ willStart: function() {
+ if (!window.ace && !this.loadJS_def) {
+ this.loadJS_def = ajax.loadJS('/web/static/lib/ace/ace.js').then(function () {
+ return $.when(ajax.loadJS('/web/static/lib/ace/mode-python.js'),
+ ajax.loadJS('/web/static/lib/ace/mode-xml.js'),
+ ajax.loadJS('/web/static/lib/ace/theme-monokai.js'));
+ });
+ }
+ return $.when(this._super(), this.loadJS_def);
+ },
+ initialize_content: function () {
+ if (! this.get("effective_readonly")) {
+ var self = this;
+
+ this.aceEditor = ace.edit(this.$('.ace-view-editor')[0]);
+ this.aceEditor.setOptions({"maxLines": Infinity});
+ this.aceEditor.setTheme("ace/theme/monokai");
+ this.aceEditor.$blockScrolling = true;
+
+ var scrollIntoViewIfNeeded = _.throttle(function () {
+ var node = self.aceEditor.renderer.textarea;
+ if (node.scrollIntoViewIfNeeded) {
+ node.scrollIntoViewIfNeeded(false);
+ } else {
+ var offsetParent = node.offsetParent;
+ while (offsetParent) {
+ var elY = 0;
+ var elH = node.offsetHeight+20;
+ var parent = node;
+ while (offsetParent && parent) {
+ elY += node.offsetTop;
+ // get if a parent have a scrollbar
+ parent = node.parentNode;
+ while (parent != offsetParent &&
+ (parent.tagName === "BODY" || ["auto", "scroll"].indexOf(window.getComputedStyle(parent).overflowY) === -1)) {
+ parent = parent.parentNode;
+ }
+ node = parent;
+ if (parent !== offsetParent) {
+ elY -= parent.offsetTop;
+ parent = null;
+ }
+ offsetParent = node.offsetParent;
+ }
+
+ if ((node.tagName === "BODY" || ["auto", "scroll"].indexOf(window.getComputedStyle(node).overflowY) !== -1) &&
+ (node.scrollTop + node.clientHeight) < (elY + elH)) {
+ node.scrollTop = (elY + elH) - node.clientHeight;
+ }
+ }
+ }
+ });
+ var $moveTextAreaToCursor = this.aceEditor.renderer.$moveTextAreaToCursor;
+ self.aceEditor.renderer.$moveTextAreaToCursor = function() {
+ $moveTextAreaToCursor.call(this);
+ if (parseInt($(self.aceEditor.renderer.textarea).css('top'), 10) >= 0) {
+ scrollIntoViewIfNeeded();
+ }
+ };
+
+ this.aceSession = this.aceEditor.getSession();
+ this.aceSession.setUseWorker(false);
+ this.aceSession.setMode("ace/mode/"+(this.options.mode || 'xml'));
+
+ this.aceEditor.on("blur", function() {
+ if (self.aceSession.getUndoManager().hasUndo()) {
+ self.set_value(self.aceSession.getValue());
+ }
+ });
+ }
+ },
+ destroy_content: function() {
+ if (this.aceEditor) {
+ this.aceEditor.destroy();
+ }
+ },
+ render_value: function() {
+ if (! this.get("effective_readonly")) {
+ var value = formats.format_value(this.get('value'), this);
+ this.aceSession.setValue(value);
+
+ } else {
+ var txt = this.get("value") || '';
+ this.$(".oe_form_text_content").text(txt);
+ }
+ },
+ focus: function() {
+ return this.aceEditor.focus();
+ },
+});
/**
* Registry of form fields, called by :js:`instance.web.FormView`.
@@ -1614,8 +1711,8 @@ core.form_widget_registry
.add('kanban_state_selection', KanbanSelection)
.add('statinfo', StatInfo)
.add('timezone_mismatch', TimezoneMismatch)
- .add('label_selection', LabelSelection);
-
+ .add('label_selection', LabelSelection)
+ .add('ace', AceEditor);
/**
* Registry of widgets usable in the form view that can substitute to any possible
diff --git a/addons/web/static/src/less/form_view_layout.less b/addons/web/static/src/less/form_view_layout.less
index d94c3c3129d..281cf0d217e 100644
--- a/addons/web/static/src/less/form_view_layout.less
+++ b/addons/web/static/src/less/form_view_layout.less
@@ -267,6 +267,15 @@
.o_group.o_inner_group.oe_subtotal_footer {
.oe_right;
}
+
+ //ace
+ .o_ace_view_editor {
+ width: 100%;
+ .ace-view-editor {
+ height: 300px;
+ padding: 0 1em;
+ }
+ }
}
// Overriden style when form view in modal
diff --git a/addons/web/static/src/xml/base.xml b/addons/web/static/src/xml/base.xml
index a9f51b12c48..56b69d91225 100644
--- a/addons/web/static/src/xml/base.xml
+++ b/addons/web/static/src/xml/base.xml
@@ -1250,6 +1250,19 @@
</div>
</t>
+<t t-name="AceEditor">
+ <t t-if="!widget.get('effective_readonly')">
+ <div class="oe_form_field o_ace_view_editor oe_ace_open">
+ <div class="ace-view-editor"/>
+ </div>
+ </t>
+ <t t-if="widget.get('effective_readonly')">
+ <div class="oe_form_field oe_form_field_text" t-att-style="widget.node.attrs.style">
+ <span class="oe_form_text_content"></span>
+ </div>
+ </t>
+</t>
+
<t t-name="notification-box">
<div t-att-class="'alert alert-dismissible alert-' + type" role="alert">
<button type="button" class="close" data-dismiss="alert" aria-label="Close">