@eclipse-scout/core
Version:
Eclipse Scout runtime
208 lines (187 loc) • 9.34 kB
text/typescript
/*
* Copyright (c) 2010, 2024 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 {AbstractLayout, Dimension, graphics, HtmlComponent, HtmlCompPrefSizeOptions, Rectangle, SplitBox} from '../../../index';
import $ from 'jquery';
export class SplitBoxLayout extends AbstractLayout {
splitBox: SplitBox;
constructor(splitBox: SplitBox) {
super();
this.splitBox = splitBox;
}
override layout($container: JQuery) {
// Extract components
let htmlContainer = HtmlComponent.get($container), // = split-area
$splitter = $container.children('.splitter'),
$fields = $container.children('.form-field'),
htmlFirstField = HtmlComponent.optGet($fields.eq(0)),
htmlSecondField = HtmlComponent.optGet($fields.eq(1)),
// Calculate available size for split area
splitXAxis = this.splitBox.splitHorizontal;
$splitter.removeClass('hidden');
let firstFieldBounds: Rectangle,
availableSize = htmlContainer.availableSize().subtract(htmlContainer.insets()),
hasFirstField = (htmlFirstField && htmlFirstField.isVisible()),
hasSecondField = (htmlSecondField && htmlSecondField.isVisible()),
hasTwoFields = hasFirstField && hasSecondField,
hasOneField = !hasTwoFields && (hasFirstField || hasSecondField),
splitterPosition = this.splitBox.getEffectiveSplitterPosition();
// remove splitter size from available with, only when both fields are visible
// otherwise the splitter is invisible and requires no space.
let availableSizeForFields = new Dimension(availableSize);
if (hasTwoFields) {
if (splitXAxis) { // "|"
availableSizeForFields.width -= htmlFirstField.margins().right;
} else { // "--"
availableSizeForFields.height -= htmlFirstField.margins().bottom;
}
}
// Default case: two fields
if (hasTwoFields) {
// Distribute available size to the two fields according to the splitter position ratio
let firstFieldSize = new Dimension(availableSizeForFields);
let secondFieldSize = new Dimension(availableSizeForFields);
this.computeInnerFieldsDimensions(splitXAxis, firstFieldSize, secondFieldSize, splitterPosition);
// Calculate and set bounds (splitter and second field have to be moved)
firstFieldBounds = new Rectangle(0, 0, firstFieldSize.width, firstFieldSize.height);
let secondFieldBounds = new Rectangle(0, 0, secondFieldSize.width, secondFieldSize.height);
if (splitXAxis) { // "|"
$splitter.cssLeft(firstFieldBounds.width);
secondFieldBounds.x = firstFieldBounds.width + htmlFirstField.margins().right;
} else { // "--"
$splitter.cssTop(firstFieldBounds.height);
secondFieldBounds.y = firstFieldBounds.height + htmlFirstField.margins().bottom;
}
htmlFirstField.setBounds(firstFieldBounds);
htmlSecondField.setBounds(secondFieldBounds);
} else {
// Special case: only one field (or none at all)
if (hasOneField) {
let singleField = hasFirstField ? htmlFirstField : htmlSecondField,
singleFieldSize = availableSize.subtract(singleField.margins());
singleField.setBounds(new Rectangle(0, 0, singleFieldSize.width, singleFieldSize.height));
}
$splitter.addClass('hidden');
}
// Calculate collapse button position
let collapseHandle = this.splitBox.collapseHandle;
if (collapseHandle) {
let $collapseHandle = collapseHandle.$container;
// Show collapse handle, if split box has two fields which are visible (one field may be collapsed)
let collapseHandleVisible = this.splitBox.firstField && this.splitBox.firstField.visible && this.splitBox.secondField && this.splitBox.secondField.visible;
$collapseHandle.setVisible(collapseHandleVisible);
let x = null;
if (hasTwoFields) {
// - if 1st field is collapsible -> align button on the right side of the field (there is not enough space on the left side)
// - if 2nd field is collapsible -> button is always aligned on the right side using CSS
if (this.splitBox.collapsibleField === this.splitBox.firstField) {
let collapseHandleSize = graphics.size($collapseHandle);
x = firstFieldBounds.width - collapseHandleSize.width;
}
}
$collapseHandle.cssLeft(x);
}
}
override preferredLayoutSize($container: JQuery, options?: HtmlCompPrefSizeOptions): Dimension {
// Extract components
let htmlContainer = HtmlComponent.get($container), // = split-area
$fields = $container.children('.form-field'),
htmlFirstField = HtmlComponent.optGet($fields.eq(0)),
htmlSecondField = HtmlComponent.optGet($fields.eq(1));
let splitXAxis = this.splitBox.splitHorizontal;
let splitterPosition = this.splitBox.getEffectiveSplitterPosition();
// compute width hints
let firstFieldOptions = $.extend({}, options);
let secondFieldOptions = $.extend({}, options);
if (options.widthHint) {
let firstFieldSizeHint = new Dimension(options.widthHint, 0);
let secondFieldSizeHint = new Dimension(options.widthHint, 0);
this.computeInnerFieldsDimensions(splitXAxis, firstFieldSizeHint, secondFieldSizeHint, splitterPosition);
firstFieldOptions.widthHint = firstFieldSizeHint.width;
secondFieldOptions.widthHint = secondFieldSizeHint.width;
}
// Get preferred size of fields
let firstFieldSize = new Dimension(0, 0);
if (htmlFirstField) {
firstFieldSize = htmlFirstField.prefSize(firstFieldOptions)
.add(htmlFirstField.margins());
}
let secondFieldSize = new Dimension(0, 0);
if (htmlSecondField) {
secondFieldSize = htmlSecondField.prefSize(secondFieldOptions)
.add(htmlSecondField.margins());
}
// Calculate prefSize
let prefSize: Dimension;
if (splitXAxis) { // "|"
prefSize = new Dimension(
firstFieldSize.width + secondFieldSize.width,
Math.max(firstFieldSize.height, secondFieldSize.height)
);
} else { // "--"
prefSize = new Dimension(
Math.max(firstFieldSize.width, secondFieldSize.width),
firstFieldSize.height + secondFieldSize.height
);
}
prefSize = prefSize.add(htmlContainer.insets());
return prefSize;
}
/**
* Distributes the available size according to the split axis and the splitter position
*
* @param splitXAxis truthy if the splitter splits vertical |, falsy if the splitter splits horizontal --
* @param firstFieldSize initialize with the total available space. Will be adjusted to the available size of the first field.
* @param secondFieldSize initialize with the total available space. Will be adjusted to the available size of the second field.
* @param splitterPosition effective splitter position
*/
computeInnerFieldsDimensions(splitXAxis: boolean, firstFieldSize: Dimension, secondFieldSize: Dimension, splitterPosition: number) {
if (splitXAxis) { // "|"
if (this.splitBox.splitterPositionType === SplitBox.SplitterPositionType.RELATIVE_FIRST) {
// Relative first
firstFieldSize.width = Math.floor(firstFieldSize.width * splitterPosition);
secondFieldSize.width -= firstFieldSize.width;
} else if (this.splitBox.splitterPositionType === SplitBox.SplitterPositionType.RELATIVE_SECOND) {
// Relative second
secondFieldSize.width = Math.floor(secondFieldSize.width * splitterPosition);
firstFieldSize.width -= secondFieldSize.width;
} else {
// Absolute
splitterPosition = Math.min(splitterPosition, firstFieldSize.width);
if (this.splitBox.splitterPositionType === SplitBox.SplitterPositionType.ABSOLUTE_SECOND) {
firstFieldSize.width = firstFieldSize.width - splitterPosition;
secondFieldSize.width = splitterPosition;
} else {
firstFieldSize.width = splitterPosition;
secondFieldSize.width = secondFieldSize.width - splitterPosition;
}
}
} else { // "--"
if (this.splitBox.splitterPositionType === SplitBox.SplitterPositionType.RELATIVE_FIRST) {
// Relative first
firstFieldSize.height = Math.floor(firstFieldSize.height * splitterPosition);
secondFieldSize.height -= firstFieldSize.height;
} else if (this.splitBox.splitterPositionType === SplitBox.SplitterPositionType.RELATIVE_SECOND) {
// Relative second
secondFieldSize.height = Math.floor(secondFieldSize.height * splitterPosition);
firstFieldSize.height -= secondFieldSize.height;
} else {
// Absolute
splitterPosition = Math.min(splitterPosition, firstFieldSize.height);
if (this.splitBox.splitterPositionType === SplitBox.SplitterPositionType.ABSOLUTE_SECOND) {
firstFieldSize.height = firstFieldSize.height - splitterPosition;
secondFieldSize.height = splitterPosition;
} else {
firstFieldSize.height = splitterPosition;
secondFieldSize.height = secondFieldSize.height - splitterPosition;
}
}
}
}
}