From 51d5e4d4308ba98921b1d6ea8cf946d0e17d0a7a Mon Sep 17 00:00:00 2001 From: nightwing Date: Sun, 19 Mar 2023 20:54:47 +0400 Subject: [PATCH] feat: add ResizeObserver to make calling editor.resize optional --- doc/site/js/main.js | 6 ----- doc/site/style.css | 4 ++- src/ace.js | 3 --- src/ace_test.js | 56 ++++++++++++++++++++++++++++++++++++++++- src/editor.js | 1 + src/virtual_renderer.js | 36 ++++++++++++++++++++++++++ 6 files changed, 95 insertions(+), 11 deletions(-) diff --git a/doc/site/js/main.js b/doc/site/js/main.js index ffea39e5976..3c11442cc88 100644 --- a/doc/site/js/main.js +++ b/doc/site/js/main.js @@ -153,12 +153,6 @@ $(function() { $.bbq.pushState(state); }); - $('#tabnav a[data-toggle="tab"]').on('shown', function (e) { - $(".tab-content .tab-pane.active .ace_editor").each(function(i, el){ - el.env.onResize(); - }); - }); - $(window).on("hashchange", function(e) { _gaq.push(['_trackPageview',location.pathname + location.search + location.hash]); tabs.each(function() { diff --git a/doc/site/style.css b/doc/site/style.css index af9c0d3ec1d..54d2ed15130 100644 --- a/doc/site/style.css +++ b/doc/site/style.css @@ -496,4 +496,6 @@ img { .nav>li>a.external-nav:hover { text-decoration: underline; -} \ No newline at end of file +} + +pre { background-color: white!important } \ No newline at end of file diff --git a/src/ace.js b/src/ace.js index fa1bf05fd35..5198e4cf6cb 100644 --- a/src/ace.js +++ b/src/ace.js @@ -8,7 +8,6 @@ "include loader_build"; var dom = require("./lib/dom"); -var event = require("./lib/event"); var Range = require("./range").Range; var Editor = require("./editor").Editor; @@ -66,9 +65,7 @@ exports.edit = function(el, options) { onResize: editor.resize.bind(editor, null) }; if (oldNode) env.textarea = oldNode; - event.addListener(window, "resize", env.onResize); editor.on("destroy", function() { - event.removeListener(window, "resize", env.onResize); env.editor.container.env = null; // prevent memory leak on old ie }); editor.container.env = editor.env = env; diff --git a/src/ace_test.js b/src/ace_test.js index fa9462e01e8..1917b7ac039 100644 --- a/src/ace_test.js +++ b/src/ace_test.js @@ -1,5 +1,4 @@ if (typeof process !== "undefined") { - require("amd-loader"); require("./test/mockdom"); } @@ -102,6 +101,61 @@ module.exports = { ace.config.set("useStrictCSP", false); assert.ok(getStyleNode()); }, + "test: resizeObserver": function(done) { + var mockObserver = { + disconnect: function() { mockObserver.target = null; }, + observe: function(el) { + mockObserver.target = el; + }, + $create: function(fn) { + mockObserver.callback = fn; + return mockObserver; + }, + call: function() { + setTimeout(function() { + if (mockObserver.target) + mockObserver.callback([{contentRect: mockObserver.target.getBoundingClientRect()}]); + }); + } + }; + if (!window.ResizeObserver) { + window.ResizeObserver = mockObserver.$create; + } + var editor = ace.edit(null); + document.body.appendChild(editor.container); + editor.container.style.width = "100px"; + editor.container.style.height = "100px"; + mockObserver.call(); + editor.resize(true); + assert.ok(!editor.renderer.$resizeTimer.isPending()); + assert.equal(editor.renderer.$size.width, 100); + editor.container.style.width = "200px"; + mockObserver.call(); + setTimeout(function() { + if (editor.renderer.$resizeTimer.isPending()) + editor.renderer.$resizeTimer.call(); + assert.equal(editor.renderer.$size.width, 200); + editor.container.style.height = "200px"; + mockObserver.call(); + setTimeout(function() { + assert.ok(editor.renderer.$resizeTimer.isPending()); + editor.container.style.height = "100px"; + mockObserver.call(); + setTimeout(function() { + assert.ok(!editor.renderer.$resizeTimer.isPending()); + editor.setOption("useResizeObserver", false); + editor.container.style.height = "300px"; + mockObserver.call(); + assert.ok(!editor.renderer.$resizeObserver); + editor.setOption("useResizeObserver", true); + assert.ok(editor.renderer.$resizeObserver); + if (window.ResizeObserver === mockObserver.$create) + window.ResizeObserver = undefined; + done(); + }, 15); + }, 15); + }, 15); + }, "test: edit template" : function() { var template = document.createElement("template"); var div = document.createElement("div"); diff --git a/src/editor.js b/src/editor.js index 8fc5808b14d..9b8ee510617 100644 --- a/src/editor.js +++ b/src/editor.js @@ -2897,6 +2897,7 @@ config.defineOptions(Editor.prototype, "editor", { hasCssTransforms: "renderer", maxPixelHeight: "renderer", useTextareaForIME: "renderer", + useResizeObserver: "renderer", scrollSpeed: "$mouseHandler", dragDelay: "$mouseHandler", diff --git a/src/virtual_renderer.js b/src/virtual_renderer.js index f73f4271273..dc465a2976a 100644 --- a/src/virtual_renderer.js +++ b/src/virtual_renderer.js @@ -2,6 +2,7 @@ var oop = require("./lib/oop"); var dom = require("./lib/dom"); +var lang = require("./lib/lang"); var config = require("./config"); var GutterLayer = require("./layer/gutter").Gutter; var MarkerLayer = require("./layer/marker").Marker; @@ -157,6 +158,7 @@ var VirtualRenderer = function(container, theme) { this.updateCharacterSize(); this.setPadding(4); + this.$addResizeObserver(); config.resetOptions(this); config._signal("renderer", this); }; @@ -344,6 +346,7 @@ var VirtualRenderer = function(container, theme) { width = el.clientWidth || el.scrollWidth; var changes = this.$updateCachedSize(force, gutterWidth, width, height); + if (this.$resizeTimer) this.$resizeTimer.cancel(); if (!this.$size.scrollerHeight || (!width && !height)) return this.resizing = 0; @@ -1799,6 +1802,7 @@ var VirtualRenderer = function(container, theme) { this.$cursorLayer.destroy(); this.removeAllListeners(); this.container.textContent = ""; + this.setOption("useResizeObserver", false); }; this.$updateCustomScrollbar = function (val) { @@ -1836,10 +1840,42 @@ var VirtualRenderer = function(container, theme) { } }; + this.$addResizeObserver = function() { + if (!window.ResizeObserver || this.$resizeObserver) return; + var self = this; + this.$resizeTimer = lang.delayedCall(function() { + if (!self.destroyed) self.onResize(); + }, 50); + this.$resizeObserver = new window.ResizeObserver(function(e) { + var w = e[0].contentRect.width; + var h = e[0].contentRect.height; + if ( + Math.abs(self.$size.width - w) > 1 + || Math.abs(self.$size.height - h) > 1 + ) { + self.$resizeTimer.delay(); + } else { + self.$resizeTimer.cancel(); + } + }); + this.$resizeObserver.observe(this.container); + }; + }).call(VirtualRenderer.prototype); config.defineOptions(VirtualRenderer.prototype, "renderer", { + useResizeObserver: { + set: function(value) { + if (!value && this.$resizeObserver) { + this.$resizeObserver.disconnect(); + this.$resizeTimer.cancel(); + this.$resizeTimer = this.$resizeObserver = null; + } else if (value && !this.$resizeObserver) { + this.$addResizeObserver(); + } + } + }, animatedScroll: {initialValue: false}, showInvisibles: { set: function(value) {