Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorClaudio Guglielmo2021-05-17 16:45:11 +0000
committerClaudio Guglielmo2021-05-31 07:52:07 +0000
commit58187b8942ced371a223315b1b0ee2859e8a76f8 (patch)
tree2401e1324f760f2caef7aa445ede664a36b622b9
parent1afdc9970b689b10ea20b2bb2f5898642e973f5f (diff)
downloadorg.eclipse.scout.rt-58187b8942ced371a223315b1b0ee2859e8a76f8.tar.gz
org.eclipse.scout.rt-58187b8942ced371a223315b1b0ee2859e8a76f8.tar.xz
org.eclipse.scout.rt-58187b8942ced371a223315b1b0ee2859e8a76f8.zip
BrushUp: improve menu, context menu, menu bar, combo menu, button
Improve menubar and menus - The menus now have a padding and hover effect. - The menu bar cannot have a margin/padding anymore, the first and last menu entries need to have the margin - The rules for a menu are moved to Menu.less so that a menu can easier be used outside of a menu bar. This also makes the rules less complicated. - Menus and buttons now use flex box to layout the content -> vertical align helper and other hacks can be removed. - The style for the font icons are removed, they were not needed but made overriding more complex. - Renamed .menubox to .menubar-box because it belongs to the MenubarBox.js and not to MenuBox.js - The selected state of menus and buttons is now correctly visualized. Improve focus style - The focus style for menus and buttons is updated and simplified. - No pseudo element is necessary anymore. Harmonize buttons and menus - Rename class default-menu to default - A menu button now uses the same style as a regular button, no duplicated rules anymore Simplify ButtonField - The additional div is not necessary anymore because of the more simple focus state. - The additional rules are not necessary anymore Add borderless style to Button.js - The existing link style cannot use paddings because it would break a lot of alignments. The new style looks like a menu in the menu bar. Improve context menu - The context menu now uses a modern style. - The old code could not handle the rounded corners well -> refactored context menu animations. - The paddings are now animated. - If an image is loaded during animation the size of the popup is now adjusted. Improve combo menu - Added a separator to make it not look the same as a menu with sub menus - The combo menu now supports the button and default menu style - A regular menu now shows the sub menu icon even if there is no text and icon which makes it easier to use it in a combo menu Improve form field menu - Form fields without border (check box, label fields etc). now don't increase the menu bar height anymore by adding an unnecessary margin. - Form fields are now moved 10px to the left to remove the mandatory indicator gap so that it is not necessary anymore to explicitly add the class no_mandatory_indicator.
-rw-r--r--eclipse-scout-core/src/desktop/DesktopDense.less5
-rw-r--r--eclipse-scout-core/src/desktop/bench/DesktopBench.js3
-rw-r--r--eclipse-scout-core/src/desktop/bench/DesktopBench.less5
-rw-r--r--eclipse-scout-core/src/desktop/outline/Outline.js8
-rw-r--r--eclipse-scout-core/src/desktop/outline/Outline.less29
-rw-r--r--eclipse-scout-core/src/desktop/outline/navigation/NavigateButton.js5
-rw-r--r--eclipse-scout-core/src/desktop/outline/navigation/NavigateButton.less18
-rw-r--r--eclipse-scout-core/src/desktop/outline/navigation/NavigateUpButton.js1
-rw-r--r--eclipse-scout-core/src/desktop/toolbox/DesktopToolBox.less9
-rw-r--r--eclipse-scout-core/src/focus/FocusManager.js2
-rw-r--r--eclipse-scout-core/src/form/FormMenu.js7
-rw-r--r--eclipse-scout-core/src/form/FormMenu.less1
-rw-r--r--eclipse-scout-core/src/form/fields/button/Button.js26
-rw-r--r--eclipse-scout-core/src/form/fields/button/Button.less86
-rw-r--r--eclipse-scout-core/src/form/fields/groupbox/GroupBox.less8
-rw-r--r--eclipse-scout-core/src/form/fields/tabbox/TabBox.less24
-rw-r--r--eclipse-scout-core/src/form/fields/wrappedform/WrappedFormField.less12
-rw-r--r--eclipse-scout-core/src/index.js4
-rw-r--r--eclipse-scout-core/src/index.less3
-rw-r--r--eclipse-scout-core/src/main.less78
-rw-r--r--eclipse-scout-core/src/menu/ComboMenu.js31
-rw-r--r--eclipse-scout-core/src/menu/ComboMenu.less93
-rw-r--r--eclipse-scout-core/src/menu/ContextMenuPopup.js407
-rw-r--r--eclipse-scout-core/src/menu/ContextMenuPopup.less59
-rw-r--r--eclipse-scout-core/src/menu/ContextMenuPopupLayout.js132
-rw-r--r--eclipse-scout-core/src/menu/Menu.js32
-rw-r--r--eclipse-scout-core/src/menu/Menu.less111
-rw-r--r--eclipse-scout-core/src/menu/form/field/FormFieldMenu.less44
-rw-r--r--eclipse-scout-core/src/menu/menubar/MenuBar.js4
-rw-r--r--eclipse-scout-core/src/menu/menubar/MenuBar.less310
-rw-r--r--eclipse-scout-core/src/menu/menubar/MenuBarBox.js (renamed from eclipse-scout-core/src/menu/menubar/MenubarBox.js)8
-rw-r--r--eclipse-scout-core/src/menu/menubar/MenuBarBoxLayout.js (renamed from eclipse-scout-core/src/menu/menubar/MenubarBoxLayout.js)2
-rw-r--r--eclipse-scout-core/src/menu/menubar/MenuBarLayout.js27
-rw-r--r--eclipse-scout-core/src/planner/Planner.js3
-rw-r--r--eclipse-scout-core/src/planner/Planner.less5
-rw-r--r--eclipse-scout-core/src/planner/PlannerHeader.less6
-rw-r--r--eclipse-scout-core/src/popup/Popup.js5
-rw-r--r--eclipse-scout-core/src/popup/Popup.less4
-rw-r--r--eclipse-scout-core/src/popup/PopupLayout.js34
-rw-r--r--eclipse-scout-core/src/style/colors.less37
-rw-r--r--eclipse-scout-core/src/style/mixins.less29
-rw-r--r--eclipse-scout-core/src/style/sizes.less26
-rw-r--r--eclipse-scout-core/src/table/Table.js3
-rw-r--r--eclipse-scout-core/src/table/Table.less5
-rw-r--r--eclipse-scout-core/src/table/TableHeader.less6
-rw-r--r--eclipse-scout-core/src/tile/fields/tablefield/TileTableField.less4
-rw-r--r--eclipse-scout-core/src/tree/Tree.js3
-rw-r--r--eclipse-scout-core/src/tree/Tree.less5
-rw-r--r--eclipse-scout-core/src/widget/Widget.js3
-rw-r--r--eclipse-scout-core/test/menu/ContextMenuPopupSpec.js10
-rw-r--r--eclipse-scout-core/test/menu/MenuBarSpec.js56
-rw-r--r--org.eclipse.scout.rt.client/src/main/java/org/eclipse/scout/rt/client/ui/form/fields/button/IButton.java1
52 files changed, 898 insertions, 941 deletions
diff --git a/eclipse-scout-core/src/desktop/DesktopDense.less b/eclipse-scout-core/src/desktop/DesktopDense.less
index 69c9d659ab..2adc5fe2dc 100644
--- a/eclipse-scout-core/src/desktop/DesktopDense.less
+++ b/eclipse-scout-core/src/desktop/DesktopDense.less
@@ -48,15 +48,14 @@
/* MenuBar.less */
- .menubar {
- & > .menubox {
+ .menubar:not(.main-menubar) {
+ & > .menubar-box {
& > .menu-item {
padding-top: 3px; /* @menubar-item-padding-y; */
padding-bottom: 3px; /* @menubar-item-padding-y; */
/* By making sure menu items are always at least the same height as form fields, it is easier to align them with each other, especially when zoomed */
min-height: @logical-grid-row-height-dense; /* @logical-grid-row-height; */
-
}
}
}
diff --git a/eclipse-scout-core/src/desktop/bench/DesktopBench.js b/eclipse-scout-core/src/desktop/bench/DesktopBench.js
index 96e6ce0316..86f1b21dcb 100644
--- a/eclipse-scout-core/src/desktop/bench/DesktopBench.js
+++ b/eclipse-scout-core/src/desktop/bench/DesktopBench.js
@@ -410,7 +410,8 @@ export default class DesktopBench extends Widget {
}
if (content) {
if (content instanceof Table) {
- content.menuBar.setCssClass('main-menubar');
+ content.menuBar.addCssClass('main-menubar');
+ content.menuBar.removeCssClass('bounded');
}
content.displayViewId = 'C';
}
diff --git a/eclipse-scout-core/src/desktop/bench/DesktopBench.less b/eclipse-scout-core/src/desktop/bench/DesktopBench.less
index e0f1f2df8a..2e0967add9 100644
--- a/eclipse-scout-core/src/desktop/bench/DesktopBench.less
+++ b/eclipse-scout-core/src/desktop/bench/DesktopBench.less
@@ -112,11 +112,10 @@
}
& > .menubar-container {
- padding-left: 6px;
- padding-right: @detail-table-header-menubar-padding-right;
- & > .menubar > .menubox > .menu-item {
+ & > .menubar > .menubar-box > .menu-item {
margin-right: 12px;
+ border-radius: @border-radius;
}
}
}
diff --git a/eclipse-scout-core/src/desktop/outline/Outline.js b/eclipse-scout-core/src/desktop/outline/Outline.js
index 71b58cd2a7..e126d84be4 100644
--- a/eclipse-scout-core/src/desktop/outline/Outline.js
+++ b/eclipse-scout-core/src/desktop/outline/Outline.js
@@ -20,7 +20,7 @@ import {
HtmlComponent,
keyStrokeModifier,
MenuBar,
- menus as menus_1,
+ menus as menuUtil,
MessageBoxController,
NavigateButton,
NavigateDownButton,
@@ -899,7 +899,7 @@ export default class Outline extends Tree {
// DetailContent can be null or it is the tableRowDetail. Don't show menus on OutlineOverview.
if (selectedPage.detailTable) {
detailTable = selectedPage.detailTable;
- menuItems = menus_1.filter(detailTable.menus, ['Table.EmptySpace'], false, true);
+ menuItems = menuUtil.filter(detailTable.menus, ['Table.EmptySpace'], false, true);
tableControls = detailTable.tableControls;
detailTable.setMenuBarVisible(false);
this._attachDetailMenusListener(detailTable);
@@ -908,7 +908,7 @@ export default class Outline extends Tree {
let parentPage = selectedPage.parentNode;
if (parentPage && parentPage.detailTable) {
detailTable = parentPage.detailTable;
- menuItems = menuItems.concat(menus_1.filter(detailTable.menus, ['Table.SingleSelection'], false, true));
+ menuItems = menuItems.concat(menuUtil.filter(detailTable.menus, ['Table.SingleSelection'], false, true));
detailTable.setMenuBarVisible(false);
this._attachDetailMenusListener(detailTable);
}
@@ -1196,7 +1196,7 @@ export default class Outline extends Tree {
this.updateKeyStrokes(menus, oldMenus);
this._setProperty('menus', menus);
if (this.titleMenuBar) { // _setMenus is called by parent class Tree.js, at this time titleMenuBar is not yet initialized
- let menuItems = menus_1.filter(this.menus, ['Tree.Header']);
+ let menuItems = menuUtil.filter(this.menus, ['Tree.Header']);
this.titleMenuBar.setMenuItems(menuItems);
}
}
diff --git a/eclipse-scout-core/src/desktop/outline/Outline.less b/eclipse-scout-core/src/desktop/outline/Outline.less
index 425ce09ff8..84ad37a171 100644
--- a/eclipse-scout-core/src/desktop/outline/Outline.less
+++ b/eclipse-scout-core/src/desktop/outline/Outline.less
@@ -85,7 +85,6 @@
cursor: pointer;
color: @outline-title-color;
font-size: 19px;
- #scout.overflow-ellipsis-nowrap();
padding: 0 @outline-title-padding-right 0 @outline-title-padding-left;
border-bottom: 1px solid @outline-title-border-color;
font-weight: @font-weight-bold;
@@ -103,6 +102,7 @@
& > .text {
flex-grow: 1;
+ #scout.overflow-ellipsis-nowrap();
}
& > .menubar {
@@ -110,6 +110,11 @@
border: none;
width: auto;
flex-grow: 0;
+ margin-right: -10px;
+
+ & > .menubar-box > .menu-item.last {
+ margin-right: 0;
+ }
}
}
@@ -226,6 +231,10 @@
& > .outline-title {
padding-left: @compact-outline-title-padding-x;
padding-right: @compact-outline-title-padding-x;
+
+ & > .menubar {
+ margin-right: -@menubar-item-icononly-padding-x;
+ }
}
& > .tree-data {
@@ -247,11 +256,27 @@
right: 0;
top: 0;
width: auto;
- padding: @compact-outline-node-padding-y - 7px @compact-outline-node-padding-x 0 0;
+ padding: @compact-outline-node-padding-y - 7px @compact-outline-node-padding-x - @menubar-item-icononly-padding-x 0 0;
}
& > .detail-menubar {
padding: 0 @compact-outline-node-padding-x @compact-outline-node-padding-y @compact-outline-node-padding-x;
+ margin-left: -@menubar-item-icononly-padding-x;
+
+ & > .menubar-box > .menu-item {
+ padding-left: @menubar-item-icononly-padding-x;
+ padding-right: @menubar-item-icononly-padding-x;
+ margin-left: @menubar-item-icononly-margin-x;
+ margin-right: @menubar-item-icononly-margin-x;
+
+ &.first {
+ margin-left: 0;
+ }
+
+ &.last {
+ margin-right: 0;
+ }
+ }
}
& > .form {
diff --git a/eclipse-scout-core/src/desktop/outline/navigation/NavigateButton.js b/eclipse-scout-core/src/desktop/outline/navigation/NavigateButton.js
index b2c472755f..15a897bd19 100644
--- a/eclipse-scout-core/src/desktop/outline/navigation/NavigateButton.js
+++ b/eclipse-scout-core/src/desktop/outline/navigation/NavigateButton.js
@@ -27,10 +27,6 @@ export default class NavigateButton extends Menu {
this.node = null;
this.outline = null;
this.actionStyle = Action.ActionStyle.BUTTON;
- /**
- * Additional CSS class to be applied in _render method.
- */
- this._additionalCssClass = '';
this._addCloneProperties(['node', 'outline', 'altKeyStrokeContext']);
this.inheritAccessibility = false;
}
@@ -49,7 +45,6 @@ export default class NavigateButton extends Menu {
this.updateEnabled();
super._render();
this.$container.addClass('navigate-button small');
- this.$container.addClass(this._additionalCssClass);
this.altKeyStrokeContext.registerKeyStroke(this);
}
diff --git a/eclipse-scout-core/src/desktop/outline/navigation/NavigateButton.less b/eclipse-scout-core/src/desktop/outline/navigation/NavigateButton.less
index 0931331a42..e2bc79cc7d 100644
--- a/eclipse-scout-core/src/desktop/outline/navigation/NavigateButton.less
+++ b/eclipse-scout-core/src/desktop/outline/navigation/NavigateButton.less
@@ -10,18 +10,22 @@
*/
.navigate-button {
+ &.menu-button > .font-icon {
+ font-size: 18px;
+ }
+
&.up {
color: @navigate-up-button-color;
border-color: @navigate-up-button-border-color;
+ }
+}
- & > .font-icon {
- color: @navigate-up-button-color;
- }
+.menubar-box > .menu-button.navigate-button {
+ &.up {
+ margin-right: 6px;
}
- .menubar > .menubox > &.menu-item.down.menu-button:not(.last) {
- /* Right and left margin of the navigate buttons should be equal.
- Setting this margin-right is especially necessary if on the right side of these buttons is another button. */
- margin-right: @bench-padding-x;
+ &.down.left-of-button {
+ margin-right: @bench-padding-x - @menubar-button-margin;
}
}
diff --git a/eclipse-scout-core/src/desktop/outline/navigation/NavigateUpButton.js b/eclipse-scout-core/src/desktop/outline/navigation/NavigateUpButton.js
index 99dd84ad33..761c1c43a8 100644
--- a/eclipse-scout-core/src/desktop/outline/navigation/NavigateUpButton.js
+++ b/eclipse-scout-core/src/desktop/outline/navigation/NavigateUpButton.js
@@ -17,7 +17,6 @@ export default class NavigateUpButton extends NavigateButton {
super();
this._defaultIconId = icons.ANGLE_UP;
this._defaultText = 'ui.Up';
- this._additionalCssClass = 'small-gap';
this.iconId = this._defaultIconId;
this.keyStroke = 'backspace';
}
diff --git a/eclipse-scout-core/src/desktop/toolbox/DesktopToolBox.less b/eclipse-scout-core/src/desktop/toolbox/DesktopToolBox.less
index 89ae8c6711..09de08ae6e 100644
--- a/eclipse-scout-core/src/desktop/toolbox/DesktopToolBox.less
+++ b/eclipse-scout-core/src/desktop/toolbox/DesktopToolBox.less
@@ -25,6 +25,8 @@
padding: 0 14px;
border-radius: @desktop-tool-box-item-border-radius;
color: inherit;
+ display: inline-flex;
+ align-items: center;
&.compact {
padding-left: 10px;
@@ -35,10 +37,6 @@
}
}
- &:not(.menu-button) > .icon {
- top: 0; // TODO BrushUp remove when -1 hack is removed from menu.less
- }
-
& > .font-icon {
font-size: @desktop-tool-box-item-font-size;
}
@@ -53,7 +51,8 @@
color: inherit;
}
- &.selected {
+ &.selected.has-popup {
+ color: inherit;
background-color: @desktop-tool-box-item-selected-background-color;
}
diff --git a/eclipse-scout-core/src/focus/FocusManager.js b/eclipse-scout-core/src/focus/FocusManager.js
index 43b9271e5d..c62ea095aa 100644
--- a/eclipse-scout-core/src/focus/FocusManager.js
+++ b/eclipse-scout-core/src/focus/FocusManager.js
@@ -361,7 +361,7 @@ export default class FocusManager {
firstElement = candidate;
}
- if (!firstDefaultButton && $candidate.is('.default-menu')) {
+ if (!firstDefaultButton && $candidate.is('.default')) {
firstDefaultButton = candidate;
}
diff --git a/eclipse-scout-core/src/form/FormMenu.js b/eclipse-scout-core/src/form/FormMenu.js
index d13b802310..1a2f4a6460 100644
--- a/eclipse-scout-core/src/form/FormMenu.js
+++ b/eclipse-scout-core/src/form/FormMenu.js
@@ -112,6 +112,13 @@ export default class FormMenu extends Menu {
}
}
+ _renderSelected() {
+ super._renderSelected();
+
+ // Form menu always has a popup (form could be set later, so super call cannot set the class correctly)
+ this.$container.addClass('has-popup');
+ }
+
_canOpenPopup() {
// A menu can be opened in the menu bar but also in a context menu, where it will be cloned.
// The form itself won't be cloned, so there can always be only one rendered form.
diff --git a/eclipse-scout-core/src/form/FormMenu.less b/eclipse-scout-core/src/form/FormMenu.less
index ae637d53a8..73398e49c1 100644
--- a/eclipse-scout-core/src/form/FormMenu.less
+++ b/eclipse-scout-core/src/form/FormMenu.less
@@ -13,5 +13,4 @@
& > .form > .root-group-box > .main-menubar {
background-color: @form-menu-popup-main-menubar-background-color;
}
-
}
diff --git a/eclipse-scout-core/src/form/fields/button/Button.js b/eclipse-scout-core/src/form/fields/button/Button.js
index 7c8a2fe41e..8c824ac8be 100644
--- a/eclipse-scout-core/src/form/fields/button/Button.js
+++ b/eclipse-scout-core/src/form/fields/button/Button.js
@@ -50,7 +50,8 @@ export default class Button extends FormField {
DEFAULT: 0,
TOGGLE: 1,
RADIO: 2,
- LINK: 3
+ LINK: 3,
+ BORDERLESS: 4
};
static SUBMENU_ICON = icons.ANGLE_DOWN_BOLD;
@@ -122,19 +123,17 @@ export default class Button extends FormField {
_render() {
let $button;
if (this.displayStyle === Button.DisplayStyle.LINK) {
- // Render as link-button/ menu-item.
- // This is a bit weird: the model defines a button, but in the UI it behaves like a menu-item.
- // Probably it would be more reasonable to change the configuration (which would lead to additional
- // effort required to change an existing application).
- $button = this.$parent.makeDiv('link-button');
- // Separate $link element to have a smaller focus border
- this.$link = $button.appendDiv('menu-item link');
- this.$buttonLabel = this.$link.appendSpan('button-label text');
+ // Render as link-button
+ $button = this.$parent.makeDiv('link-button menu-item');
+ this.$buttonLabel = $button.appendSpan('button-label text');
} else {
// render as button
$button = this.$parent.makeElement('<button>')
.addClass('button');
- this.$buttonLabel = $button.appendSpan('button-label');
+ if (this.displayStyle === Button.DisplayStyle.BORDERLESS) {
+ $button.addClass('borderless');
+ }
+ this.$buttonLabel = $button.appendSpan('button-label text');
if (Device.get().supportsOnlyTouch()) {
$button.setTabbable(false);
@@ -156,7 +155,7 @@ export default class Button extends FormField {
}, this);
if (this.label || !this.iconId) { // no indicator when _only_ the icon is visible
let icon = icons.parseIconId(Button.SUBMENU_ICON);
- this.$submenuIcon = (this.$link || $button)
+ this.$submenuIcon = $button
.appendSpan('submenu-icon')
.text(icon.iconCharacter);
}
@@ -269,7 +268,6 @@ export default class Button extends FormField {
_renderEnabled() {
super._renderEnabled();
if (this.displayStyle === Button.DisplayStyle.LINK) {
- this.$link.setEnabled(this.enabledComputed);
this.$field.setTabbable(this.enabledComputed && !Device.get().supportsOnlyTouch());
}
}
@@ -303,7 +301,7 @@ export default class Button extends FormField {
* Adds an image or font-based icon to the button by adding either an IMG or SPAN element to the button.
*/
_renderIconId() {
- let $iconTarget = this.$link || this.$fieldContainer;
+ let $iconTarget = this.$fieldContainer;
$iconTarget.icon(this.iconId);
let $icon = $iconTarget.data('$icon');
if ($icon) {
@@ -337,7 +335,7 @@ export default class Button extends FormField {
}
get$Icon() {
- let $iconTarget = this.$link || this.$fieldContainer;
+ let $iconTarget = this.$fieldContainer;
return $iconTarget.children('.icon');
}
diff --git a/eclipse-scout-core/src/form/fields/button/Button.less b/eclipse-scout-core/src/form/fields/button/Button.less
index 4d6b7f95a4..7953fd16ec 100644
--- a/eclipse-scout-core/src/form/fields/button/Button.less
+++ b/eclipse-scout-core/src/form/fields/button/Button.less
@@ -8,25 +8,6 @@
* Contributors:
* BSI Business Systems Integration AG - initial API and implementation
*/
-.button-field > .button {
-
- /* The 18px min-height is required because in Chrome the height of the ::after element
- * is not calculated properly when height is set to 100%. This has the effect that buttons
- * change their size when a button changes its focus state. See ticket 194111.
- */
- #scout.vertical-align-helper-after(18px);
-
- & > .button-label {
- padding: @text-field-padding-y - @button-padding-y - @button-margin-top 0;
- }
-
- &.selected {
- border-style: inset;
- border-right-color: @border-color;
- border-bottom-color: @border-color;
- }
-}
-
.button-field > .field {
margin-left: @mandatory-indicator-width;
@@ -35,67 +16,22 @@
}
}
-.button-field > .field.link-button {
- /* add a transparent border to align text with normal buttons which also have a border */
- border-top: 1px solid transparent;
- border-bottom: 1px solid transparent;
-
- .logical-grid-layout > & {
- /* make sure link is centered when button has a fixed height (which is the case with logical grid layout) */
- #scout.vertical-align-helper-before();
- white-space: nowrap;
- }
-
- & > .link.menu-item {
- /* Draw underline in child element, otherwise the position would not be correct in FF */
- text-decoration: none;
- vertical-align: middle;
- color: @link-color;
-
- & > .text {
- text-decoration: underline;
- overflow: hidden;
- text-overflow: ellipsis;
- }
-
- & > .text,
- & > .submenu-icon,
- & > .icon {
- /* Reset IE hack for link buttons because it breaks ellipsis tooltip */
- pointer-events: auto;
-
- /* Make sure button is not clickable if it is covered by a glass pane */
-
- .glasspane-parent&,
- .glasspane-parent & {
- pointer-events: none;
- }
- }
-
- &:hover {
- color: @link-hover-color;
- }
+.button-field > .button {
+ // Ensure button height remains the same if fill vertical = false
+ min-height: @logical-grid-row-height;
+}
- &:active, &.active {
- color: @link-active-color;
- }
+.button-field > .link-button {
+ padding-left: 0;
+ padding-right: 0;
- &.disabled {
- color: @disabled-color;
- }
+ &:hover,
+ &:active, &.active {
+ background-color: transparent;
}
&:focus {
- outline: none;
-
- & > .link.menu-item::after {
- #scout.button-focus();
- }
- }
-
- & > .link.menu-item > .icon.image-icon {
- /* override hack in Menu.css */
- top: 0;
+ #scout.box-shadow-focus();
}
}
diff --git a/eclipse-scout-core/src/form/fields/groupbox/GroupBox.less b/eclipse-scout-core/src/form/fields/groupbox/GroupBox.less
index 4a4eee7fd2..dc513f0f37 100644
--- a/eclipse-scout-core/src/form/fields/groupbox/GroupBox.less
+++ b/eclipse-scout-core/src/form/fields/groupbox/GroupBox.less
@@ -32,7 +32,6 @@
& > .menubar {
margin-left: @mandatory-indicator-width;
- margin-right: 0;
background-color: inherit;
#scout.menubar-background-color-inherit;
}
@@ -76,6 +75,13 @@
display: inline-block;
border: none;
background-color: transparent;
+ vertical-align: middle;
+ padding-left: 10px;
+
+ & > .menubar-box > .menu-item {
+ margin-top: 0;
+ margin-bottom: 0;
+ }
}
}
diff --git a/eclipse-scout-core/src/form/fields/tabbox/TabBox.less b/eclipse-scout-core/src/form/fields/tabbox/TabBox.less
index ed894614f7..85466972fc 100644
--- a/eclipse-scout-core/src/form/fields/tabbox/TabBox.less
+++ b/eclipse-scout-core/src/form/fields/tabbox/TabBox.less
@@ -64,11 +64,6 @@
& > .tab-item > .title > .sub-label {
display: block;
}
-
- & > .overflow-tab-item.menu-item {
- padding-top: 12px;
- padding-bottom: 20px;
- }
}
&.spread-even {
@@ -90,8 +85,7 @@
& > .menubar {
position: absolute;
#scout.menubar-background-color-inherit();
- border-bottom: none;
- padding-left: 12px;
+ border-bottom-color: transparent;
}
& > .status {
@@ -221,28 +215,16 @@
}
.overflow-tab-item.menu-item {
- padding: @tab-item-title-padding-top @tab-item-padding-x - 8px @tab-item-title-padding-bottom;
- /* Reduce padding a little and compensate with margin to make popup header a little smaller */
- margin: 0 8px;
-
font-weight: bold;
-
- /* This makes sure the overflow menu item is not larger than a tab item (especially if a larger font is used).
- It also prevents the popup from jumping. */
- line-height: 1;
+ vertical-align: middle;
+ color: @tab-item-color;
&::before {
display: none;
}
- & > .text {
- padding: 0;
-
- }
-
& > .submenu-icon {
padding-left: 4px;
- line-height: 15px;
}
&:focus {
diff --git a/eclipse-scout-core/src/form/fields/wrappedform/WrappedFormField.less b/eclipse-scout-core/src/form/fields/wrappedform/WrappedFormField.less
index c0aed49885..20edad4478 100644
--- a/eclipse-scout-core/src/form/fields/wrappedform/WrappedFormField.less
+++ b/eclipse-scout-core/src/form/fields/wrappedform/WrappedFormField.less
@@ -36,8 +36,16 @@
& > .root-group-box > .menubar {
margin-left: @mandatory-indicator-width;
- margin-right: 0;
- padding-left: 0;
#scout.menubar-background-color-inherit;
+
+ & > .menubar-box > .menu-item {
+ &.first {
+ margin-left: 0;
+ }
+
+ &.last {
+ margin-right: 0;
+ }
+ }
}
}
diff --git a/eclipse-scout-core/src/index.js b/eclipse-scout-core/src/index.js
index 697ce7c968..aaaa4c6259 100644
--- a/eclipse-scout-core/src/index.js
+++ b/eclipse-scout-core/src/index.js
@@ -240,8 +240,8 @@ export {default as MenuBarLayout} from './menu/menubar/MenuBarLayout';
export {default as MenuBarLeftKeyStroke} from './menu/menubar/MenuBarLeftKeyStroke';
export {default as MenuBarRightKeyStroke} from './menu/menubar/MenuBarRightKeyStroke';
export {default as MenuBarPopup} from './menu/menubar/MenuBarPopup';
-export {default as MenubarBox} from './menu/menubar/MenubarBox';
-export {default as MenubarBoxLayout} from './menu/menubar/MenubarBoxLayout';
+export {default as MenuBarBox} from './menu/menubar/MenuBarBox';
+export {default as MenuBarBoxLayout} from './menu/menubar/MenuBarBoxLayout';
export {default as Calendar} from './calendar/Calendar';
export {default as CalendarAdapter} from './calendar/CalendarAdapter';
export {default as CalendarComponent} from './calendar/CalendarComponent';
diff --git a/eclipse-scout-core/src/index.less b/eclipse-scout-core/src/index.less
index b293201f68..e1f1ac2188 100644
--- a/eclipse-scout-core/src/index.less
+++ b/eclipse-scout-core/src/index.less
@@ -88,8 +88,9 @@
@import "image/Image";
@import "keystroke/keybox";
@import "layout/logicalgrid/LogicalGridLayout";
-@import "menu/ContextMenuPopup";
@import "menu/Menu";
+@import "menu/ContextMenuPopup";
+@import "menu/ComboMenu";
@import "menu/form/field/FormFieldMenu";
@import "menu/menubar/MenuBar";
@import "menu/menubar/MenuBarPopup";
diff --git a/eclipse-scout-core/src/main.less b/eclipse-scout-core/src/main.less
index 3304c1996c..1f5aaed33b 100644
--- a/eclipse-scout-core/src/main.less
+++ b/eclipse-scout-core/src/main.less
@@ -319,27 +319,23 @@ button {
.button {
position: relative;
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
color: @button-color;
background-color: @button-background-color;
border: 1px solid @button-border-color;
border-radius: @button-border-radius;
cursor: pointer;
- white-space: nowrap;
padding: @button-padding-y @button-padding-x;
- /* Note: no vertical align helper here! this is very dependent on the content of the button */
- /* (only text or other DOM nodes), therefore it has to be added manually when desired. */
- & > .button-label,
- & > .submenu-icon,
- & > .icon {
- vertical-align: middle;
- display: inline-block;
+ &.borderless {
+ background-color: transparent;
+ border: 0;
}
- & > .button-label {
- margin-top: @button-margin-top;
- overflow: hidden;
- text-overflow: ellipsis;
+ & > .text {
+ #scout.overflow-ellipsis-nowrap();
}
& > .icon {
@@ -362,13 +358,16 @@ button {
}
& > .font-icon {
- color: @button-font-icon-color;
font-size: @button-font-icon-size;
}
& > .submenu-icon {
#scout.submenu-icon();
padding-left: 8px;
+
+ .selected& {
+ #scout.submenu-icon-open();
+ }
}
& > .button-label,
@@ -380,39 +379,43 @@ button {
&:hover {
color: @button-hover-color;
+ background-color: @hover-background-color;
}
- &:focus {
- outline: none;
- overflow: visible; /* without this, IE would cut off the focus glow */
-
- &::before {
- #scout.button-focus();
- }
- }
-
- &:active,
- &.active {
+ &:active, &.active {
color: @button-active-color;
background-color: @button-active-background-color;
+ }
- & > .font-icon {
- color: @button-active-color;
+ &.selected {
+ color: @active-inverted-color;
+ background-color: @active-inverted-background-color;
+ border-color: @active-inverted-background-color;
+
+ &:hover {
+ background-color: @active-inverted-hover-background-color;
+ border-color: @active-inverted-hover-background-color;
+ }
+
+ &.active, &:active {
+ background-color: @active-inverted-active-background-color;
+ border-color: @active-inverted-active-background-color;
}
}
- &.default:not(.disabled) {
+ &:focus {
+ #scout.button-focus();
+ }
+
+ &.default {
background-color: @default-button-background-color;
/* border is necessary to align the text with text from buttons with a real border */
border-color: @default-button-background-color;
color: @default-button-color;
- &:focus {
- color: @default-button-color;
- }
-
&:hover {
background-color: @default-button-hover-background-color;
+ border-color: @default-button-hover-background-color;
}
&:active, &.active {
@@ -420,20 +423,21 @@ button {
border-color: @default-button-active-background-color;
}
- & > .font-icon {
- font-weight: normal;
- color: @icon-inverted-color;
+ &:focus {
+ #scout.button-focus();
}
}
- &:disabled {
+ &:disabled,
+ &.disabled {
background-color: @button-disabled-background-color;
border-color: @button-disabled-border-color;
color: @button-disabled-color;
cursor: default;
- & > .font-icon {
- color: @button-font-icon-disabled-color;
+ &:hover, &.active, &:active {
+ background-color: @button-disabled-background-color;
+ border-color: @button-disabled-border-color;
}
}
diff --git a/eclipse-scout-core/src/menu/ComboMenu.js b/eclipse-scout-core/src/menu/ComboMenu.js
index 93818a9605..5b57608c83 100644
--- a/eclipse-scout-core/src/menu/ComboMenu.js
+++ b/eclipse-scout-core/src/menu/ComboMenu.js
@@ -14,6 +14,15 @@ export default class ComboMenu extends Menu {
constructor() {
super();
+ this.childSelected = false;
+ this._childSelectedHandler = this._onChildSelected.bind(this);
+ }
+
+ _setChildActions(childActions) {
+ this.childActions.forEach(child => child.off('propertyChange:selected', this._childSelectedHandler));
+ super._setChildActions(childActions);
+ this.childActions.forEach(child => child.on('propertyChange:selected', this._childSelectedHandler));
+ this._updateChildSelected();
}
_render() {
@@ -23,6 +32,16 @@ export default class ComboMenu extends Menu {
}
this.$container.unfocusable();
this.htmlComp = HtmlComponent.install(this.$container, this.session);
+ }
+
+ _renderProperties() {
+ super._renderProperties();
+ this._renderChildSelected();
+ this._renderChildActions();
+ }
+
+ _renderChildActions() {
+ super._renderChildActions();
this.childActions.forEach(childAction => {
childAction.addCssClass('combo-menu-child');
@@ -34,4 +53,16 @@ export default class ComboMenu extends Menu {
_togglesSubMenu() {
return false;
}
+
+ _onChildSelected(event) {
+ this._updateChildSelected();
+ }
+
+ _updateChildSelected() {
+ this.setProperty('childSelected', this.childActions.some(child => child.selected));
+ }
+
+ _renderChildSelected() {
+ this.$container.toggleClass('child-selected', this.childSelected);
+ }
}
diff --git a/eclipse-scout-core/src/menu/ComboMenu.less b/eclipse-scout-core/src/menu/ComboMenu.less
new file mode 100644
index 0000000000..e612164783
--- /dev/null
+++ b/eclipse-scout-core/src/menu/ComboMenu.less
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 2010-2021 BSI Business Systems Integration AG.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * BSI Business Systems Integration AG - initial API and implementation
+ */
+
+.combo-menu {
+ padding: 0;
+ cursor: default;
+ align-items: stretch;
+
+ &:active, &.active, &:hover {
+ background-color: transparent;
+ }
+
+ & > .menu-item:not(:first-child) {
+ margin-left: 3px;
+
+ &::before {
+ content: '';
+ position: absolute;
+ left: -1px;
+ top: 7px;
+ height: calc(100% - 14px);
+ width: 1px;
+ background-color: @border-color;
+ }
+ }
+
+ &:not(.disabled).child-selected,
+ &:not(.disabled):hover {
+ & > .menu-item:not(:first-child)::before {
+ display: none;
+ }
+ }
+}
+
+.combo-menu.menu-button {
+ border: 0;
+
+ & > .menu-item {
+ border: 1px solid @button-border-color;
+
+ &:not(:last-child) {
+ border-top-right-radius: 0;
+ border-bottom-right-radius: 0;
+ border-right: 0;
+ }
+
+ &:not(:first-child) {
+ border-top-left-radius: 0;
+ border-bottom-left-radius: 0;
+ border-left: 0;
+ margin-left: 0;
+ }
+
+ .disabled& {
+ border-color: @button-disabled-color;
+ }
+ }
+
+ &.default {
+ & > .menu-item {
+ .button.default();
+
+ &.selected {
+ background-color: @default-button-active-background-color;
+ border-color: @default-button-active-background-color;
+ }
+ }
+ }
+}
+
+.context-menu {
+ & > .combo-menu {
+ padding: 0;
+
+ & > .menu-item {
+ color: @context-menu-item-color;
+ padding-top: @context-menu-item-padding-y;
+ padding-bottom: @context-menu-item-padding-y;
+
+ &:first-child {
+ margin-left: @context-menu-item-padding-left - @menu-item-padding-x;
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/eclipse-scout-core/src/menu/ContextMenuPopup.js b/eclipse-scout-core/src/menu/ContextMenuPopup.js
index 30599361fc..b886b17940 100644
--- a/eclipse-scout-core/src/menu/ContextMenuPopup.js
+++ b/eclipse-scout-core/src/menu/ContextMenuPopup.js
@@ -8,7 +8,7 @@
* Contributors:
* BSI Business Systems Integration AG - initial API and implementation
*/
-import {Action, arrays, ContextMenuPopupLayout, HtmlComponent, MenuDestinations, menuNavigationKeyStrokes, Popup, RowLayout, scrollbars} from '../index';
+import {Action, arrays, ContextMenuPopupLayout, HtmlComponent, MenuDestinations, menuNavigationKeyStrokes, Popup, RowLayout, Rectangle, graphics} from '../index';
import $ from 'jquery';
export default class ContextMenuPopup extends Popup {
@@ -20,8 +20,9 @@ export default class ContextMenuPopup extends Popup {
this.animateRemoval = true;
this.menuItems = [];
this.cloneMenuItems = true;
+ this.bodyAnimating = false;
this._toggleSubMenuQueue = [];
- this.repositionEnabled = true;
+ this.animationDuration = 300;
}
_init(options) {
@@ -65,6 +66,11 @@ export default class ContextMenuPopup extends Popup {
this._renderMenuItems();
}
+ _remove() {
+ this._toggleSubMenuQueue = [];
+ super._remove();
+ }
+
_renderBody() {
this.$body = this.$container.appendDiv('context-menu');
// Complete the layout hierarchy between the popup and the menu items
@@ -72,41 +78,33 @@ export default class ContextMenuPopup extends Popup {
htmlBody.setLayout(this._createBodyLayout());
}
- /**
- * @param [options]
- * @override
- */
_installScrollbars(options) {
super._installScrollbars({
axis: 'y'
});
}
- /**
- * @override
- */
- get$Scrollable() {
- return this.$body;
- }
-
- _bounds() {
- return this.htmlComp.bounds().subtractFromDimension(this.htmlComp.insets());
- }
-
- removeSubMenuItems(parentMenu, animated) {
+ _checkRemoveSubMenuItemsPossible(parentMenu, animated) {
if (!this.rendered && !this.rendering) {
- return;
+ return false;
}
let openingAnimationRunning = this.isOpeningAnimationRunning();
- if (this.bodyAnimating || openingAnimationRunning) {
+ let resizeAnimationRunning = this.htmlComp.layout.resizeAnimationRunning;
+ if (this.bodyAnimating || openingAnimationRunning || resizeAnimationRunning) {
// Let current animation finish and execute afterwards to prevent an unpredictable behavior and inconsistent state
this._toggleSubMenuQueue.push(this.removeSubMenuItems.bind(this, parentMenu, animated));
if (openingAnimationRunning) {
this.$container.oneAnimationEnd(() => this._processSubMenuQueue());
}
- return;
+ return false;
}
+ return true;
+ }
+ removeSubMenuItems(parentMenu, animated) {
+ if (!this._checkRemoveSubMenuItemsPossible(parentMenu, animated)) {
+ return false;
+ }
this.$body = parentMenu.__originalParent.$subMenuBody;
// move new body to back
this.$body.insertBefore(parentMenu.$subMenuBody);
@@ -115,75 +113,74 @@ export default class ContextMenuPopup extends Popup {
parentMenu.__originalParent._doActionTogglesSubMenu();
}
- let actualBounds = this._bounds();
+ let popupBounds = this.htmlComp.bounds();
- this.revalidateLayout();
+ this._adjustTextAlignment();
+ HtmlComponent.get(this.$body).invalidateLayoutTree();
+ this.validateLayoutTree();
this.position();
if (animated) {
- this.bodyAnimating = true;
- let duration = 300;
- let position = parentMenu.$placeHolder.position();
- parentMenu.$subMenuBody.css({
- width: 'auto',
- height: 'auto'
- });
- let targetBounds = this._bounds();
- parentMenu.$subMenuBody.css('box-shadow', 'none');
- this.htmlComp.setBounds(actualBounds);
- if (this.verticalAlignment !== Popup.Alignment.TOP) {
- // set container to element
- parentMenu.$subMenuBody.cssTop();
- }
-
- this._animateTopAndLeft(this.htmlComp.$comp, actualBounds, targetBounds, duration);
+ this._animateRemoveSubmenuItems(parentMenu, popupBounds);
+ }
+ }
- // move new body to top of popup
- parentMenu.$subMenuBody.cssHeightAnimated(actualBounds.height, parentMenu.$container.cssHeight(), {
- duration: duration,
+ _animateRemoveSubmenuItems(parentMenu, popupBounds) {
+ let parentMenuPosition = parentMenu.$placeHolder.position();
+ let popupInsets = this.htmlComp.insets();
+ let endPopupBounds = this.htmlComp.bounds();
+ let oldBodyBounds = HtmlComponent.get(parentMenu.$subMenuBody).bounds();
+ let bodyBounds = HtmlComponent.get(this.$body).bounds();
+ let startBodyBounds = new Rectangle(0, popupInsets.top, oldBodyBounds.width, oldBodyBounds.height);
+ let endBodyBounds = new Rectangle(0, popupInsets.top + parentMenuPosition.top, bodyBounds.width, parentMenu.$container.cssHeight());
+
+ this.bodyAnimating = true;
+ this.htmlComp.layout.disableAutoPosition();
+ this._animateResizePopup(this.htmlComp.$comp, popupBounds, endPopupBounds);
+ this._animateTextOffset(parentMenu.$subMenuBody, parentMenu.$subMenuBody.data('text-offset'));
+
+ // Collapse old body
+ parentMenu.$subMenuBody
+ .cssWidthAnimated(startBodyBounds.width, endBodyBounds.width, {
+ duration: this.animationDuration,
+ progress: this.revalidateLayout.bind(this),
+ complete: () => this._completeAnimateRemoveSubMenuItems(parentMenu),
+ queue: false
+ })
+ .cssHeightAnimated(startBodyBounds.height, endBodyBounds.height, {
+ duration: this.animationDuration,
+ queue: false
+ })
+ .cssTopAnimated(startBodyBounds.y, endBodyBounds.y, {
+ duration: this.animationDuration,
queue: false
});
- let endTopposition = position.top - this.$body.cssHeight(),
- startTopposition = 0 - actualBounds.height;
-
- parentMenu.$subMenuBody.cssTopAnimated(startTopposition, endTopposition, {
- duration: duration,
- queue: false,
- complete: () => {
- this.bodyAnimating = false;
- if (!this.rendered || !parentMenu.$container) {
- return;
- }
- scrollbars.uninstall(parentMenu.$subMenuBody, this.session);
- parentMenu.$placeHolder.replaceWith(parentMenu.$container);
- parentMenu.$container.toggleClass('expanded', false);
- this._updateFirstLastClass();
- this.updateNextToSelected('menu-item', parentMenu.$container);
-
- parentMenu.$subMenuBody.detach();
- this._installScrollbars();
- this.$body.css('box-shadow', '');
- // Do one final layout to fix any potentially wrong sizes (e.g. due to async image loading)
- this._invalidateLayoutTreeAndRepositionPopup();
- this._processSubMenuQueue();
- }
- });
-
- this.$body.cssWidthAnimated(actualBounds.width, targetBounds.width, {
- duration: duration,
- start: this.revalidateLayout.bind(this, true),
- progress: this.revalidateLayout.bind(this, false),
+ // Resize new body so that it doesn't increase the popup height and shows unnecessary scrollbars if new body will be bigger
+ // It also ensures correct text ellipsis during animation, position is already correct
+ this.$body
+ .cssWidthAnimated(oldBodyBounds.width, bodyBounds.width, {
+ duration: this.animationDuration,
+ queue: false
+ })
+ .cssHeightAnimated(oldBodyBounds.height, bodyBounds.height, {
+ duration: this.animationDuration,
queue: false
});
+ }
- if (targetBounds.height !== actualBounds.height) {
- this.$body.cssHeightAnimated(actualBounds.height, targetBounds.height, {
- duration: duration,
- queue: false
- });
- }
+ _completeAnimateRemoveSubMenuItems(parentMenu) {
+ this.bodyAnimating = false;
+ if (!this.rendered || !parentMenu.$container) {
+ return;
}
+ parentMenu.$placeHolder.replaceWith(parentMenu.$container);
+ parentMenu.$container.toggleClass('expanded', false);
+ this._updateFirstLastClass();
+ this.updateNextToSelected('menu-item', parentMenu.$container);
+
+ parentMenu.$subMenuBody.detach();
+ this._processSubMenuQueue();
}
_processSubMenuQueue() {
@@ -193,33 +190,40 @@ export default class ContextMenuPopup extends Popup {
}
}
- renderSubMenuItems(parentMenu, menus, animated, initialSubMenuRendering) {
+ _checkRenderSubMenuItemsPossible(parentMenu, menus, animated, initialSubMenuRendering) {
if (!this.session.desktop.rendered && !initialSubMenuRendering) {
this.initialSubMenusToRender = {
parentMenu: parentMenu,
menus: menus
};
- return;
+ return false;
}
if (!this.rendered && !this.rendering) {
- return;
+ return false;
}
let openingAnimationRunning = this.isOpeningAnimationRunning();
- if (this.bodyAnimating || openingAnimationRunning) {
+ let resizeAnimationRunning = this.htmlComp.layout.resizeAnimationRunning;
+ if (this.bodyAnimating || openingAnimationRunning || resizeAnimationRunning) {
// Let current animation finish and execute afterwards to prevent an unpredictable behavior and inconsistent state
this._toggleSubMenuQueue.push(this.renderSubMenuItems.bind(this, parentMenu, menus, animated, initialSubMenuRendering));
if (openingAnimationRunning) {
this.$container.oneAnimationEnd(() => this._processSubMenuQueue());
}
- return;
+ return false;
}
+ return true;
+ }
- let actualBounds = this._bounds();
-
- parentMenu.__originalParent.$subMenuBody = this.$body;
+ renderSubMenuItems(parentMenu, menus, animated, initialSubMenuRendering) {
+ if (!this._checkRenderSubMenuItemsPossible(parentMenu, menus, animated, initialSubMenuRendering)) {
+ return false;
+ }
- let $all = this.$body.find('.' + 'menu-item');
- $all.removeClass('next-to-selected');
+ let popupBounds = this.htmlComp.bounds();
+ let $oldBody = this.$body;
+ parentMenu.__originalParent.$subMenuBody = $oldBody;
+ let $menuItems = this.$body.find('.menu-item');
+ $menuItems.removeClass('next-to-selected');
if (!parentMenu.$subMenuBody) {
this._renderBody();
@@ -230,117 +234,114 @@ export default class ContextMenuPopup extends Popup {
this.$body = parentMenu.$subMenuBody;
}
let $insertAfterElement = parentMenu.$container.prev();
- let position = parentMenu.$container.position();
+ let parentMenuPosition = parentMenu.$container.position();
parentMenu.$placeHolder = parentMenu.$container.clone();
// HtmlComponent is necessary for the row layout (it would normally be installed by Menu.js, but $placeholder is just a jquery clone of parentMenu.$container and is not managed by a real widget)
HtmlComponent.install(parentMenu.$placeHolder, this.session);
if ($insertAfterElement.length) {
parentMenu.$placeHolder.insertAfter($insertAfterElement);
} else {
- parentMenu.__originalParent.$subMenuBody.prepend(parentMenu.$placeHolder);
+ $oldBody.prepend(parentMenu.$placeHolder);
}
- this.$body.insertAfter(parentMenu.__originalParent.$subMenuBody);
+ this.$body.insertAfter($oldBody);
this.$body.prepend(parentMenu.$container);
parentMenu.$container.toggleClass('expanded');
+ this._adjustTextAlignment();
- this.revalidateLayout();
+ HtmlComponent.get(this.$body).invalidateLayoutTree();
+ this.validateLayoutTree();
this.position();
-
this.updateNextToSelected();
if (animated) {
- this.bodyAnimating = true;
- let duration = 300;
- parentMenu.__originalParent.$subMenuBody.css({
- width: 'auto',
- height: 'auto'
- });
- let targetBounds = this._bounds();
-
- this._animateTopAndLeft(this.htmlComp.$comp, actualBounds, targetBounds, duration);
+ this._animateRenderSubMenuItems(parentMenu, popupBounds, parentMenuPosition);
+ } else {
+ $oldBody.detach();
+ this._updateFirstLastClass();
+ }
+ }
- this.$body.css('box-shadow', 'none');
- // set container to element
- this.$body.cssWidthAnimated(actualBounds.width, targetBounds.width, {
- duration: duration,
- start: this.revalidateLayout.bind(this, true),
- progress: this.revalidateLayout.bind(this, false),
+ _animateRenderSubMenuItems(parentMenu, popupBounds, parentMenuPosition) {
+ let $oldBody = parentMenu.__originalParent.$subMenuBody;
+ let endPopupBounds = this.htmlComp.bounds();
+ let popupInsets = this.htmlComp.insets();
+ let oldBodyBounds = graphics.bounds($oldBody);
+ let bodyBounds = HtmlComponent.get(this.$body).bounds();
+ let startBodyBounds = new Rectangle(0, popupInsets.top + parentMenuPosition.top, oldBodyBounds.width, parentMenu.$container.cssHeight());
+ let endBodyBounds = new Rectangle(0, popupInsets.top, bodyBounds.width, bodyBounds.height);
+
+ this.bodyAnimating = true;
+ this.htmlComp.layout.disableAutoPosition();
+ this._animateResizePopup(this.htmlComp.$comp, popupBounds, endPopupBounds);
+ this._animateTextOffset(this.$body, $oldBody.data('text-offset'));
+
+ // Expand new body
+ this.$body
+ .cssWidthAnimated(startBodyBounds.width, endBodyBounds.width, {
+ duration: this.animationDuration,
+ progress: this.revalidateLayout.bind(this),
+ complete: () => this._completeAnimateRenderSubMenuItems($oldBody),
queue: false
- });
-
- this.$body.cssHeightAnimated(parentMenu.$container.cssHeight(), targetBounds.height, {
- duration: duration,
+ })
+ .cssHeightAnimated(startBodyBounds.height, endBodyBounds.height, {
+ duration: this.animationDuration,
+ queue: false
+ })
+ .cssTopAnimated(startBodyBounds.y, endBodyBounds.y, {
+ duration: this.animationDuration,
queue: false
});
- let endTopposition = 0 - targetBounds.height,
- startTopposition = position.top - parentMenu.__originalParent.$subMenuBody.cssHeight(),
- topMargin = 0;
-
- // move new body to top of popup.
- this.$body.cssTopAnimated(startTopposition, endTopposition, {
- duration: duration,
- queue: false,
- complete: () => {
- this.bodyAnimating = false;
- if (!this.rendered) {
- return;
- }
- if (parentMenu.__originalParent.$subMenuBody) {
- scrollbars.uninstall(parentMenu.__originalParent.$subMenuBody, this.session);
- parentMenu.__originalParent.$subMenuBody.detach();
- this.$body.cssTop(topMargin);
- this._installScrollbars();
- this._updateFirstLastClass();
- this.$body.css('box-shadow', '');
- }
- // Do one final layout to fix any potentially wrong sizes (e.g. due to async image loading)
- this._invalidateLayoutTreeAndRepositionPopup();
- this._processSubMenuQueue();
- }
+ // Resize old body so that it doesn't increase the popup height and shows unnecessary scrollbars if new body will be smaller
+ // It also ensures correct text ellipsis during animation, position is already correct
+ $oldBody
+ .cssWidthAnimated(oldBodyBounds.width, endBodyBounds.width, {
+ duration: this.animationDuration,
+ queue: false
+ })
+ .cssHeightAnimated(oldBodyBounds.height, endBodyBounds.height, {
+ duration: this.animationDuration,
+ queue: false
});
+ }
- if (actualBounds.height !== targetBounds.height) {
- parentMenu.__originalParent.$subMenuBody.cssHeightAnimated(actualBounds.height, targetBounds.height, {
- duration: duration,
- queue: false
- });
- this.$container.cssHeight(actualBounds.height, targetBounds.height, {
- duration: duration,
- queue: false
- });
- }
- if (this.verticalAlignment === Popup.Alignment.TOP) {
- this.$container.cssTopAnimated(actualBounds.y, targetBounds.y, {
- duration: duration,
- queue: false
- }).css('overflow', 'visible');
- }
- } else {
- if (!initialSubMenuRendering) {
- scrollbars.uninstall(parentMenu.__originalParent.$subMenuBody, this.session);
- }
- parentMenu.__originalParent.$subMenuBody.detach();
- this._installScrollbars();
+ _completeAnimateRenderSubMenuItems($oldBody) {
+ this.bodyAnimating = false;
+ if (!this.rendered) {
+ return;
+ }
+ if ($oldBody) {
+ $oldBody.detach();
+ this.$body.cssTop('');
this._updateFirstLastClass();
}
+ this.htmlComp.layout.resetAutoPosition();
+ this._processSubMenuQueue();
}
- _animateTopAndLeft($comp, actualBounds, targetBounds, duration) {
+ _animateResizePopup($comp, popupBounds, targetBounds) {
let options = {
- duration: duration,
+ duration: this.animationDuration,
queue: false
};
$comp
- .cssTopAnimated(actualBounds.y, targetBounds.y, options)
- .cssLeftAnimated(actualBounds.x, targetBounds.x, options);
+ .cssTopAnimated(popupBounds.y, targetBounds.y, options)
+ .cssLeftAnimated(popupBounds.x, targetBounds.x, options)
+ .cssWidthAnimated(popupBounds.width, targetBounds.width, options)
+ .cssHeightAnimated(popupBounds.height, targetBounds.height, options);
}
- revalidateLayout(repositionEnabled) {
- this.repositionEnabled = scout.nvl(repositionEnabled, true);
- super.revalidateLayout();
- this.repositionEnabled = true;
+ _animateTextOffset($body, textOffset, targetOffset) {
+ targetOffset = scout.nvl(targetOffset, this.$body.data('text-offset'));
+ let $menuItems = this.$visibleMenuItems($body);
+ $menuItems.each((index, menuItem) => {
+ let $menuItem = $(menuItem);
+ let $text = $menuItem.children('.text');
+ let padding = this._calcTextPaddingLeft($menuItem, textOffset);
+ let targetPadding = this._calcTextPaddingLeft($menuItem, targetOffset);
+ $text.cssAnimated({paddingLeft: padding}, {paddingLeft: targetPadding}, {duration: this.animationDuration});
+ });
}
_renderMenuItems(menus, initialSubMenuRendering) {
@@ -382,19 +383,13 @@ export default class ContextMenuPopup extends Popup {
menu.__originalParent = originalParent;
}
menu.render(this.$body);
+ menu.$container.removeClass('menu-button');
this._attachMenuListeners(menu);
-
- // Invalidate popup layout after images icons have been loaded, because the
- // correct size might not be known yet. If the layout would not be revalidated, the popup
- // size will be wrong (text is cut off after image has been loaded).
- // The menu item actually does it by itself, but the popup needs to be repositioned too.
- if (menu.icon) {
- menu.icon.on('load error', this._invalidateLayoutTreeAndRepositionPopup.bind(this));
- }
}, this);
this._handleInitialSubMenus(initialSubMenuRendering);
this._updateFirstLastClass();
+ this._adjustTextAlignment();
}
_attachCloneMenuListeners(menu) {
@@ -456,8 +451,9 @@ export default class ContextMenuPopup extends Popup {
return this.$body.children('.menu-item');
}
- $visibleMenuItems() {
- return this.$body.children('.menu-item:visible');
+ $visibleMenuItems($body) {
+ $body = $body || this.$body;
+ return $body.children('.menu-item:visible');
}
/**
@@ -469,7 +465,7 @@ export default class ContextMenuPopup extends Popup {
this.$body.children('.menu-item').each(function() {
let $menuItem = $(this);
- $menuItem.removeClass('context-menu-item-first context-menu-item-last');
+ $menuItem.removeClass('first last');
if ($menuItem.isVisible()) {
if (!$firstMenuItem) {
@@ -479,10 +475,10 @@ export default class ContextMenuPopup extends Popup {
}
});
if ($firstMenuItem) {
- $firstMenuItem.addClass('context-menu-item-first');
+ $firstMenuItem.addClass('first');
}
if ($lastMenuItem) {
- $lastMenuItem.addClass('context-menu-item-last');
+ $lastMenuItem.addClass('last');
}
}
@@ -530,13 +526,52 @@ export default class ContextMenuPopup extends Popup {
}, this);
}
- _invalidateLayoutTreeAndRepositionPopup() {
- this.invalidateLayoutTree();
- this.session.layoutValidator.schedulePostValidateFunction(() => {
- if (!this.rendered) { // check needed because this is an async callback
- return;
+ _adjustTextAlignment($body) {
+ $body = $body || this.$body;
+ let $menuItems = this.$visibleMenuItems($body);
+ let textOffset = this._calcTextOffset($menuItems);
+ $body.data('text-offset', textOffset);
+ this._updateTextOffset(textOffset, $menuItems);
+ }
+
+ _calcTextOffset($menuItems) {
+ let textOffset = 0;
+ $menuItems = $menuItems || this.$visibleMenuItems();
+ $menuItems.each((index, menuItem) => {
+ let $menuItem = $(menuItem);
+ let $icon = $menuItem.children('.icon');
+ let iconWidth = 0;
+
+ if ($icon.length > 0) {
+ iconWidth = $icon.outerWidth(true);
+ }
+ textOffset = Math.max(textOffset, iconWidth);
+ });
+ return textOffset;
+ }
+
+ _updateTextOffset(textOffset, $menuItems) {
+ // Update the padding of each text such that the sum of icon width and the padding
+ // are the same for all items. This ensures that the texts are all aligned.
+ $menuItems = $menuItems || this.$visibleMenuItems();
+ $menuItems.each((index, menuItem) => {
+ let $menuItem = $(menuItem);
+ let $text = $menuItem.children('.text');
+ $text.css('padding-left', this._calcTextPaddingLeft($menuItem, textOffset));
+ let htmlComp = HtmlComponent.optGet($menuItem);
+ if (htmlComp) {
+ htmlComp.invalidateLayout();
}
- this.position();
});
}
+
+ _calcTextPaddingLeft($menuItem, textOffset) {
+ let $icon = $menuItem.children('.icon');
+ let iconWidth = 0;
+
+ if ($icon.length > 0) {
+ iconWidth = $icon.outerWidth(true);
+ }
+ return textOffset - iconWidth;
+ }
}
diff --git a/eclipse-scout-core/src/menu/ContextMenuPopup.less b/eclipse-scout-core/src/menu/ContextMenuPopup.less
index e51c35093e..7f4044408f 100644
--- a/eclipse-scout-core/src/menu/ContextMenuPopup.less
+++ b/eclipse-scout-core/src/menu/ContextMenuPopup.less
@@ -10,6 +10,10 @@
*/
.context-menu-popup {
+ background-color: @popup-background-color;
+ border-radius: @border-radius-medium;
+ overflow: hidden;
+ #scout.drop-shadow-large();
&.animate-open {
.popup-animate-open();
@@ -21,16 +25,20 @@
}
.context-menu {
- position: relative;
- border: 1px solid @popup-border-color;
+ position: absolute;
background-color: @popup-background-color;
- #scout.drop-shadow-large();
+ margin: 6px 0;
& > .menu-item {
- border-top: solid 1px @border-color;
- display: block;
- width: 100%;
- padding: 11px 15px;
+ display: flex;
+ padding: @context-menu-item-padding-y 24px @context-menu-item-padding-y @context-menu-item-padding-left;
+ color: @context-menu-item-color;
+ border-radius: 0;
+ font-weight: normal;
+
+ & > .font-icon {
+ color: @context-menu-item-icon-color;
+ }
& > .text {
#scout.overflow-ellipsis();
@@ -39,43 +47,36 @@
}
&.menu-textandicon > .icon {
- margin-right: 10px;
+ margin-right: 14px;
}
&.selected {
- border-top: 1px solid @item-selection-border-color;
- border-color: @item-selection-border-color;
background-color: @item-selection-background-color;
- }
- &.next-to-selected {
- border-top-color: @item-selection-border-color;
+ & > .submenu-icon {
+ transform: none;
+ }
}
- &.context-menu-item-first {
- border-top: 1px solid transparent;
- }
+ &.expanded {
+ color: @active-color;
- &.context-menu-item-last {
- border-bottom: 1px solid transparent;
+ & > .submenu-icon {
+ #scout.submenu-icon-open();
+ }
}
- &.selected.context-menu-item-first {
- border-top: 1px solid @item-selection-border-color;
- }
-
- &.selected.context-menu-item-last {
- border-color: @item-selection-border-color;
- }
+ &.disabled {
+ color: @menu-item-disabled-color;
- &.expanded > .submenu-icon {
- #scout.transform(rotateX(180deg));
- top: 0;
+ & > .font-icon {
+ color: @menu-item-disabled-color;
+ }
}
& > .key-box {
bottom: auto;
- top: 7px;
+ top: 5px;
}
}
}
diff --git a/eclipse-scout-core/src/menu/ContextMenuPopupLayout.js b/eclipse-scout-core/src/menu/ContextMenuPopupLayout.js
index b4ec7edf29..c08d79e53e 100644
--- a/eclipse-scout-core/src/menu/ContextMenuPopupLayout.js
+++ b/eclipse-scout-core/src/menu/ContextMenuPopupLayout.js
@@ -8,8 +8,7 @@
* Contributors:
* BSI Business Systems Integration AG - initial API and implementation
*/
-import {graphics, HtmlComponent, PopupLayout} from '../index';
-import $ from 'jquery';
+import {graphics, HtmlComponent, PopupLayout, scrollbars} from '../index';
export default class ContextMenuPopupLayout extends PopupLayout {
@@ -17,24 +16,37 @@ export default class ContextMenuPopupLayout extends PopupLayout {
super(popup);
}
- layout($container) {
- let $menuItems = this.popup.$visibleMenuItems();
- this._adjustTextAlignment($menuItems);
- this._resetMaxWidthFor($menuItems);
- super.layout($container);
- this._setMaxWidthFor($menuItems);
- }
-
_setSize(prefSize) {
- super._setSize(prefSize);
-
if (this.popup.bodyAnimating) {
+ scrollbars.update(this.popup.get$Scrollable());
return;
}
- let htmlPopup = this.popup.htmlComp;
+ super._setSize(prefSize);
+ scrollbars.update(this.popup.get$Scrollable());
let htmlBody = HtmlComponent.get(this.popup.$body);
- let bodySize = prefSize.subtract(htmlPopup.insets());
- htmlBody.setSize(bodySize.subtract(htmlBody.margins()));
+ htmlBody.pack();
+ }
+
+ invalidate(htmlSource) {
+ // If a child triggers a layout invalidation, the popup needs to be resized.
+ // This will happen for sure if a child is an image which will be loaded during the animation.
+ // Ideally, the running animations would be stopped, the popup layouted, the animations adjusted to the new bounds and then continued.
+ // This is currently too complicated to implement, so instead we let the animations finish and resize the popup at the end (but before other resize animations start).
+ if (this.popup.bodyAnimating && htmlSource && htmlSource.isDescendantOf(this.popup.htmlComp)) {
+ this.popup._toggleSubMenuQueue.unshift(() => {
+ if (!this.popup.rendered) {
+ return;
+ }
+ let oldOffset = this.popup.$body.data('text-offset');
+ this.popup._adjustTextAlignment();
+ this.popup.animateResize = true;
+ this.resizeAnimationDuration = this.popup.animationDuration;
+ this.popup.revalidateLayoutTree();
+ this.popup.animateResize = false;
+ this.popup._animateTextOffset(this.popup.$body, oldOffset);
+ this.popup.$container.promise().done(() => this.popup._processSubMenuQueue());
+ });
+ }
}
preferredLayoutSize($container, options) {
@@ -47,7 +59,7 @@ export default class ContextMenuPopupLayout extends PopupLayout {
let popupStyleBackup = this.popup.$container.attr('style');
let $siblingBodies = this.popup.$body.siblings('.context-menu');
$siblingBodies.addClass('hidden');
- this.popup.$container.css({
+ this.popup.$body.css({
width: 'auto',
height: 'auto'
});
@@ -62,92 +74,4 @@ export default class ContextMenuPopupLayout extends PopupLayout {
.add(htmlComp.insets())
.add(htmlBody.margins());
}
-
- _adjustTextAlignment($menuItems) {
- // Calculate the text offset (= max icon width)
- let textOffset = 0;
- $menuItems.each((index, menuItem) => {
- let $menuItem = $(menuItem);
- let $icon = $menuItem.children('.icon');
- let iconWidth = 0;
-
- if ($icon.length > 0) {
- iconWidth = $icon.outerWidth(true);
- }
- textOffset = Math.max(textOffset, iconWidth);
- });
-
- // Update the padding of each text such that the sum of icon width and the padding
- // are the same for all items. This ensures that the texts are all aligned.
- $menuItems.each((index, menuItem) => {
- let $menuItem = $(menuItem);
- let $icon = $menuItem.children('.icon');
- let $text = $menuItem.children('.text');
- let iconWidth = 0;
-
- if ($icon.length > 0) {
- iconWidth = $icon.outerWidth(true);
- }
- $text.css('padding-left', textOffset - iconWidth);
- let htmlComp = HtmlComponent.optGet($menuItem);
- if (htmlComp) {
- htmlComp.invalidateLayout();
- }
- });
- }
-
- _resetMaxWidthFor($menuItems) {
- $menuItems.each((pos, item) => {
- let $menu = $(item),
- menu = $menu.data('widget');
-
- if (!menu) {
- // After closing a submenu the link to the widget gets lost
- return;
- }
-
- if (menu.$text) {
- menu.$text.css('max-width', '');
- }
- });
- }
-
- _setMaxWidthFor($menuItems) {
- $menuItems.each((pos, item) => {
- let $menu = $(item),
- menu = $menu.data('widget');
-
- if (!menu) {
- // After closing a submenu the link to the widget gets lost
- return;
- }
-
- if (menu.$text) {
- // Submenu icon is on the right side of the text.
- // If there is not enough space to show the whole menu item (icon, text and submenu icon), the text is truncated.
- // Icon and submenu icon are always shown.
- let textMaxWidth = this._calcTextMaxWidth(menu);
- menu.$text.cssPxValue('max-width', textMaxWidth);
- }
- });
- }
-
- _calcTextMaxWidth(menu) {
- let containerWidth = menu.$container.width(),
- $icon = menu.get$Icon(),
- $text = menu.$text,
- $submenuIcon = menu.$submenuIcon,
- textWidth = containerWidth + 1; // add 1px to make it work even if containerWidth is a float
-
- if ($text && $text.isVisible()) {
- textWidth -= $text.cssMarginX();
- }
- if ($icon && $icon.isVisible()) {
- textWidth -= $icon.outerWidth(true);
- }
- if ($submenuIcon && $submenuIcon.isVisible()) {
- textWidth -= $submenuIcon.outerWidth(true);
- }
- return textWidth;
- }
}
diff --git a/eclipse-scout-core/src/menu/Menu.js b/eclipse-scout-core/src/menu/Menu.js
index 519f5e4bd9..2fbfe82fe7 100644
--- a/eclipse-scout-core/src/menu/Menu.js
+++ b/eclipse-scout-core/src/menu/Menu.js
@@ -40,7 +40,7 @@ export default class Menu extends Action {
this.menuFilter = null;
this.$submenuIcon = null;
this.$subMenuBody = null;
- this._addCloneProperties(['defaultMenu', 'menuTypes', 'overflow', 'stackable', 'separator', 'shrinkable', 'parentMenu', 'menuFilter']);
+ this._addCloneProperties(['defaultMenu', 'menuTypes', 'overflow', 'stackable', 'separator', 'shrinkable', 'parentMenu', 'menuFilter', 'subMenuVisibility']);
this._addWidgetProperties('childActions');
}
@@ -158,6 +158,7 @@ export default class Menu extends Action {
this._closeSubMenues();
}
}
+ this.$container.toggleClass('has-popup', this._doActionTogglesSubMenu() || this._doActionTogglesPopup());
}
_closeSubMenues() {
@@ -253,18 +254,11 @@ export default class Menu extends Action {
return this.childActions.length > 0;
}
- /**
- * Only render child actions if the sub-menu popup is open.
- */
_renderChildActions() {
- if (objects.optProperty(this.popup, 'rendered')) {
- let $popup = this.popup.$container;
- this.childActions.forEach(menu => {
- menu.render($popup);
- });
+ // Child action in a sub menu cannot be replaced dynamically, popup has to be closed first.
+ if (!this.rendering) {
+ this._renderSubMenuIcon();
}
-
- this._renderSubMenuIcon();
}
setSubMenuVisibility(subMenuVisibility) {
@@ -311,16 +305,16 @@ export default class Menu extends Action {
this.invalidateLayoutTree();
}
}
- if (this.rendered) {
+ if (!this.rendering) {
this._renderTextPosition();
+ this._updateIconAndTextStyle();
}
}
_renderText(text) {
super._renderText(text);
this.$container.toggleClass('has-text', strings.hasText(this.text) && this.textVisible);
- if (this.rendered) {
- this._updateIconAndTextStyle();
+ if (!this.rendering) {
this._renderSubMenuIcon();
}
this.invalidateLayoutTree();
@@ -342,8 +336,7 @@ export default class Menu extends Action {
_renderIconId() {
super._renderIconId();
this.$container.toggleClass('has-icon', !!this.iconId);
- if (this.rendered) {
- this._updateIconAndTextStyle();
+ if (!this.rendering) {
this._renderSubMenuIcon();
}
this.invalidateLayoutTree();
@@ -462,8 +455,11 @@ export default class Menu extends Action {
_updateIconAndTextStyle() {
let hasText = this._hasText();
let hasTextAndIcon = !!(hasText && this.iconId);
+ let hasIcon = !!this.iconId;
+ let hasSubMenuIcon = !!this.$submenuIcon;
+ let hasOneIcon = (hasIcon && !hasSubMenuIcon) || (!hasIcon && hasSubMenuIcon);
this.$container.toggleClass('menu-textandicon', hasTextAndIcon);
- this.$container.toggleClass('menu-icononly', !hasText);
+ this.$container.toggleClass('menu-icononly', !hasText && hasOneIcon);
}
_closePopup() {
@@ -679,7 +675,7 @@ export default class Menu extends Action {
}
_renderMenuStyle() {
- this.$container.toggleClass('default-menu', this.menuStyle === Menu.MenuStyle.DEFAULT);
+ this.$container.toggleClass('default', this.menuStyle === Menu.MenuStyle.DEFAULT);
}
setDefaultMenu(defaultMenu) {
diff --git a/eclipse-scout-core/src/menu/Menu.less b/eclipse-scout-core/src/menu/Menu.less
index e62c70e4a7..b154e757d7 100644
--- a/eclipse-scout-core/src/menu/Menu.less
+++ b/eclipse-scout-core/src/menu/Menu.less
@@ -10,12 +10,17 @@
*/
.menu-item {
position: relative;
- display: inline-block;
- vertical-align: middle;
- white-space: nowrap;
- color: @text-color;
+ display: inline-flex;
+ align-items: center;
+ color: @menu-item-color;
cursor: pointer;
- #scout.vertical-align-helper-before();
+ padding: @menu-item-padding-y @menu-item-padding-x;
+ border-radius: @border-radius;
+
+ &.menu-icononly {
+ padding-left: @menubar-item-icononly-padding-x;
+ padding-right: @menubar-item-icononly-padding-x;
+ }
& > .key-box {
bottom: -3px;
@@ -23,49 +28,57 @@
&:hover {
color: @hover-color;
+ background-color: @hover-background-color;
}
&:active, &.active {
- color: @active-color;
+ background-color: @active-background-color;
+ }
+
+ &.selected {
+ color: @active-inverted-color;
+ background-color: @active-inverted-background-color;
+
+ &:hover {
+ background-color: @active-inverted-hover-background-color;
+ }
+
+ &.active, &:active {
+ background-color: @active-inverted-active-background-color;
+ }
+
+ &.has-popup {
+ color: @menu-item-color;
+ background-color: @selected-background-color;
+ }
+ }
+
+ #scout.box-shadow-focus-transition();
+
+ &:focus {
+ #scout.box-shadow-focus-inset();
}
&.disabled {
color: @menu-item-disabled-color;
+ background-color: transparent;
cursor: default;
+
+ &:hover, &.active, &:active {
+ background-color: transparent;
+ }
}
&.menu-textandicon > .icon {
margin-right: 8px;
}
- & > .icon,
- & > .text,
- & > .submenu-icon,
- & > .text > .submenu-icon {
- position: relative;
- vertical-align: middle;
- display: inline-block;
- line-height: normal;
- height: auto;
-
- /* Make IE trigger the :active state if the text or icon is pressed */
- pointer-events: none;
- }
-
& > .text {
- /* Icon is bigger than the text -> increase text size to make menu items with and without font icons about equal height. */
- /* Otherwise menu items in context menus have a different size, and the popup of a menu with submenus may not be positioned correctly. */
- padding-top: @menu-item-text-padding-top;
- padding-bottom: 1px;
- }
-
- &:not(.menu-button) > .icon {
- /* Move "a bit" towards the top, because the icon would not look centered with the text's baseline otherwise. */
- top: -1px;
+ #scout.overflow-ellipsis-nowrap();
}
& > .font-icon {
- font-size: 18px;
+ font-size: 16px;
}
& > .image-icon {
@@ -92,31 +105,18 @@
& > .text > .submenu-icon {
#scout.submenu-icon();
padding-left: 8px;
- top: -1px;
- }
-
- & > .font-icon {
- font-size: 18px;
- }
-
- &.menu-open {
- color: @active-color;
- }
-
- &.combo-menu-child.menu-icononly:not(:first-child) {
- margin-left: 8px;
- & > .submenu-icon {
+ .menu-icononly& {
padding-left: 0;
}
+
+ .selected& {
+ #scout.submenu-icon-open();
+ }
}
&.bottom-text.menu-textandicon {
- text-align: center;
-
- &::before {
- display: none;
- }
+ flex-direction: column;
& > .icon {
display: block;
@@ -124,22 +124,21 @@
padding-bottom: 3px;
}
- &:not(.menu-button) > .icon {
- top: 0; // reset
- }
-
& > .text {
- display: block;
+ display: flex;
+ align-items: center;
font-size: @font-size-extra-small;
- padding-bottom: 0; // reset
& > .submenu-icon {
font-size: 12px;
padding-left: 4px;
- top: 0; // reset
// Make sure the icon does not increase the height and therefore the whole menu
line-height: 0;
}
}
}
}
+
+.menu-button {
+ .button();
+}
diff --git a/eclipse-scout-core/src/menu/form/field/FormFieldMenu.less b/eclipse-scout-core/src/menu/form/field/FormFieldMenu.less
index 627494d707..1b1c0b1422 100644
--- a/eclipse-scout-core/src/menu/form/field/FormFieldMenu.less
+++ b/eclipse-scout-core/src/menu/form/field/FormFieldMenu.less
@@ -9,10 +9,19 @@
* BSI Business Systems Integration AG - initial API and implementation
*/
.form-field-menu {
+ // Remove y padding but keep x padding to have the same gap between form field menus and regular menus.
+ padding-top: 0;
+ padding-bottom: 0;
cursor: default;
+ color: @text-color;
&:hover {
color: @text-color;
+ background-color: transparent;
+ }
+
+ &:active, &.active {
+ background-color: transparent;
}
&:not(.has-text):not(.has-icon) {
@@ -26,8 +35,6 @@
position: relative;
vertical-align: middle;
- /* remove margin left since the menu-item already has a padding */
-
& > label {
margin-left: 8px;
}
@@ -38,6 +45,12 @@
}
}
+ &:not(.has-icon) > .form-field:not(.no-mandatory-indicator) {
+ // Remove margin left since the menu-item already has a padding
+ // This reduces the gap between form field menus in the menu bar, but also improves the alignment in the context menu
+ margin-left: -@mandatory-indicator-width;
+ }
+
& > .check-box-field.disabled:not(.read-only) > .field > .label,
& > .radiobutton-group > .radiobutton-group-body > .radio-button.disabled:not(.read-only) > .field > .label,
& > .radio-button.disabled:not(.read-only) > .field > .label {
@@ -46,11 +59,30 @@
}
}
-/* Inside context menu popup -> set preferred size */
-.context-menu > .menu-item.form-field-menu {
- display: flex;
- align-items: center;
+.menubar-box > .form-field-menu {
+ & > .string-field,
+ & > .smart-field,
+ & > .number-field,
+ & > .date-field,
+ & > .tag-field,
+ & > .file-chooser-field {
+ margin-top: @menubar-field-menu-margin-y - @menubar-item-margin-y;
+ margin-bottom: @menubar-field-menu-margin-y - @menubar-item-margin-y;
+ }
+}
+
+:not(.main-menubar):not(.bounded) > .menubar-box > .form-field-menu {
+ &.first {
+ padding-left: 0;
+ }
+ &.last {
+ padding-right: 0;
+ }
+}
+
+/* Inside context menu popup -> set preferred size */
+.context-menu > .form-field-menu {
& > .text,
& > .icon {
flex: none;
diff --git a/eclipse-scout-core/src/menu/menubar/MenuBar.js b/eclipse-scout-core/src/menu/menubar/MenuBar.js
index 3072d7b5a7..a90a646e80 100644
--- a/eclipse-scout-core/src/menu/menubar/MenuBar.js
+++ b/eclipse-scout-core/src/menu/menubar/MenuBar.js
@@ -70,12 +70,12 @@ export default class MenuBar extends Widget {
this.menuFilter = (menus, destination, onlyVisible, enableDisableKeyStroke) => options.menuFilter(menus, MenuDestinations.MENU_BAR, onlyVisible, enableDisableKeyStroke);
}
- this.menuboxLeft = scout.create('MenubarBox', {
+ this.menuboxLeft = scout.create('MenuBarBox', {
parent: this,
cssClass: 'left',
tooltipPosition: this._oppositePosition()
});
- this.menuboxRight = scout.create('MenubarBox', {
+ this.menuboxRight = scout.create('MenuBarBox', {
parent: this,
cssClass: 'right',
tooltipPosition: this._oppositePosition()
diff --git a/eclipse-scout-core/src/menu/menubar/MenuBar.less b/eclipse-scout-core/src/menu/menubar/MenuBar.less
index c17694f2ea..a27c56f6d8 100644
--- a/eclipse-scout-core/src/menu/menubar/MenuBar.less
+++ b/eclipse-scout-core/src/menu/menubar/MenuBar.less
@@ -13,66 +13,23 @@
#scout {
.main-menubar-background-color-standard() {
background-color: @main-menubar-background-color;
-
- & > .menu-button.disabled {
- background-color: @menubar-button-disabled-background-color;
- }
}
.menubar-background-color-inherit() {
background-color: inherit;
-
- & > .menu-button.disabled {
- /* use regular button color since menubar has no color */
- background-color: @button-disabled-background-color;
- }
}
/* standard main-menubar */
.main-menubar-standard() {
#scout.main-menubar-background-color-standard();
- padding-left: @bench-padding-x;
- padding-right: @bench-padding-x;
min-height: @main-menubar-height;
margin-left: inherit;
margin-right: inherit;
}
- /* a transparent main-menubar with a smaller bottom border */
+ /* a transparent main-menubar */
.main-menubar-light() {
#scout.menubar-background-color-inherit();
- padding-left: 0;
- padding-right: 0;
- margin-left: @bench-padding-x;
- margin-right: @bench-padding-x;
- }
-
- .menubar-focus() {
- &:focus {
- outline: none;
-
- &:not(.disabled) {
- color: @hover-color;
-
- &:not(.menu-button) {
- color: @focus-color;
-
- &::after {
- color: @focus-color;
- }
- }
-
- /* Draw border for icon only items */
-
- &.menu-icononly::after {
- #scout.button-focus();
- }
-
- &:not(.menu-icononly):not(.menu-button) > .text {
- text-decoration: underline;
- }
- }
- }
}
}
@@ -83,184 +40,76 @@
width: 100%;
background-color: @menubar-background-color;
border-bottom: 1px solid @border-color;
- /* default style is 'top' */
- vertical-align: middle;
white-space: nowrap;
&.bottom {
border-bottom: 0;
border-top: 1px solid @border-color;
}
+}
- & > .menubox {
- display: inline-block;
- height: 100%;
- #scout.vertical-align-helper-before();
+.menubar-box {
+ display: inline-flex;
+ height: 100%;
+ align-items: center;
- &.right {
- float: right;
- }
+ &.right {
+ float: right;
+ }
- & > .menu-separator {
- display: inline-block;
- vertical-align: middle;
- width: 1px;
- margin-right: @menu-item-margin-right;
- background-color: @border-color;
- height: 15px;
+ & > .menu-separator {
+ width: 1px;
+ margin: 0 @menu-item-padding-x;
+ background-color: @border-color;
+ height: 15px;
- &.overflown {
- display: none;
- }
+ &.overflown {
+ display: none;
}
+ }
- /* child menus of combo-menu must have the same padding, but not the all the other styles of > .menu-item */
- & > .combo-menu > .menu-item {
- padding-top: @menubar-item-padding-y;
- padding-bottom: @menubar-item-padding-y;
- }
-
- & > .menu-item {
- display: inline-block;
- padding-top: @menubar-item-padding-y;
- padding-bottom: @menubar-item-padding-y;
-
- /* By making sure menu items are always at least the same height as form fields, it is easier to align them with each other, especially when zoomed */
- min-height: @logical-grid-row-height;
-
- &.overflown {
- display: none;
- }
-
- #scout.menubar-focus();
-
- &:not(.last) {
- margin-right: @menu-item-margin-right;
- }
-
- &.form-field-menu {
- padding-top: @menubar-field-menu-margin-y;
- padding-bottom: @menubar-field-menu-margin-y;
- }
-
- /* ---------------------------------- */
-
- &.menu-button {
- text-align: center;
- color: @menubar-button-color;
- background-color: @menubar-button-background-color;
- border: 1px solid @menubar-button-border-color;
- border-radius: @button-border-radius;
- min-width: 110px;
- margin-top: @menubar-field-menu-margin-y;
- margin-bottom: @menubar-field-menu-margin-y;
- padding-left: @button-padding-x;
- padding-right: @button-padding-x;
-
- &:not(.bottom-text) {
- height: @logical-grid-row-height;
- padding-top: @button-padding-y;
- padding-bottom: @button-padding-y;
- }
-
- /* TODO [7.0] cgu/BSH: Actually, we wanted to use .overflow-ellipsis-nowrap to support */
- /* ellipsis, but this breaks the focus, because the ::before inline element gets */
- /* cut off. We should find a better solution for this. */
- /*white-space: nowrap;*/
-
- &:hover {
- color: @button-hover-color;
-
- & > .font-icon {
- color: @button-hover-color;
- }
- }
-
- &:active, &.active {
- color: @button-active-color;
- background-color: @menubar-button-active-background-color;
-
- & > .font-icon {
- color: @button-active-color;
- }
- }
-
- &:focus::after {
- #scout.button-focus();
- }
-
- & > .font-icon {
- color: @menubar-button-font-icon-color;
- }
-
- &.default-menu:not(.disabled) {
- #scout.font-text-normal(@font-weight-bold);
- background-color: @default-button-background-color;
- /* border is necessary to align the text with text from buttons with a real border */
- border-color: transparent;
- color: @default-button-color;
-
- &:focus {
- color: @default-button-color;
- }
-
- &:hover {
- background-color: @default-button-hover-background-color;
- }
-
- &:active, &.active {
- background-color: @default-button-active-background-color;
- border-color: @default-button-active-background-color;
- }
-
- & > .font-icon {
- font-weight: normal;
- color: @icon-inverted-color;
- }
- }
+ & > .menu-item {
+ margin: @menubar-item-margin-y @menubar-item-margin-x;
+ /* By making sure menu items are always at least the same height as form fields, it is easier to align them with each other, especially when zoomed */
+ min-height: @logical-grid-row-height;
- &.disabled {
- background-color: @menubar-button-disabled-background-color;
- border-color: @menubar-button-disabled-border-color;
- color: @menubar-button-disabled-color;
- cursor: default;
+ &.menu-icononly {
+ margin-left: @menubar-item-icononly-margin-x;
+ margin-right: @menubar-item-icononly-margin-x;
+ min-width: @logical-grid-row-height; // Make it square
+ justify-content: center;
+ }
- & > .font-icon {
- color: @menubar-button-font-icon-disabled-color;
- }
- }
+ &.overflown {
+ display: none;
+ }
- &.selected {
- border-style: inset;
- border-right-color: @border-color;
- border-bottom-color: @border-color;
- }
+ &.first {
+ margin-left: 0;
+ }
- &:not(.last) {
- margin-right: @menu-item-margin-right;
- }
+ &.last {
+ margin-right: 0;
+ }
+ }
- &.left-of-button {
- margin-right: @menu-item-margin-right-between-buttons;
- }
+ & > .menu-button {
+ min-width: 110px;
+ margin: @menubar-button-margin;
- &.small-gap {
- margin-right: 6px;
- }
-
- &.small {
- min-width: 53px;
+ &.small {
+ min-width: 53px;
+ }
+ }
- & > .font-icon {
- font-size: 18px;
- }
- }
- }
+ /* If the menubar has a border left and right, the first and last items need to be adjusted */
+ .bounded > & > .menu-item {
+ &.first {
+ margin-left: @menubar-item-margin-y;
}
- /* Menu items within a top-level combo-menu should have a focus-border too */
- & > .combo-menu > .menu-item {
- #scout.menubar-focus();
+ &.last {
+ margin-right: @menubar-item-margin-y;
}
}
}
@@ -268,47 +117,48 @@
.main-menubar {
#scout.main-menubar-standard;
- & > .menubox {
+ & > .menubar-box {
- & > .menu-item, & > .combo-menu > .menu-item {
- margin-top: 8px;
- margin-bottom: 7px;
+ & > .menu-separator {
+ height: @logical-grid-row-height - 12px;
+ }
- &.bottom-text.menu-textandicon {
- margin-top: 0;
- margin-bottom: 0;
- }
+ & > .menu-item {
+ margin-left: @main-menubar-item-margin-x;
+ margin-right: @main-menubar-item-margin-x;
- &.menu-button {
- margin-top: 8px;
- margin-bottom: 7px;
+ &.first {
+ margin-left: @main-menubar-first-menu-item-margin-left;
}
- &.form-field-menu {
- padding-top: 0;
- padding-bottom: 0;
+ &.last {
+ margin-right: @main-menubar-last-menu-item-margin-right;
}
- &.small > .font-icon {
- font-size: 18px;
- }
+ &.menu-icononly {
+ margin-left: @menubar-item-icononly-margin-x;
+ margin-right: @menubar-item-icononly-margin-x;
- &.last {
- margin-right: 0;
+ &.first {
+ margin-left: @main-menubar-first-menu-item-icononly-margin-left;
+ }
+
+ &.last {
+ margin-right: @main-menubar-last-menu-item-icononly-margin-right;
+ }
}
}
- & > .menu-separator {
- height: @logical-grid-row-height - 12px;
- }
+ & > .menu-button {
+ &.first,
+ &.first.menu-icononly {
+ margin-left: @bench-padding-x;
+ }
- /* combo-menu itself should have no margins and no padding so its child menus
- * can render a focus border like regular top-level menus. */
- & > .combo-menu.menu-item {
- margin-top: 0;
- margin-bottom: 0;
- padding-top: 0;
- padding-bottom: 0;
+ &.last,
+ &.last.menu-icononly {
+ margin-right: @bench-padding-x;
+ }
}
}
}
diff --git a/eclipse-scout-core/src/menu/menubar/MenubarBox.js b/eclipse-scout-core/src/menu/menubar/MenuBarBox.js
index 8c6c16eead..d280673276 100644
--- a/eclipse-scout-core/src/menu/menubar/MenubarBox.js
+++ b/eclipse-scout-core/src/menu/menubar/MenuBarBox.js
@@ -8,9 +8,9 @@
* Contributors:
* BSI Business Systems Integration AG - initial API and implementation
*/
-import {arrays, HtmlComponent, MenuBar, MenubarBoxLayout, Widget} from '../../index';
+import {arrays, HtmlComponent, MenuBar, MenuBarBoxLayout, Widget} from '../../index';
-export default class MenubarBox extends Widget {
+export default class MenuBarBox extends Widget {
constructor() {
super();
@@ -26,10 +26,10 @@ export default class MenubarBox extends Widget {
}
_render() {
- this.$container = this.$parent.appendDiv('menubox');
+ this.$container = this.$parent.appendDiv('menubar-box');
this.htmlComp = HtmlComponent.install(this.$container, this.session);
- this.htmlComp.setLayout(new MenubarBoxLayout(this));
+ this.htmlComp.setLayout(new MenuBarBoxLayout(this));
}
_renderProperties() {
diff --git a/eclipse-scout-core/src/menu/menubar/MenubarBoxLayout.js b/eclipse-scout-core/src/menu/menubar/MenuBarBoxLayout.js
index 9961a55a28..62ee03027f 100644
--- a/eclipse-scout-core/src/menu/menubar/MenubarBoxLayout.js
+++ b/eclipse-scout-core/src/menu/menubar/MenuBarBoxLayout.js
@@ -10,7 +10,7 @@
*/
import {AbstractLayout, Dimension} from '../../index';
-export default class MenubarBoxLayout extends AbstractLayout {
+export default class MenuBarBoxLayout extends AbstractLayout {
constructor(menubox) {
super();
diff --git a/eclipse-scout-core/src/menu/menubar/MenuBarLayout.js b/eclipse-scout-core/src/menu/menubar/MenuBarLayout.js
index acff64e83b..49d9529f83 100644
--- a/eclipse-scout-core/src/menu/menubar/MenuBarLayout.js
+++ b/eclipse-scout-core/src/menu/menubar/MenuBarLayout.js
@@ -17,20 +17,20 @@ export default class MenuBarLayout extends AbstractLayout {
this._menuBar = menuBar;
this._overflowMenuItems = [];
- this._visibleMenuItems = [];
this._ellipsis = null;
this.collapsed = false;
}
layout($container) {
- let menuItems = this._menuBar.orderedMenuItems.left.concat(this._menuBar.orderedMenuItems.right),
- htmlContainer = HtmlComponent.get($container),
- ellipsis;
+ let menuItems = this._menuBar.orderedMenuItems.left.concat(this._menuBar.orderedMenuItems.right);
+ let visibleMenuItems = this.visibleMenuItems();
+ let htmlContainer = HtmlComponent.get($container);
- ellipsis = arrays.find(menuItems, menuItem => {
+ let ellipsis = arrays.find(menuItems, menuItem => {
return menuItem.ellipsis;
});
+ this._setFirstLastMenuMarker(visibleMenuItems); // is required to determine available size correctly
this.preferredLayoutSize($container, {
widthHint: htmlContainer.availableSize().width,
undo: false
@@ -40,7 +40,7 @@ export default class MenuBarLayout extends AbstractLayout {
if (ellipsis && this._overflowMenuItems.length > 0) {
ellipsis.setHidden(false);
}
- this._visibleMenuItems.forEach(menuItem => {
+ visibleMenuItems.forEach(menuItem => {
menuItem._setOverflown(false);
}, this);
@@ -62,11 +62,11 @@ export default class MenuBarLayout extends AbstractLayout {
}
// trigger menu items layout
- this._visibleMenuItems.forEach(menuItem => {
+ visibleMenuItems.forEach(menuItem => {
menuItem.validateLayout();
});
- this._visibleMenuItems.forEach(menuItem => {
+ visibleMenuItems.forEach(menuItem => {
// Make sure open popups are at the correct position after layouting
if (menuItem.popup) {
menuItem.popup.position();
@@ -79,9 +79,7 @@ export default class MenuBarLayout extends AbstractLayout {
if (!this._menuBar.isVisible()) {
return new Dimension(0, 0);
}
- let visibleMenuItems = this._menuBar.orderedMenuItems.all.filter(menuItem => {
- return menuItem.visible;
- }, this);
+ let visibleMenuItems = this.visibleMenuItems();
let overflowMenuItems = visibleMenuItems.filter(menuItem => {
let overflown = menuItem.overflown;
menuItem._setOverflown(false);
@@ -119,7 +117,6 @@ export default class MenuBarLayout extends AbstractLayout {
this.shrink(shrinkedMenuItems);
}
- this._visibleMenuItems = visibleMenuItems;
return prefSize.add(htmlComp.insets());
}
@@ -255,6 +252,12 @@ export default class MenuBarLayout extends AbstractLayout {
}, this);
}
+ visibleMenuItems() {
+ return this._menuBar.orderedMenuItems.all.filter(menuItem => {
+ return menuItem.visible;
+ }, this);
+ }
+
/* --- STATIC HELPERS ------------------------------------------------------------- */
/**
diff --git a/eclipse-scout-core/src/planner/Planner.js b/eclipse-scout-core/src/planner/Planner.js
index 1f199014f4..6239392def 100644
--- a/eclipse-scout-core/src/planner/Planner.js
+++ b/eclipse-scout-core/src/planner/Planner.js
@@ -124,7 +124,8 @@ export default class Planner extends Widget {
this.menuBar = scout.create('MenuBar', {
parent: this,
position: MenuBar.Position.BOTTOM,
- menuOrder: new PlannerMenuItemsOrder(this.session, 'Planner')
+ menuOrder: new PlannerMenuItemsOrder(this.session, 'Planner'),
+ cssClass: 'bounded'
});
for (let i = 0; i < this.resources.length; i++) {
this._initResource(this.resources[i]);
diff --git a/eclipse-scout-core/src/planner/Planner.less b/eclipse-scout-core/src/planner/Planner.less
index 38471d4a30..497bf282c4 100644
--- a/eclipse-scout-core/src/planner/Planner.less
+++ b/eclipse-scout-core/src/planner/Planner.less
@@ -24,11 +24,6 @@
position: absolute;
overflow: hidden;
- & > .menubar {
- padding-left: @resource-padding-x;
- padding-right: @resource-padding-x;
- }
-
& > .menubar.bottom {
position: absolute;
bottom: 0;
diff --git a/eclipse-scout-core/src/planner/PlannerHeader.less b/eclipse-scout-core/src/planner/PlannerHeader.less
index de871e9df2..3de6f78b80 100644
--- a/eclipse-scout-core/src/planner/PlannerHeader.less
+++ b/eclipse-scout-core/src/planner/PlannerHeader.less
@@ -127,9 +127,9 @@
}
&.disabled {
- background-color: @menubar-button-disabled-background-color;
- border-color: @menubar-button-disabled-border-color;
- color: @menubar-button-disabled-color;
+ background-color: @button-disabled-background-color;
+ border-color: @button-disabled-border-color;
+ color: @button-disabled-color;
cursor: default;
}
}
diff --git a/eclipse-scout-core/src/popup/Popup.js b/eclipse-scout-core/src/popup/Popup.js
index 5a3c80fb7e..fe4a4b35b4 100644
--- a/eclipse-scout-core/src/popup/Popup.js
+++ b/eclipse-scout-core/src/popup/Popup.js
@@ -207,12 +207,13 @@ export default class Popup extends Widget {
return;
}
// Give the browser time to layout properly before starting the animation to make sure it will be smooth.
- this.$container.addClass('invisible');
+ // The before-animate-open class will make the popup invisible (cannot use the invisible class because it is already used by _validateVisibility)
+ this.$container.addClass('before-animate-open');
setTimeout(() => {
if (!this.rendered || this.removing) {
return;
}
- this.$container.removeClass('invisible');
+ this.$container.removeClass('before-animate-open');
this.validateFocus(); // Need to be done after popup is visible again because focus cannot be set on invisible elements.
this.$container.addClassForAnimation('animate-open');
});
diff --git a/eclipse-scout-core/src/popup/Popup.less b/eclipse-scout-core/src/popup/Popup.less
index 0e531b77f9..86968e73a3 100644
--- a/eclipse-scout-core/src/popup/Popup.less
+++ b/eclipse-scout-core/src/popup/Popup.less
@@ -20,6 +20,10 @@
min-width: @tooltip-arrow-size;
min-height: @tooltip-arrow-size;
}
+
+ &.before-animate-open {
+ .invisible();
+ }
}
.popup-arrow {
diff --git a/eclipse-scout-core/src/popup/PopupLayout.js b/eclipse-scout-core/src/popup/PopupLayout.js
index f30155911a..d2d050aa90 100644
--- a/eclipse-scout-core/src/popup/PopupLayout.js
+++ b/eclipse-scout-core/src/popup/PopupLayout.js
@@ -18,6 +18,9 @@ export default class PopupLayout extends AbstractLayout {
this.doubleCalcPrefSize = true; // enables popups with a height which depends on the width (= popups with wrapping content)
this.autoPosition = true;
this.autoSize = true;
+ this.resizeAnimationRunning = false;
+ this.resizeAnimationDuration = null; // default
+ this._autoPositionOrig = null;
}
layout($container) {
@@ -69,6 +72,7 @@ export default class PopupLayout extends AbstractLayout {
// Bounds did not change -> do nothing
return;
}
+ this.resizeAnimationRunning = true;
htmlComp.$comp
.stop(true)
.cssHeight(currentBounds.height)
@@ -81,13 +85,15 @@ export default class PopupLayout extends AbstractLayout {
left: prefPosition.left,
top: prefPosition.top
}, {
- complete: function() {
+ duration: this.resizeAnimationDuration,
+ complete: () => {
+ this.resizeAnimationRunning = false;
if (!this.popup.rendered) {
return;
}
// Ensure the arrow is at the correct position after the animation
this._position();
- }.bind(this)
+ }
});
}
@@ -129,10 +135,8 @@ export default class PopupLayout extends AbstractLayout {
* @returns {Dimension}
*/
_calcMaxSize() {
- if (this.popup.repositionEnabled) {
- // Position the popup at the desired location before doing any calculations to consider the preferred bounds
- this._position(false);
- }
+ // Position the popup at the desired location before doing any calculations to consider the preferred bounds
+ this._position(false);
let maxWidth, maxHeight,
htmlComp = this.popup.htmlComp,
@@ -222,10 +226,8 @@ export default class PopupLayout extends AbstractLayout {
* @returns {Insets}
*/
_calcMaxSizeAroundAnchor() {
- if (this.popup.repositionEnabled) {
- // Position the popup at the desired location before doing any calculations because positioning adds CSS classes which might change margins
- this._position(false);
- }
+ // Position the popup at the desired location before doing any calculations because positioning adds CSS classes which might change margins
+ this._position(false);
let maxWidthLeft, maxWidthRight, maxHeightDown, maxHeightUp,
htmlComp = this.popup.htmlComp,
@@ -256,4 +258,16 @@ export default class PopupLayout extends AbstractLayout {
return new Insets(maxHeightUp, maxWidthRight, maxHeightDown, maxWidthLeft);
}
+
+ disableAutoPosition() {
+ if (this._autoPositionOrig === null) {
+ this._autoPositionOrig = this.autoPosition;
+ this.autoPosition = false;
+ }
+ }
+
+ resetAutoPosition() {
+ this.autoPosition = this._autoPositionOrig;
+ this._autoPositionOrig = null;
+ }
}
diff --git a/eclipse-scout-core/src/style/colors.less b/eclipse-scout-core/src/style/colors.less
index 8c4286fb84..2b88d25133 100644
--- a/eclipse-scout-core/src/style/colors.less
+++ b/eclipse-scout-core/src/style/colors.less
@@ -76,7 +76,10 @@
@active-color: @palette-blue-6;
@active-inverted-background-color: @palette-blue-5;
+@active-inverted-hover-background-color: @palette-blue-6;
+@active-inverted-active-background-color: darken(@active-inverted-hover-background-color, 10%);
@active-inverted-color: @palette-white;
+@active-background-color: fade(@palette-black, 14%);
@application-loading-background-color: @palette-white;
@application-loading01-color: fade(@palette-blue-5, 90%);
@application-loading02-color: @palette-cyan-4;
@@ -99,6 +102,7 @@
@error-color: @palette-red-3;
@error-glow-color: @palette-red-3;
@focus-border-color: @palette-blue-6;
+@focus-box-shadow-color: darken(@palette-blue-1, 10%);
@focus-color: @palette-blue-6;
@focus-glow-color: fade(@focus-border-color, 50%);
@focus-border-color-inverted: @active-inverted-color;
@@ -122,7 +126,7 @@
@loading-indicator-knight-rider-color: @palette-blue-3;
@panel-background-color: @palette-gray-2;
@read-only-color: @text-color;
-@selected-background-color: fade(@palette-black, 14%);
+@selected-background-color: @active-background-color;
@selected-intense-background-color: fade(@palette-black, 30%);
@sub-title-color: @palette-gray-7;
@text-color: @palette-gray-10;
@@ -148,15 +152,13 @@
@boxbutton-border-color: @border-color;
@browser-field-background-color: @palette-white; /* always white to not change the look of the website */
@button-active-color: @active-color;
-@button-active-background-color: @palette-gray-3;
+@button-active-background-color: @active-background-color;
@button-background-color: @control-background-color;
@button-border-color: @button-color;
@button-color: @palette-blue-6;
-@button-disabled-background-color: @control-disabled-background-color;
+@button-disabled-background-color: transparent;
@button-disabled-border-color: @border-color;
@button-disabled-color: @disabled-color;
-@button-font-icon-color: @button-color;
-@button-font-icon-disabled-color: @button-disabled-border-color;
@button-hover-color: @button-color;
@busyindicator-active-color: @palette-blue-6;
@busyindicator-border-color: @border-color;
@@ -194,6 +196,8 @@
@command-button-active-color: @active-inverted-color;
@command-button-active-border-color: @active-inverted-background-color;
@command-button-active-background-color: @active-inverted-background-color;
+@context-menu-item-color: @text-color;
+@context-menu-item-icon-color: @link-color;
@date-picker-background-color: @control-popup-background-color;
@date-picker-header-background-color: @panel-background-color;
@date-picker-day-hover-border-color: @item-selection-border-color;
@@ -213,10 +217,10 @@
@date-picker-weekend-color: @palette-blue-6;
@date-picker-day-preselected-border-color: @item-selection-border-color;
@date-picker-now-preselected-border-color: @highlight-color;
-@default-button-active-background-color: @palette-blue-6;
-@default-button-background-color: @palette-blue-4;
-@default-button-color: @palette-white;
-@default-button-hover-background-color: @default-button-background-color;
+@default-button-active-background-color: @active-inverted-active-background-color;
+@default-button-background-color: @active-inverted-background-color;
+@default-button-color: @active-inverted-color;
+@default-button-hover-background-color: @active-inverted-hover-background-color;
@desktop-bench-background-color: @background-color;
@desktop-bench-tab-area-background-color: @palette-gray-3;
@desktop-navigation-handle-active-background-color: @collapse-handle-active-background-color;
@@ -260,24 +264,15 @@
@link-hover-color: lighten(@link-color, 10%);
@main-menubar-background-color: @menubar-background-color;
@menubar-background-color: @palette-white;
-@menubar-button-active-background-color: @button-active-background-color;
-@menubar-button-background-color: @button-background-color;
-@menubar-button-border-color: @button-border-color;
-@menubar-button-color: @button-color;
-@menubar-button-disabled-background-color: @button-disabled-background-color;
-@menubar-button-disabled-border-color: @palette-gray-5;
-@menubar-button-disabled-color: @button-disabled-color;
-@menubar-button-font-icon-color: @menubar-button-color;
-@menubar-button-font-icon-disabled-color: @menubar-button-disabled-border-color;
-@menubar-item-color: @text-color;
+@menu-item-color: @link-color;
@menu-item-disabled-color: @disabled-color;
@messagebox-background-color: @popup-background-color;
@mode-hover-text-color: @hover-color;
@mode-selected-background-color: @active-inverted-background-color;
@mode-selected-text-color: @active-inverted-color;
@mode-alternative-selected-color: @mode-selected-background-color;
-@navigate-up-button-border-color: @menubar-button-border-color;
-@navigate-up-button-color: @menubar-button-color;
+@navigate-up-button-border-color: @button-border-color;
+@navigate-up-button-color: @button-color;
@desktop-navigation-background-color: @desktop-header-background-color;
@desktop-navigation-body-background-color: @palette-white;
@desktop-navigation-color: @palette-gray-10;
diff --git a/eclipse-scout-core/src/style/mixins.less b/eclipse-scout-core/src/style/mixins.less
index 06351efe43..9e37194208 100644
--- a/eclipse-scout-core/src/style/mixins.less
+++ b/eclipse-scout-core/src/style/mixins.less
@@ -40,12 +40,6 @@
#scout.glow(@focus-glow-color);
}
- .focus-border-inverted(@bordersize: 1px) {
- outline: none;
- border: @bordersize solid @focus-border-color-inverted;
- #scout.glow(@focus-glow-color-inverted);
- }
-
.no-focus-border() {
outline: none;
border: 0;
@@ -380,9 +374,22 @@
}
.button-focus() {
- #scout.overlay(-3px, -3px, 6px, 6px);
- #scout.focus-border();
- border-radius: @button-border-radius;
+ #scout.box-shadow-focus();
+ border-color: mix(@button-border-color, @focus-box-shadow-color, 25%);
+ }
+
+ .box-shadow-focus() {
+ outline: none;
+ box-shadow: 0 0 0 @focus-box-shadow-size @focus-box-shadow-color;
+ }
+
+ .box-shadow-focus-inset() {
+ outline: none;
+ box-shadow: inset 0 0 0 @focus-box-shadow-size @focus-box-shadow-color;
+ }
+
+ .box-shadow-focus-transition() {
+ transition: box-shadow 0.3s ease;
}
.triangle-top-left(@size, @color) {
@@ -437,6 +444,10 @@
font-size: 16px;
}
+ .submenu-icon-open() {
+ #scout.transform(rotateX(180deg) translateY(-1px));
+ }
+
/* Invisible pseudo element that enables vertical-align if container has height set */
.vertical-align-helper(@minHeight: 0) {
content: '';
diff --git a/eclipse-scout-core/src/style/sizes.less b/eclipse-scout-core/src/style/sizes.less
index fc29d67e32..c63cd3e166 100644
--- a/eclipse-scout-core/src/style/sizes.less
+++ b/eclipse-scout-core/src/style/sizes.less
@@ -34,6 +34,7 @@
@field-label-width: 140px;
@field-status-width: 20px;
@field-status-margin-left: 10px;
+@focus-box-shadow-size: 3px;
@text-field-padding-x: 10px;
@text-field-padding-y: 7px;
@text-field-padding-top-compensation: 1px;
@@ -89,6 +90,7 @@
@busyindicator-large-size: 100px;
@busyindicator-large-border-width: 2px;
@button-border-radius: @border-radius;
+@button-focus-border-radius: 6px;
@button-padding-x: 7px;
@button-padding-y: 2px;
@button-margin-top: 1px; /* Necessary to align icon with text */
@@ -109,6 +111,8 @@
@compact-outline-node-padding-x: 16px;
@compact-outline-node-padding-y: @outline-breadcrumb-node-padding-y;
@compact-outline-title-padding-x: @compact-outline-node-padding-x;
+@context-menu-item-padding-y: 8px;
+@context-menu-item-padding-left: 16px;
@dashboard-tile-label-padding-bottom: 5px;
@dashboard-tile-label-large-padding-bottom: 10px;
@date-picker-header-height: @logical-grid-row-height;
@@ -164,15 +168,23 @@
@group-box-title-padding-top: 12px;
@group-box-title-with-sub-label-padding-bottom: 6px;
@login-box-font-size: 16px;
-@menu-item-height: 39px;
-@menu-item-margin-right-between-buttons: 10px;
-@menu-item-margin-right: 20px;
-@menu-item-max-image-icon-height: 18px;
-@menu-item-min-image-icon-width: 18px;
-@menu-item-text-padding-top: 1px;
@main-menubar-height: @desktop-header-height;
+@main-menubar-item-margin-x: 5px;
+@main-menubar-first-menu-item-margin-left: @bench-padding-x - @menu-item-padding-x;
+@main-menubar-last-menu-item-margin-right: @main-menubar-first-menu-item-margin-left;
+@main-menubar-first-menu-item-icononly-margin-left: @bench-padding-x - @menubar-item-icononly-padding-x;
+@main-menubar-last-menu-item-icononly-margin-right: @main-menubar-first-menu-item-icononly-margin-left;
+@menubar-button-margin: @menubar-field-menu-margin-y;
@menubar-field-menu-margin-y: 4px;
-@menubar-item-padding-y: 6px;
+@menubar-item-icononly-padding-x: 8px;
+@menubar-item-icononly-margin-x: 2px;
+@menubar-item-margin-x: 2px;
+@menubar-item-margin-y: 2px;
+@menu-item-height: 39px;
+@menu-item-min-image-icon-width: 18px;
+@menu-item-max-image-icon-height: 18px;
+@menu-item-padding-x: 10px;
+@menu-item-padding-y: 6px;
@messagebox-label-padding: 20px;
@messagebox-max-width: 330px;
@mobile-popup-title-margin-right: 30px;
diff --git a/eclipse-scout-core/src/table/Table.js b/eclipse-scout-core/src/table/Table.js
index 8b0594770b..cbba2db559 100644
--- a/eclipse-scout-core/src/table/Table.js
+++ b/eclipse-scout-core/src/table/Table.js
@@ -4325,7 +4325,8 @@ export default class Table extends Widget {
parent: this,
position: MenuBar.Position.BOTTOM,
menuOrder: new MenuItemsOrder(this.session, 'Table'),
- menuFilter: this._filterMenusHandler
+ menuFilter: this._filterMenusHandler,
+ cssClass: 'bounded'
});
}
diff --git a/eclipse-scout-core/src/table/Table.less b/eclipse-scout-core/src/table/Table.less
index d434b09f2b..37433bd10a 100644
--- a/eclipse-scout-core/src/table/Table.less
+++ b/eclipse-scout-core/src/table/Table.less
@@ -25,11 +25,6 @@
}
}
- & > .menubar:not(.main-menubar) {
- padding-left: @table-menubar-padding;
- padding-right: @table-menubar-padding;
- }
-
&.checkable {
& > .table-data > .table-row {
cursor: pointer;
diff --git a/eclipse-scout-core/src/table/TableHeader.less b/eclipse-scout-core/src/table/TableHeader.less
index fe7af94937..62f7539749 100644
--- a/eclipse-scout-core/src/table/TableHeader.less
+++ b/eclipse-scout-core/src/table/TableHeader.less
@@ -29,8 +29,6 @@
/* make menubar a little smaller than header (but still center it). Otherwise browsers may draw the menu bar over the bottom border of the table header when the page is zoomed */
top: 1px;
height: calc(~'100% - 2px');
- padding-right: 6px;
- padding-left: 6px;
/* Do not use transparent color here, because otherwise header items would be visible behind */
background-color: inherit;
z-index: 1;
@@ -39,6 +37,10 @@
height: 100%;
background-color: transparent;
border: none;
+
+ & > .menubar-box > .menu-item {
+ border-radius: 0;
+ }
}
}
diff --git a/eclipse-scout-core/src/tile/fields/tablefield/TileTableField.less b/eclipse-scout-core/src/tile/fields/tablefield/TileTableField.less
index 95856bc6b7..44acca8074 100644
--- a/eclipse-scout-core/src/tile/fields/tablefield/TileTableField.less
+++ b/eclipse-scout-core/src/tile/fields/tablefield/TileTableField.less
@@ -135,7 +135,7 @@
}
}
- & > .menubar > .menubox > .menu-item {
+ & > .menubar > .menubar-box > .menu-item {
color: @tile-default-inverted-color;
}
}
@@ -172,7 +172,7 @@
}
}
- & > .menubar > .menubox > .menu-item {
+ & > .menubar > .menubar-box > .menu-item {
color: @tile-alternative-inverted-color;
}
}
diff --git a/eclipse-scout-core/src/tree/Tree.js b/eclipse-scout-core/src/tree/Tree.js
index c47a0afafd..e797c02255 100644
--- a/eclipse-scout-core/src/tree/Tree.js
+++ b/eclipse-scout-core/src/tree/Tree.js
@@ -162,7 +162,8 @@ export default class Tree extends Widget {
parent: this,
position: MenuBar.Position.BOTTOM,
menuOrder: new MenuItemsOrder(this.session, 'Tree'),
- menuFilter: this._filterMenusHandler
+ menuFilter: this._filterMenusHandler,
+ cssClass: 'bounded'
});
this._updateItemPath(true);
this._setDisplayStyle(this.displayStyle);
diff --git a/eclipse-scout-core/src/tree/Tree.less b/eclipse-scout-core/src/tree/Tree.less
index d2d14a9766..b43babe035 100644
--- a/eclipse-scout-core/src/tree/Tree.less
+++ b/eclipse-scout-core/src/tree/Tree.less
@@ -26,11 +26,6 @@
border-top-color: @item-selection-nonfocus-background-color;
}
}
-
- & > .menubar {
- padding-left: 8px;
- padding-right: 8px;
- }
}
.tree-data {
diff --git a/eclipse-scout-core/src/widget/Widget.js b/eclipse-scout-core/src/widget/Widget.js
index 7540df058a..737afaf8ed 100644
--- a/eclipse-scout-core/src/widget/Widget.js
+++ b/eclipse-scout-core/src/widget/Widget.js
@@ -2099,6 +2099,9 @@ export default class Widget {
return null;
}
+ /**
+ * @param {object} [options]
+ */
_installScrollbars(options) {
let $scrollable = this.get$Scrollable();
if (!$scrollable) {
diff --git a/eclipse-scout-core/test/menu/ContextMenuPopupSpec.js b/eclipse-scout-core/test/menu/ContextMenuPopupSpec.js
index 75122949c6..f757317378 100644
--- a/eclipse-scout-core/test/menu/ContextMenuPopupSpec.js
+++ b/eclipse-scout-core/test/menu/ContextMenuPopupSpec.js
@@ -194,8 +194,8 @@ describe('ContextMenuPopup', () => {
let menu0Clone = findClone(popup, menu0);
let menu2Clone = findClone(popup, menu2);
- expect(menu0Clone.$container).toHaveClass('context-menu-item-first');
- expect(menu2Clone.$container).toHaveClass('context-menu-item-last');
+ expect(menu0Clone.$container).toHaveClass('first');
+ expect(menu2Clone.$container).toHaveClass('last');
popup.remove();
});
@@ -212,9 +212,9 @@ describe('ContextMenuPopup', () => {
let menu0Clone = findClone(popup, menu0);
let menu1Clone = findClone(popup, menu1);
let menu2Clone = findClone(popup, menu2);
- expect(menu0Clone.$container).toHaveClass('context-menu-item-first');
- expect(menu1Clone.$container).toHaveClass('context-menu-item-last');
- expect(menu2Clone.$container).not.toHaveClass('context-menu-item-last');
+ expect(menu0Clone.$container).toHaveClass('first');
+ expect(menu1Clone.$container).toHaveClass('last');
+ expect(menu2Clone.$container).not.toHaveClass('last');
popup.remove();
});
diff --git a/eclipse-scout-core/test/menu/MenuBarSpec.js b/eclipse-scout-core/test/menu/MenuBarSpec.js
index 83f91fee44..5fc66fd3bf 100644
--- a/eclipse-scout-core/test/menu/MenuBarSpec.js
+++ b/eclipse-scout-core/test/menu/MenuBarSpec.js
@@ -397,8 +397,8 @@ describe('MenuBar', () => {
expect(menuBar.menuItems[0]).toBe(menu1);
expect(menuBar.menuItems[1]).toBe(menu2);
- expect(menu1.$container).not.toHaveClass('default-menu');
- expect(menu2.$container).toHaveClass('default-menu');
+ expect(menu1.$container).not.toHaveClass('default');
+ expect(menu2.$container).toHaveClass('default');
});
it('marks ButtonAdapterMenu that reacts to ENTER keystroke as default menu', () => {
@@ -426,7 +426,7 @@ describe('MenuBar', () => {
expect(menuBar.menuItems.length).toBe(1);
expect(menuBar.menuItems[0]).toBe(adapterMenu);
- expect(adapterMenu.$container).toHaveClass('default-menu');
+ expect(adapterMenu.$container).toHaveClass('default');
});
it('marks first visible and enabled menu that has the "defaultMenu" flag set as default menu', () => {
@@ -469,12 +469,12 @@ describe('MenuBar', () => {
expect(menuBar.menuItems[4]).toBe(menu5);
expect(menuBar.menuItems[5]).toBe(menu6);
- expect(menu1.$container).not.toHaveClass('default-menu');
- expect(menu2.$container).not.toHaveClass('default-menu');
- expect(menu3.$container).not.toHaveClass('default-menu');
- expect(menu4.$container).toHaveClass('default-menu');
- expect(menu5.$container).not.toHaveClass('default-menu');
- expect(menu6.$container).not.toHaveClass('default-menu');
+ expect(menu1.$container).not.toHaveClass('default');
+ expect(menu2.$container).not.toHaveClass('default');
+ expect(menu3.$container).not.toHaveClass('default');
+ expect(menu4.$container).toHaveClass('default');
+ expect(menu5.$container).not.toHaveClass('default');
+ expect(menu6.$container).not.toHaveClass('default');
expect(menu4).toBe(menuBar.defaultMenu);
});
@@ -496,18 +496,18 @@ describe('MenuBar', () => {
menuBar.setMenuItems(menusItems);
menuBar.render();
expect(menu1.rendered).toBe(true);
- expect(menu1.$container).not.toHaveClass('default-menu');
+ expect(menu1.$container).not.toHaveClass('default');
expect(menu2.rendered).toBe(true);
expect(menuBar.defaultMenu).toBe(menu2);
- expect(menu2.$container).toHaveClass('default-menu');
+ expect(menu2.$container).toHaveClass('default');
menu2.setProperty('enabled', false);
expect(menuBar.defaultMenu).toBe(null);
- expect(menu2.$container).not.toHaveClass('default-menu');
+ expect(menu2.$container).not.toHaveClass('default');
menu2.setProperty('enabled', true);
expect(menuBar.defaultMenu).toBe(menu2);
- expect(menu2.$container).toHaveClass('default-menu');
+ expect(menu2.$container).toHaveClass('default');
});
it('updates state if keyStroke or defaultMenu property of menu changes', () => {
@@ -530,35 +530,35 @@ describe('MenuBar', () => {
expect(menu1.rendered).toBe(true);
expect(menu2.rendered).toBe(true);
expect(menuBar.defaultMenu).toBe(menu2);
- expect(menu1.$container).not.toHaveClass('default-menu');
- expect(menu2.$container).toHaveClass('default-menu');
+ expect(menu1.$container).not.toHaveClass('default');
+ expect(menu2.$container).toHaveClass('default');
menu2.setProperty('keyStroke', null);
expect(menuBar.defaultMenu).toBe(null);
- expect(menu1.$container).not.toHaveClass('default-menu');
- expect(menu2.$container).not.toHaveClass('default-menu');
+ expect(menu1.$container).not.toHaveClass('default');
+ expect(menu2.$container).not.toHaveClass('default');
menu1.setProperty('keyStroke', 'enter');
expect(menuBar.defaultMenu).toBe(menu1);
- expect(menu1.$container).toHaveClass('default-menu');
- expect(menu2.$container).not.toHaveClass('default-menu');
+ expect(menu1.$container).toHaveClass('default');
+ expect(menu2.$container).not.toHaveClass('default');
menu2.setProperty('defaultMenu', true);
expect(menuBar.defaultMenu).toBe(menu2);
- expect(menu1.$container).not.toHaveClass('default-menu');
- expect(menu2.$container).toHaveClass('default-menu');
+ expect(menu1.$container).not.toHaveClass('default');
+ expect(menu2.$container).toHaveClass('default');
menu1.setProperty('defaultMenu', false);
menu2.setProperty('defaultMenu', false);
expect(menuBar.defaultMenu).toBe(null);
- expect(menu1.$container).not.toHaveClass('default-menu');
- expect(menu2.$container).not.toHaveClass('default-menu');
+ expect(menu1.$container).not.toHaveClass('default');
+ expect(menu2.$container).not.toHaveClass('default');
menu1.setProperty('defaultMenu', undefined);
menu2.setProperty('defaultMenu', undefined);
expect(menuBar.defaultMenu).toBe(menu1);
- expect(menu1.$container).toHaveClass('default-menu');
- expect(menu2.$container).not.toHaveClass('default-menu');
+ expect(menu1.$container).toHaveClass('default');
+ expect(menu2.$container).not.toHaveClass('default');
});
it('considers rendered state of default menu', () => {
@@ -579,16 +579,16 @@ describe('MenuBar', () => {
menuBar.setMenuItems(menuItems);
menuBar.render();
expect(menu1.rendered).toBe(true);
- expect(menu1.$container).not.toHaveClass('default-menu');
+ expect(menu1.$container).not.toHaveClass('default');
expect(menu2.rendered).toBe(true);
expect(menuBar.defaultMenu).toBe(menu2);
- expect(menu2.$container).toHaveClass('default-menu');
+ expect(menu2.$container).toHaveClass('default');
// Move default menu into ellipsis and call updateDefaultMenu explicitly to recalculate state
menus.moveMenuIntoEllipsis(menu2, ellipsisMenu);
menuBar.updateDefaultMenu();
expect(menu1.rendered).toBe(true);
- expect(menu1.$container).not.toHaveClass('default-menu');
+ expect(menu1.$container).not.toHaveClass('default');
expect(menu2.rendered).toBe(false);
expect(menuBar.defaultMenu).toBe(menu2);
diff --git a/org.eclipse.scout.rt.client/src/main/java/org/eclipse/scout/rt/client/ui/form/fields/button/IButton.java b/org.eclipse.scout.rt.client/src/main/java/org/eclipse/scout/rt/client/ui/form/fields/button/IButton.java
index 3bff572fd0..6d8547bed1 100644
--- a/org.eclipse.scout.rt.client/src/main/java/org/eclipse/scout/rt/client/ui/form/fields/button/IButton.java
+++ b/org.eclipse.scout.rt.client/src/main/java/org/eclipse/scout/rt/client/ui/form/fields/button/IButton.java
@@ -57,6 +57,7 @@ public interface IButton extends IFormField {
int DISPLAY_STYLE_TOGGLE = 1;
int DISPLAY_STYLE_RADIO = 2;
int DISPLAY_STYLE_LINK = 3;
+ int DISPLAY_STYLE_BORDERLESS = 4;
void doClick();

Back to the top