diff --git a/browser/src/control/Control.StatusBar.js b/browser/src/control/Control.StatusBar.js index a9e3cb89bc7e7..617f0f2055679 100644 --- a/browser/src/control/Control.StatusBar.js +++ b/browser/src/control/Control.StatusBar.js @@ -241,6 +241,10 @@ class StatusBar extends JSDialog.Toolbar { } getToolItems() { + let fitWidthZoomLabel = this.map.getDocType() === 'presentation' + ? _('Zoom to Fit Slide') + : _('Zoom to Fit Page Width'); + return [ this._generateHtmlItem('statusdocpos'), // spreadsheet this._generateHtmlItem('rowcolselcount', 1), // spreadsheet @@ -272,7 +276,7 @@ class StatusBar extends JSDialog.Toolbar { configLabel: _('Overview'), configPeers: ['overviewbreak']}, {type: 'separator', id: 'overviewbreak', orientation: 'vertical', dataPriority: 9, visible: !app.isReadOnly()}, ].concat(window.mode.isTablet() ? [] : [ - {type: 'customtoolitem', id: 'fitwidthzoom', command: 'fitwidthzoom', text: _('Zoom to Fit Page Width'), icon: 'pagewidth.svg', dataPriority: 8, visible: false}, + {type: 'customtoolitem', id: 'fitwidthzoom', command: 'fitwidthzoom', text: fitWidthZoomLabel, icon: 'pagewidth.svg', dataPriority: 8, visible: false}, {type: 'customtoolitem', id: 'zoomreset', command: 'zoomreset', text: _('Reset zoom'), icon: 'zoomreset.svg', dataPriority: 8}, {type: 'customtoolitem', id: 'zoomout', command: 'zoomout', text: _UNO('.uno:ZoomMinus'), icon: 'minus.svg'}, {type: 'menubutton', id: 'zoom', text: '100', selected: 'zoom100', menu: this._generateZoomItems(), image: false}, @@ -449,6 +453,8 @@ class StatusBar extends JSDialog.Toolbar { this.showItem('languagestatusbreak', !app.map.isReadOnlyMode()); this.showItem('permissionmode-container', true); this.showItem('documentstatus-container', true); + this.showItem('fitwidthzoom', true); + this.showItem('zoomreset', false); } break; case 'drawing': diff --git a/browser/src/layer/tile/CanvasTileLayer.js b/browser/src/layer/tile/CanvasTileLayer.js index 510ce25aecfb8..c8e20bbba6ab9 100644 --- a/browser/src/layer/tile/CanvasTileLayer.js +++ b/browser/src/layer/tile/CanvasTileLayer.js @@ -3264,8 +3264,12 @@ window.L.CanvasTileLayer = window.L.Layer.extend({ }, recalculateZoomOnResize: function() { - if (this.isWriter()) + /* in impress, we always recalculate zoom on resize to keep the slide in-view. + * so the second condition is unnecessary, but we keep it for consistency. */ + if (this.isWriter() || this.isImpress()) { this._invalidateZoomFirstFit = true; + this._fitWidthZoom(); + } }, // This is really just called on zoomend @@ -3280,7 +3284,7 @@ window.L.CanvasTileLayer = window.L.Layer.extend({ } if (!maxZoom) { - if (this.isImpress()) maxZoom = 10; + if (this.isImpress()) maxZoom = 14; else if (this.isWriter()) maxZoom = 13; } @@ -3303,6 +3307,8 @@ window.L.CanvasTileLayer = window.L.Layer.extend({ bringCommentsIntoView = true; this._includedCommentsInFirstFit = true; this._firstFitDone = false; + } else if (this.isImpress()) { + recalcFirstFit = true; } // `recalcFirstFit` is used to recalculate/reset the zoom levels to the @@ -3318,11 +3324,32 @@ window.L.CanvasTileLayer = window.L.Layer.extend({ const commentWidth = app.sectionContainer.getSectionWithName(app.CSections.CommentList.name).sectionProperties.commentWidth; let documentWidth = app.activeDocument.fileSize.pX; + let documentHeight = app.activeDocument.fileSize.pY; if (bringCommentsIntoView) documentWidth += commentWidth; var ratio = newSize.x / documentWidth; var zoom = this._map.getScaleZoom(ratio); + if (this.isImpress()) { + /* assume that this is the range of diagonal sizes we are going to get + * for the window and find where the current window size lies in these, + * clamping it between [0,1]. then find appropriate margin between 4-9% + * based on the window size factor. */ + const MAX_DIAGONAL = 2200; + const MIN_DIAGONAL = 1000; + const diagonal = Math.sqrt(window.innerWidth * window.innerWidth + window.innerHeight + window.innerHeight); + const factor = clamp((diagonal - MIN_DIAGONAL) / (MAX_DIAGONAL - MIN_DIAGONAL), 0, 1); + + const percentMargin = 0.04 + factor * (0.09 - 0.04); + const availW = newSize.x * (1 - percentMargin); + const availH = newSize.y * (1 - percentMargin); + + const xRatio = availW / documentWidth; + const yRatio = availH / documentHeight; + ratio = Math.min(xRatio, yRatio); + zoom = this._map.getScaleZoom(ratio); + } + if (maxZoom) zoom = Math.min(maxZoom, Math.max(0.1, zoom)); diff --git a/cypress_test/integration_tests/common/desktop_helper.js b/cypress_test/integration_tests/common/desktop_helper.js index 459a5788f8289..2b4d988d73015 100644 --- a/cypress_test/integration_tests/common/desktop_helper.js +++ b/cypress_test/integration_tests/common/desktop_helper.js @@ -209,8 +209,11 @@ function selectZoomLevel(zoomLevel, makeZoomVisible = true) { // Force because sometimes the icons are scrolled off the screen to the right if (makeZoomVisible) makeZoomItemsVisible(); + cy.wait(1000); cy.cGet('#toolbar-down #zoom .arrowbackground').click(); + cy.wait(1000); cy.cGet('#zoom-dropdown').contains('.ui-combobox-entry', zoomLevel).click(); + cy.wait(1000); shouldHaveZoomLevel(zoomLevel); cy.log('<< selectZoomLevel - end'); diff --git a/cypress_test/integration_tests/desktop/impress/editable_area_spec.js b/cypress_test/integration_tests/desktop/impress/editable_area_spec.js index 17187a9e82abd..c1a3177b4480f 100644 --- a/cypress_test/integration_tests/desktop/impress/editable_area_spec.js +++ b/cypress_test/integration_tests/desktop/impress/editable_area_spec.js @@ -2,6 +2,7 @@ var helper = require('../../common/helper'); var impressHelper = require('../../common/impress_helper'); +var desktopHelper = require('../../common/desktop_helper'); var ceHelper = require('../../common/contenteditable_helper'); function selectTextShape(i) { @@ -33,6 +34,7 @@ describe(['taga11yenabled'], 'Editable area - Basic typing and caret moving', fu // do not cover shapes with sidebar cy.cGet('#sidebar-panel').should('not.be.visible'); cy.cGet('div.clipboard').as('clipboard'); + desktopHelper.selectZoomLevel('60', false); }); it.skip('Editing top text shape', function () { diff --git a/cypress_test/integration_tests/desktop/impress/image_operation_spec.js b/cypress_test/integration_tests/desktop/impress/image_operation_spec.js index 062b0e01fd26a..a2c58309dc166 100644 --- a/cypress_test/integration_tests/desktop/impress/image_operation_spec.js +++ b/cypress_test/integration_tests/desktop/impress/image_operation_spec.js @@ -87,7 +87,7 @@ describe(['tagdesktop'], 'Image Operation Tests', function() { triggerNewSVGForShapeInTheCenter(); - helper.assertImageSize(463, 185); + helper.assertImageSize(322, 129); //Keep ratio checked //sidebar needs more time @@ -101,6 +101,6 @@ describe(['tagdesktop'], 'Image Operation Tests', function() { triggerNewSVGForShapeInTheCenter(); - helper.assertImageSize(579, 232); + helper.assertImageSize(402, 161); }); }); diff --git a/cypress_test/integration_tests/desktop/impress/statusbar_spec.js b/cypress_test/integration_tests/desktop/impress/statusbar_spec.js index a6b9dea4f4d16..4dbd0f7d2321e 100644 --- a/cypress_test/integration_tests/desktop/impress/statusbar_spec.js +++ b/cypress_test/integration_tests/desktop/impress/statusbar_spec.js @@ -10,7 +10,7 @@ describe(['tagdesktop', 'tagnextcloud', 'tagproxy'], 'Statusbar tests.', functio if (Cypress.env('INTEGRATION') === 'nextcloud') { desktopHelper.showStatusBarIfHidden (); } - desktopHelper.shouldHaveZoomLevel('70'); + cy.viewport(1920, 1080); }); it('Selected slide.', function() { @@ -22,7 +22,7 @@ describe(['tagdesktop', 'tagnextcloud', 'tagproxy'], 'Statusbar tests.', functio }); it('Change zoom level.', function() { - desktopHelper.resetZoomLevel(); + desktopHelper.fitWidthZoom(); desktopHelper.shouldHaveZoomLevel('100'); desktopHelper.zoomIn(); desktopHelper.shouldHaveZoomLevel('120'); @@ -31,9 +31,23 @@ describe(['tagdesktop', 'tagnextcloud', 'tagproxy'], 'Statusbar tests.', functio }); it('Select zoom level.', function() { - desktopHelper.resetZoomLevel(); + desktopHelper.fitWidthZoom(); desktopHelper.shouldHaveZoomLevel('100'); desktopHelper.selectZoomLevel('280', false); desktopHelper.shouldHaveZoomLevel('280'); }); + + it('Dynamic Zoom', function () { + desktopHelper.fitWidthZoom(); + desktopHelper.shouldHaveZoomLevel('100'); + + cy.viewport(1420, 1080); + desktopHelper.fitWidthZoom(); + desktopHelper.shouldHaveZoomLevel('70'); + + desktopHelper.zoomIn(); + desktopHelper.zoomIn(); + desktopHelper.fitWidthZoom(); + desktopHelper.shouldHaveZoomLevel('70'); + }); }); diff --git a/cypress_test/integration_tests/desktop/writer/annotation_spec.js b/cypress_test/integration_tests/desktop/writer/annotation_spec.js index ed8f11ba2685c..5da81ebf309d2 100644 --- a/cypress_test/integration_tests/desktop/writer/annotation_spec.js +++ b/cypress_test/integration_tests/desktop/writer/annotation_spec.js @@ -272,7 +272,7 @@ describe(['tagdesktop'], 'Annotation Tests', function() { cy.cGet('.annotation-button-delete').should('be.visible'); }); - it('Global opreations without doc focused', function () { + it('Global operations without doc focused', function () { cy.getFrameWindow().then(function (win) { cy.spy(win.app.socket, 'sendMessage').as('sendMessage'); });