/* * 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; } } } } }