@eclipse-scout/core
Version:
Eclipse Scout runtime
151 lines (128 loc) • 6.28 kB
text/typescript
/*
* Copyright (c) 2010, 2023 BSI Business Systems Integration AG
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*/
import {Dimension, Form, FormLayout, graphics, HtmlComponent, Insets, Point, Rectangle} from '../index';
export class DialogLayout extends FormLayout {
declare form: Form;
autoSize: boolean;
shrinkEnabled: boolean;
constructor(form: Form) {
super(form);
this.autoSize = true;
this.shrinkEnabled = false;
}
override layout($container: JQuery) {
if (!this.autoSize) {
super.layout($container);
return;
}
let currentBounds,
htmlComp = this.form.htmlComp,
prefBounds = this.form.prefBounds(),
dialogMargins = htmlComp.margins(),
windowSize = $container.windowSize();
if (prefBounds) {
currentBounds = prefBounds;
} else {
currentBounds = htmlComp.bounds();
}
let dialogSize = this._calcSize($container, currentBounds, prefBounds);
// Add markers to be able to style the dialog in a different way when it uses the full width or height
$container
.toggleClass('full-width', this.form.maximized || (currentBounds.x === 0 && dialogMargins.horizontal() === 0 && windowSize.width === dialogSize.width))
.toggleClass('full-height', this.form.maximized || (currentBounds.y === 0 && dialogMargins.vertical() === 0 && windowSize.height === dialogSize.height));
// Ensure the dialog can only get larger, not smaller.
// This prevents 'snapping' the dialog back to the calculated size when a field changes its visibility, but the user previously enlarged the dialog.
// This must not happen when the dialog is laid out the first time (-> when it is opened, because it has not the right size yet and may get too big)
if (htmlComp.layouted && !this.shrinkEnabled) {
dialogSize.width = Math.max(dialogSize.width, currentBounds.width);
dialogSize.height = Math.max(dialogSize.height, currentBounds.height);
}
graphics.setSize($container, dialogSize);
super.layout($container);
}
/**
* @param currentBounds
* bounds as returned by the graphics.bounds() function, i.e. position is the CSS
* position (top-left of "margin box"), dimension excludes margins
* @param prefBounds
* optional preferred bounds (same expectations as with "currentBounds")
* @returns
* adjusted size excluding margins (suitable to pass to graphics.setSize())
*/
protected _calcSize($container: JQuery, currentBounds: Rectangle, prefBounds?: Rectangle): Dimension {
let dialogSize,
htmlComp = this.form.htmlComp,
dialogMargins = htmlComp.margins(),
windowSize = $container.windowSize();
if (this.form.maximized) {
return windowSize;
}
if (prefBounds) {
dialogSize = prefBounds.dimension();
// Assume (0,0) as position so that as much as possible of the stored size can be used.
// The true position will be set later in Form#position (called by FormController).
dialogSize = DialogLayout.fitContainerInWindow(windowSize, new Point(), dialogSize, dialogMargins);
return dialogSize;
}
// Calculate preferred width first...
dialogSize = this.preferredLayoutSize($container, {
widthOnly: true
});
dialogSize = DialogLayout.fitContainerInWindow(windowSize, currentBounds.point(), dialogSize, dialogMargins);
// ...then calculate the actual preferred size based on the width. This is necessary because the dialog may contain fields with wrapping content. Without a width hint the height would not be correct.
dialogSize = this.preferredLayoutSize($container, {
widthHint: dialogSize.width
}).ceil(); // always round up. If we'd round a height of 380.00005 pixel down
// there is not enough space to display the group-box, thus the browser would show scrollbars.
dialogSize = DialogLayout.fitContainerInWindow(windowSize, currentBounds.point(), dialogSize, dialogMargins);
return dialogSize;
}
/**
* Calculates the new container size and position. If the given containerSize is larger than the windowSize, the size will be adjusted.
*
* @param windowSize total size of the window
* @param containerPosition current CSS position of the container (top-left of the "margin box")
* @param containerSize preferred size of container (excluding margins)
* @param containerMargins margins of the container
* @returns the new, adjusted container size (excluding margins)
*/
static fitContainerInWindow(windowSize: Dimension, containerPosition: Point, containerSize: Dimension, containerMargins: Insets): Dimension {
// class .dialog may specify a margin
// currentBounds.y and x are 0 initially, but if size changes while dialog is open they are greater than 0
// This guarantees the dialog size may not exceed the document size
let maxWidth = (windowSize.width - containerMargins.horizontal() - containerPosition.x);
let maxHeight = (windowSize.height - containerMargins.vertical() - containerPosition.y);
// Calculate new dialog size, ensuring that the dialog is not larger than container
let size = new Dimension();
size.width = Math.min(maxWidth, containerSize.width);
size.height = Math.min(maxHeight, containerSize.height);
return size;
}
/**
* Returns the coordinates to place the given container in the optical middle of the window.
*
* @param $container
* @returns new X,Y position of the container
*/
static positionContainerInWindow($container: JQuery): Point {
let
windowSize = $container.windowSize(),
containerSize = HtmlComponent.get($container).size(true),
left = (windowSize.width - containerSize.width) / 2,
top = (windowSize.height - containerSize.height) / 2;
// optical middle (move up 20% of distance between window and dialog)
let opticalMiddleOffset = (top / 5);
top -= opticalMiddleOffset;
// Ensure integer numbers
left = Math.floor(left);
top = Math.floor(top);
return new Point(left, top);
}
}