This is an automated email from the ASF dual-hosted git repository.
hansva pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/hop.git
The following commit(s) were added to refs/heads/main by this push:
new b46a83ecb1 give left sidebar more modern look, fixes #6266 (#6267)
b46a83ecb1 is described below
commit b46a83ecb1f0417b66a32c5a141d4180f3ae6ae1
Author: Hans Van Akelyen <[email protected]>
AuthorDate: Tue Dec 30 20:58:28 2025 +0100
give left sidebar more modern look, fixes #6266 (#6267)
---
.../org/apache/hop/ui/hopgui/dark-mode.css | 34 +-
.../org/apache/hop/ui/hopgui/light-mode.css | 34 +-
.../main/java/org/apache/hop/ui/core/PropsUi.java | 66 ++--
.../org/apache/hop/ui/core/gui/GuiResource.java | 32 +-
.../main/java/org/apache/hop/ui/hopgui/HopGui.java | 400 ++++++++++++++-------
.../perspective/explorer/ExplorerPerspective.java | 6 +-
.../perspective/search/HopSearchPerspective.java | 4 +
7 files changed, 356 insertions(+), 220 deletions(-)
diff --git a/rap/src/main/resources/org/apache/hop/ui/hopgui/dark-mode.css
b/rap/src/main/resources/org/apache/hop/ui/hopgui/dark-mode.css
index 6ca08a8427..a9656701e0 100644
--- a/rap/src/main/resources/org/apache/hop/ui/hopgui/dark-mode.css
+++ b/rap/src/main/resources/org/apache/hop/ui/hopgui/dark-mode.css
@@ -1360,6 +1360,10 @@ ToolItem[VERTICAL] {
border: none;
border-top: 1px solid transparent;
border-bottom: 1px solid transparent;
+ padding: 6px;
+ width: 34px;
+ height: 34px;
+ border-radius: 6px;
}
ToolItem:first, ToolItem[RIGHT_TO_LEFT]:last {
@@ -1426,23 +1430,15 @@ ToolItem:selected {
}
ToolItem[VERTICAL]:hover {
- background-image: gradient(
- linear, left top, right top,
- from(#d9d9d9),
- color-stop(45%, #161616),
- color-stop(55%, #161616),
- to(#d9d9d9)
- );
+ background-color: #3C3F41;
+ background-image: none;
+ border-radius: 6px;
}
ToolItem[VERTICAL]:pressed, ToolItem[VERTICAL]:selected {
- background-image: gradient(
- linear, left top, right top,
- from(#161616),
- color-stop(45%, #252525),
- color-stop(55%, #252525),
- to(#161616)
- );
+ background-color: #4A9EFF;
+ background-image: none;
+ border-radius: 6px;
}
ToolItem:first:hover,
@@ -2509,6 +2505,16 @@ FileUpload-FocusIndicator {
opacity: 1;
}
+/* Sidebar button styling */
+
+Composite.sidebarButton {
+ border-radius: 6px;
+}
+
+Label.sidebarButton {
+ border-radius: 6px;
+}
+
/* JFace specific theming */
Shell.jface_contentProposalPopup, Shell.jface_infoPopupDialog {
diff --git a/rap/src/main/resources/org/apache/hop/ui/hopgui/light-mode.css
b/rap/src/main/resources/org/apache/hop/ui/hopgui/light-mode.css
index 2c22d6b056..0d0179c456 100644
--- a/rap/src/main/resources/org/apache/hop/ui/hopgui/light-mode.css
+++ b/rap/src/main/resources/org/apache/hop/ui/hopgui/light-mode.css
@@ -1368,6 +1368,10 @@ ToolItem[VERTICAL] {
border: none;
border-top: 1px solid transparent;
border-bottom: 1px solid transparent;
+ padding: 6px;
+ width: 34px;
+ height: 34px;
+ border-radius: 6px;
}
ToolItem:first, ToolItem[RIGHT_TO_LEFT]:last {
@@ -1434,23 +1438,15 @@ ToolItem:selected {
}
ToolItem[VERTICAL]:hover {
- background-image: gradient(
- linear, left top, right top,
- from(#d9d9d9),
- color-stop(45%, #e9e9e9),
- color-stop(55%, #e9e9e9),
- to(#d9d9d9)
- );
+ background-color: #D0D0D0;
+ background-image: none;
+ border-radius: 6px;
}
ToolItem[VERTICAL]:pressed, ToolItem[VERTICAL]:selected {
- background-image: gradient(
- linear, left top, right top,
- from(#e9e9e9),
- color-stop(45%, #dadada),
- color-stop(55%, #dadada),
- to(#e9e9e9)
- );
+ background-color: #4A9EFF;
+ background-image: none;
+ border-radius: 6px;
}
ToolItem:first:hover,
@@ -2520,6 +2516,16 @@ FileUpload-FocusIndicator {
opacity: 1;
}
+/* Sidebar button styling */
+
+Composite.sidebarButton {
+ border-radius: 6px;
+}
+
+Label.sidebarButton {
+ border-radius: 6px;
+}
+
/* JFace specific theming */
Shell.jface_contentProposalPopup, Shell.jface_infoPopupDialog {
diff --git a/ui/src/main/java/org/apache/hop/ui/core/PropsUi.java
b/ui/src/main/java/org/apache/hop/ui/core/PropsUi.java
index 1f79a0f156..2c292f7fcf 100644
--- a/ui/src/main/java/org/apache/hop/ui/core/PropsUi.java
+++ b/ui/src/main/java/org/apache/hop/ui/core/PropsUi.java
@@ -569,51 +569,38 @@ public class PropsUi extends Props {
protected static void setLookOnWindows(final Widget widget, int style) {
final GuiResource gui = GuiResource.getInstance();
Font font = gui.getFontDefault();
- Color background = null;
- Color foreground = null;
+ Color background = GuiResource.getInstance().getWidgetBackGroundColor();
+ ;
+ Color foreground = gui.getColorBlack();
if (widget instanceof Shell shell) {
- background = gui.getColorWhite();
- foreground = gui.getColorBlack();
shell.setBackgroundMode(SWT.INHERIT_FORCE);
- shell.setForeground(gui.getColorBlack());
- shell.setBackground(gui.getColorWhite());
+ shell.setForeground(foreground);
+ shell.setBackground(background);
return;
}
switch (style) {
case WIDGET_STYLE_DEFAULT:
- background = gui.getColorWhite();
- foreground = gui.getColorBlack();
break;
case WIDGET_STYLE_FIXED:
font = gui.getFontFixed();
- background = gui.getColorWhite();
- foreground = gui.getColorBlack();
break;
case WIDGET_STYLE_TABLE:
if (PropsUi.getInstance().isDarkMode()) {
- background = gui.getColorWhite();
- foreground = gui.getColorBlack();
Table table = (Table) widget;
- table.setHeaderBackground(gui.getColorLightGray());
- table.setHeaderForeground(gui.getColorDarkGray());
+ table.setHeaderBackground(background);
+ table.setHeaderForeground(foreground);
}
break;
case WIDGET_STYLE_TREE:
if (PropsUi.getInstance().isDarkMode()) {
- background = gui.getColorWhite();
- foreground = gui.getColorBlack();
Tree tree = (Tree) widget;
- tree.setHeaderBackground(gui.getColorLightGray());
- tree.setHeaderForeground(gui.getColorDarkGray());
+ tree.setHeaderBackground(background);
+ tree.setHeaderForeground(foreground);
}
break;
case WIDGET_STYLE_TOOLBAR:
- if (PropsUi.getInstance().isDarkMode()) {
- background = gui.getColorLightGray();
- foreground = gui.getColorBlack();
- }
break;
case WIDGET_STYLE_TAB:
CTabFolder tabFolder = (CTabFolder) widget;
@@ -621,16 +608,16 @@ public class PropsUi extends Props {
tabFolder.setTabHeight(28);
ensureSafeRenderer(tabFolder);
if (PropsUi.getInstance().isDarkMode()) {
- tabFolder.setBackground(gui.getColorWhite());
- tabFolder.setForeground(gui.getColorBlack());
- tabFolder.setSelectionBackground(gui.getColorWhite());
- tabFolder.setSelectionForeground(gui.getColorBlack());
+ tabFolder.setBackground(background);
+ tabFolder.setForeground(foreground);
+ tabFolder.setSelectionBackground(background);
+ tabFolder.setSelectionForeground(foreground);
}
break;
case WIDGET_STYLE_PUSH_BUTTON:
break;
default:
- background = gui.getColorGray();
+ background = GuiResource.getInstance().getWidgetBackGroundColor();
font = null;
break;
}
@@ -655,22 +642,19 @@ public class PropsUi extends Props {
protected static void setLookOnMac(final Widget widget, int style) {
final GuiResource gui = GuiResource.getInstance();
Font font = gui.getFontDefault();
- Color background = null;
+ Color background = GuiResource.getInstance().getWidgetBackGroundColor();
Display display = Display.getCurrent();
if (display == null) {
return;
}
- // Use system colors that automatically adapt to light/dark mode
- // Only set backgrounds where needed - let macOS handle text colors
natively
- Color systemWidgetBackground =
display.getSystemColor(SWT.COLOR_WIDGET_BACKGROUND);
Color systemListBackground =
display.getSystemColor(SWT.COLOR_LIST_BACKGROUND);
Color systemListForeground =
display.getSystemColor(SWT.COLOR_LIST_FOREGROUND);
// Handle Shell windows with system background, but let macOS handle text
color
if (widget instanceof Shell shell) {
- shell.setBackground(systemWidgetBackground);
+ shell.setBackground(background);
shell.setBackgroundMode(SWT.INHERIT_FORCE);
// Don't set foreground - let macOS handle it natively
return;
@@ -680,10 +664,8 @@ public class PropsUi extends Props {
case WIDGET_STYLE_DEFAULT:
// Use system widget background for default composites
// Don't set foreground - let macOS handle text colors
- background = systemWidgetBackground;
break;
case WIDGET_STYLE_OSX_GROUP:
- background = systemWidgetBackground;
font = gui.getFontDefault();
Group group = ((Group) widget);
final Color groupBg = background;
@@ -698,25 +680,25 @@ public class PropsUi extends Props {
break;
case WIDGET_STYLE_FIXED:
font = gui.getFontFixed();
- background = systemWidgetBackground;
+ background = background;
break;
case WIDGET_STYLE_TABLE:
- background = systemWidgetBackground;
+ background = background;
Table table = (Table) widget;
- table.setHeaderBackground(systemWidgetBackground);
+ table.setHeaderBackground(background);
// Don't set foreground colors - let macOS handle them
break;
case WIDGET_STYLE_TREE:
- background = systemWidgetBackground;
+ background = background;
break;
case WIDGET_STYLE_TOOLBAR:
- background = systemWidgetBackground;
+ background = background;
break;
case WIDGET_STYLE_TAB:
CTabFolder tabFolder = (CTabFolder) widget;
tabFolder.setBorderVisible(true);
- tabFolder.setBackground(systemWidgetBackground);
- tabFolder.setSelectionBackground(systemWidgetBackground);
+ tabFolder.setBackground(background);
+ tabFolder.setSelectionBackground(background);
// Don't set foreground colors - let macOS handle them
ensureSafeRenderer(tabFolder);
break;
@@ -761,7 +743,7 @@ public class PropsUi extends Props {
protected static void setLookOnLinux(final Widget widget, int style) {
final GuiResource gui = GuiResource.getInstance();
Font font = gui.getFontDefault();
- Color background = gui.getColorWhite();
+ Color background = GuiResource.getInstance().getWidgetBackGroundColor();
Color foreground = gui.getColorBlack();
switch (style) {
diff --git a/ui/src/main/java/org/apache/hop/ui/core/gui/GuiResource.java
b/ui/src/main/java/org/apache/hop/ui/core/gui/GuiResource.java
index 230c2569d4..c3c7804542 100644
--- a/ui/src/main/java/org/apache/hop/ui/core/gui/GuiResource.java
+++ b/ui/src/main/java/org/apache/hop/ui/core/gui/GuiResource.java
@@ -1019,13 +1019,6 @@ public class GuiResource {
return imageLogo.getAsBitmapForSize(display, ConstUi.SMALL_ICON_SIZE,
ConstUi.SMALL_ICON_SIZE);
}
- /**
- * @return Returns the imagesTransforms.
- */
- public Map<String, SwtUniversalImage> getImagesTransforms() {
- return imagesTransforms;
- }
-
/**
* Get an image of an action or a missing image if not found.
*
@@ -1054,13 +1047,6 @@ public class GuiResource {
return image;
}
- /**
- * @return Returns the imagesActions.
- */
- public Map<String, SwtUniversalImage> getImagesActions() {
- return imagesActions;
- }
-
/**
* Return the image of the IValueMeta from plugin
*
@@ -1485,14 +1471,6 @@ public class GuiResource {
return color;
}
- /**
- * @return The image map used to cache images loaded from certain location
using getImage(String
- * location);
- */
- public Map<String, Image> getImageMap() {
- return imageMap;
- }
-
/**
* @return the imageTrue
*/
@@ -1625,4 +1603,14 @@ public class GuiResource {
public SwtUniversalImage getSwtImageArrowCandidate() {
return imageArrowCandidate;
}
+
+ public Color getWidgetBackGroundColor() {
+ if (OsHelper.isWindows()) {
+ return colorWhite;
+ } else if (OsHelper.isMac()) {
+ return display.getSystemColor(SWT.COLOR_WIDGET_BACKGROUND);
+ } else {
+ return colorLightGray;
+ }
+ }
}
diff --git a/ui/src/main/java/org/apache/hop/ui/hopgui/HopGui.java
b/ui/src/main/java/org/apache/hop/ui/hopgui/HopGui.java
index 9a246820ea..5b0d965386 100644
--- a/ui/src/main/java/org/apache/hop/ui/hopgui/HopGui.java
+++ b/ui/src/main/java/org/apache/hop/ui/hopgui/HopGui.java
@@ -133,10 +133,17 @@ import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.StackLayout;
import org.eclipse.swt.events.ShellAdapter;
import org.eclipse.swt.events.ShellEvent;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.layout.FormAttachment;
import org.eclipse.swt.layout.FormData;
import org.eclipse.swt.layout.FormLayout;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Canvas;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
@@ -259,7 +266,8 @@ public class HopGui
private ToolBar statusToolbar;
private GuiToolbarWidgets statusToolbarWidgets;
- private ToolBar perspectivesToolbar;
+ private Composite perspectivesSidebar;
+ private java.util.List<SidebarButton> sidebarButtons = new
java.util.ArrayList<>();
private Composite mainPerspectivesComposite;
private HopPerspectiveManager perspectiveManager;
private IHopPerspective activePerspective;
@@ -593,78 +601,29 @@ public class HopGui
perspective.getId());
ClassLoader classLoader =
pluginRegistry.getClassLoader(perspectivePlugin);
- ToolItem item;
- if (EnvironmentUtils.getInstance().isWeb()) {
- item =
- addWebToolbarButton(
- perspectivePlugin.getIds()[0],
- this.perspectivesToolbar,
- perspectivePlugin.getImageFile(),
- tooltip,
- // TODO: check if there is unnecessary refresh
- event -> {
- // Special handling for ExplorerPerspective: if already
active, toggle file
- // explorer panel instead of just activating
- if (perspective instanceof ExplorerPerspective
- && isActivePerspective(perspective)) {
- ((ExplorerPerspective)
perspective).toggleFileExplorerPanel();
- } else {
- // Normal perspective activation
- setActivePerspective(perspective);
- }
- });
- } else {
- item = new ToolItem(this.perspectivesToolbar, SWT.RADIO);
- item.setToolTipText(tooltip);
- item.addListener(
- SWT.Selection,
- event -> {
- // Event is sent first to the unselected tool item and then
the selected item.
- // To avoid unnecessary refresh, only process on the selected
item.
- if (item.getSelection()) {
- // Special handling for ExplorerPerspective: check if
control is already on top
- // (truly active and visible) before toggling
- if (perspective instanceof ExplorerPerspective
- && mainPerspectivesComposite != null
- && !mainPerspectivesComposite.isDisposed()) {
- StackLayout layout = (StackLayout)
mainPerspectivesComposite.getLayout();
- // Only toggle if the perspective control is already on
top (truly active)
- if (layout.topControl == perspective.getControl()) {
- ((ExplorerPerspective)
perspective).toggleFileExplorerPanel();
- return; // Don't call setActivePerspective
- }
- }
- // Normal perspective activation
- setActivePerspective(perspective);
- }
- });
- Image image =
- GuiResource.getInstance()
- .getImage(
- perspectivePlugin.getImageFile(),
- classLoader,
- ConstUi.SMALL_ICON_SIZE,
- ConstUi.SMALL_ICON_SIZE);
- if (image != null) {
- item.setImage(image);
- }
- }
- item.setData(perspective);
-
- // Store reference to ExplorerPerspective ToolItem for dynamic tooltip
updates
- if (perspective instanceof ExplorerPerspective) {
- explorerPerspectiveToolItem = item;
- }
-
+ int sidebarIconSize = 21;
+ Image image =
+ GuiResource.getInstance()
+ .getImage(
+ perspectivePlugin.getImageFile(),
+ classLoader,
+ sidebarIconSize,
+ sidebarIconSize);
// See if there's a shortcut for the perspective, add it to tooltip...
KeyboardShortcut shortcut =
GuiRegistry.getInstance()
.findKeyboardShortcut(perspectiveClass.getName(), "activate",
Const.isOSX());
+
if (shortcut != null) {
- item.setToolTipText(item.getToolTipText() + " (" + shortcut + ')');
+ tooltip += " (" + shortcut + ")";
}
+
+ // Create styled sidebar button with hover, selection, and rounded
corners
+ // This works for both desktop SWT and web/RAP modes
+ createStyledSidebarButton(perspectivesSidebar, image, tooltip,
perspective);
}
- perspectivesToolbar.pack();
+
+ perspectivesSidebar.layout(true, true);
} catch (Exception e) {
new ErrorDialog(shell, "Error", "Error loading perspectives", e);
}
@@ -1311,8 +1270,6 @@ public class HopGui
}
protected void addPerspectivesToolbar() {
- // We can't mix horizontal and vertical toolbars so we need to add a
composite.
- //
shell.setLayout(new FormLayout());
mainHopGuiComposite = new Composite(shell, SWT.NO_BACKGROUND);
mainHopGuiComposite.setLayout(new FormLayout());
@@ -1323,13 +1280,24 @@ public class HopGui
formData.bottom = new FormAttachment(statusToolbar, 0);
mainHopGuiComposite.setLayoutData(formData);
- perspectivesToolbar = new ToolBar(mainHopGuiComposite, SWT.WRAP |
SWT.RIGHT | SWT.VERTICAL);
- PropsUi.setLook(perspectivesToolbar, Props.WIDGET_STYLE_TOOLBAR);
- FormData fdToolBar = new FormData();
- fdToolBar.left = new FormAttachment(0, 0);
- fdToolBar.top = new FormAttachment(0, 0);
- fdToolBar.bottom = new FormAttachment(100, 0);
- perspectivesToolbar.setLayoutData(fdToolBar);
+ // Create custom sidebar composite instead of ToolBar for better control
+ perspectivesSidebar = new Composite(mainHopGuiComposite, SWT.NONE);
+ PropsUi.setLook(perspectivesSidebar);
+
+ // Use GridLayout for vertical stacking
+ org.eclipse.swt.layout.GridLayout sidebarLayout =
+ new org.eclipse.swt.layout.GridLayout(1, false);
+ sidebarLayout.marginWidth = 1;
+ sidebarLayout.marginHeight = 2;
+ sidebarLayout.verticalSpacing = 1; // Minimal spacing between buttons
+ perspectivesSidebar.setLayout(sidebarLayout);
+
+ FormData fdSidebar = new FormData();
+ fdSidebar.left = new FormAttachment(0, 0);
+ fdSidebar.top = new FormAttachment(0, 0);
+ fdSidebar.bottom = new FormAttachment(100, 0);
+ fdSidebar.width = (int) (40 * PropsUi.getNativeZoomFactor());
+ perspectivesSidebar.setLayoutData(fdSidebar);
}
/**
@@ -1341,7 +1309,7 @@ public class HopGui
mainPerspectivesComposite.setLayout(new StackLayout());
FormData fdMain = new FormData();
fdMain.top = new FormAttachment(0, 0);
- fdMain.left = new FormAttachment(perspectivesToolbar, 0);
+ fdMain.left = new FormAttachment(perspectivesSidebar, 0);
fdMain.bottom = new FormAttachment(100, 0);
fdMain.right = new FormAttachment(100, 0);
mainPerspectivesComposite.setLayoutData(fdMain);
@@ -1532,51 +1500,10 @@ public class HopGui
layout.topControl = perspective.getControl();
mainPerspectivesComposite.layout();
- // Select toolbar item and update tooltips
- //
- if (perspectivesToolbar != null && !perspectivesToolbar.isDisposed()) {
- for (ToolItem item : perspectivesToolbar.getItems()) {
- boolean shaded = perspective.equals(item.getData());
- if (EnvironmentUtils.getInstance().isWeb()) {
- SvgLabelFacade.shadeSvg((Label) item.getControl(), (String)
item.getData("id"), shaded);
- } else {
- item.setSelection(shaded);
- }
- }
+ // Selection is handled by updateSidebarButtonSelection()
- // Update ExplorerPerspective tooltip based on active state
- if (explorerPerspectiveToolItem != null &&
!explorerPerspectiveToolItem.isDisposed()) {
- boolean isExplorerActive = perspective instanceof ExplorerPerspective;
- String baseName =
- BaseMessages.getString(ExplorerPerspective.PKG,
"ExplorerPerspective.Name");
- String tooltipText;
-
- // Get keyboard shortcut if it exists
- KeyboardShortcut shortcut =
- GuiRegistry.getInstance()
- .findKeyboardShortcut(
- ExplorerPerspective.class.getName(), "activate",
Const.isOSX());
-
- if (isExplorerActive) {
- // Format: "File Explorer (Ctrl+Shift+E). Click the folder to
show/hide the project tree"
- String additionalText =
- BaseMessages.getString(ExplorerPerspective.PKG,
"ExplorerPerspective.Tooltip.Active");
- if (shortcut != null) {
- tooltipText = baseName + " (" + shortcut + "). " + additionalText;
- } else {
- tooltipText = baseName + ". " + additionalText;
- }
- } else {
- // Format: "File Explorer (Ctrl+Shift+E)"
- if (shortcut != null) {
- tooltipText = baseName + " (" + shortcut + ')';
- } else {
- tooltipText = baseName;
- }
- }
- explorerPerspectiveToolItem.setToolTipText(tooltipText);
- }
- }
+ // Update sidebar button selection states
+ updateSidebarButtonSelection(perspective);
// Notify the perspective that it has been activated.
//
@@ -1586,14 +1513,237 @@ public class HopGui
}
public boolean isActivePerspective(IHopPerspective perspective) {
- if (perspective != null) {
- for (ToolItem item : perspectivesToolbar.getItems()) {
- if (perspective.equals(item.getData())) {
- return item.getSelection();
+ return activePerspective != null && activePerspective.equals(perspective);
+ }
+
+ /** Update the visual selection state of sidebar buttons when perspective
changes. */
+ private void updateSidebarButtonSelection(IHopPerspective activePerspective)
{
+ for (SidebarButton button : sidebarButtons) {
+ button.setSelected(button.perspective.equals(activePerspective));
+ }
+ }
+
+ /**
+ * Create a styled sidebar button with modern appearance. Features rounded
corners, hover effects,
+ * and selection colors.
+ */
+ private void createStyledSidebarButton(
+ Composite parent, Image image, String tooltip, IHopPerspective
perspective) {
+
+ SidebarButton button = new SidebarButton(parent, image, tooltip,
perspective);
+ sidebarButtons.add(button);
+
+ org.eclipse.swt.layout.GridData gd = new org.eclipse.swt.layout.GridData();
+ gd.widthHint = (int) (34 * PropsUi.getNativeZoomFactor());
+ gd.heightHint = (int) (34 * PropsUi.getNativeZoomFactor());
+ button.composite.setLayoutData(gd);
+ }
+
+ /** Custom sidebar button class with hover, selection, and rounded corners */
+ private class SidebarButton {
+ Control composite; // Use Control to allow both Composite (RAP) and Canvas
(desktop)
+ Image image;
+ IHopPerspective perspective;
+ boolean isHovered = false;
+ boolean isSelected = false;
+
+ Color selectionBg = GuiResource.getInstance().getColorLightBlue();
+ Color hoverBg = GuiResource.getInstance().getColorLightGray();
+ Color normalBg = GuiResource.getInstance().getWidgetBackGroundColor();
+
+ public SidebarButton(
+ Composite parent, Image image, String tooltip, IHopPerspective
perspective) {
+ this.image = image;
+ this.perspective = perspective;
+
+ // Create a label inside the composite to display the image (only for
RAP)
+ final Label imageLabel;
+ if (EnvironmentUtils.getInstance().isWeb()) {
+ // For RAP/web: use Composite with Label child
+ Composite comp = new Composite(parent, SWT.NONE);
+ composite = comp;
+ comp.setToolTipText(tooltip);
+ comp.setBackground(normalBg);
+
+ // Set custom variant for CSS styling in RAP
+ comp.setData("org.eclipse.rap.rwt.customVariant", "sidebarButton");
+
+ // Use GridLayout to center the image without stretching
+ GridLayout layout = new GridLayout(1, false);
+ layout.marginWidth = 0;
+ layout.marginHeight = 0;
+ layout.horizontalSpacing = 0;
+ layout.verticalSpacing = 0;
+ comp.setLayout(layout);
+
+ imageLabel = new Label(comp, SWT.NONE);
+ imageLabel.setImage(image);
+ imageLabel.setBackground(normalBg);
+ imageLabel.setToolTipText(tooltip);
+ imageLabel.setData("org.eclipse.rap.rwt.customVariant",
"sidebarButton");
+
+ // Center the label in the composite
+ GridData gd = new GridData(SWT.CENTER, SWT.CENTER, true, true);
+ imageLabel.setLayoutData(gd);
+ } else {
+ Canvas canvas = new Canvas(parent, SWT.NONE);
+ composite = canvas;
+ canvas.setToolTipText(tooltip);
+ canvas.setBackground(normalBg);
+ imageLabel = null;
+ }
+
+ // Update background colors method for both RAP and desktop
+ final Runnable updateColors =
+ () -> {
+ if (EnvironmentUtils.getInstance().isWeb()) {
+ // For RAP, update composite background color (rounded corners
applied via CSS)
+ Color bgColor;
+ if (isSelected) {
+ bgColor = selectionBg;
+ } else if (isHovered) {
+ bgColor = hoverBg;
+ } else {
+ bgColor = normalBg;
+ }
+ composite.setBackground(bgColor);
+ // Keep label background transparent/matching to show composite
background
+ if (imageLabel != null) {
+ imageLabel.setBackground(bgColor);
+ }
+ // Force redraw in RAP
+ composite.redraw();
+ if (composite instanceof Composite) {
+ ((Composite) composite).layout();
+ }
+ } else {
+ // For desktop Canvas, trigger repaint
+ composite.redraw();
+ }
+ };
+
+ // Only add paint listener for desktop SWT (RAP doesn't support it)
+ if (!EnvironmentUtils.getInstance().isWeb()) {
+ composite.addPaintListener(
+ e -> {
+ GC gc = e.gc;
+ Point size = composite.getSize();
+
+ gc.setAntialias(SWT.ON);
+
+ // Choose background color
+ if (isSelected) {
+ gc.setBackground(selectionBg);
+ } else if (isHovered) {
+ gc.setBackground(hoverBg);
+ } else {
+ gc.setBackground(normalBg);
+ }
+
+ // Fill rounded rectangle
+ gc.fillRoundRectangle(4, 4, size.x - 8, size.y - 8, 8, 8);
+
+ // Draw image centered
+ if (image != null && !image.isDisposed()) {
+ Rectangle imgBounds = image.getBounds();
+ int x = (size.x - imgBounds.width) / 2;
+ int y = (size.y - imgBounds.height) / 2;
+ gc.drawImage(image, x, y);
+ }
+ });
+ }
+
+ // Mouse listeners
+ composite.addListener(
+ SWT.MouseEnter,
+ e -> {
+ isHovered = true;
+ updateColors.run();
+ });
+
+ composite.addListener(
+ SWT.MouseExit,
+ e -> {
+ isHovered = false;
+ updateColors.run();
+ });
+
+ composite.addListener(
+ SWT.MouseDown,
+ e -> {
+ // Deselect all other buttons
+ for (SidebarButton btn : sidebarButtons) {
+ btn.setSelected(false);
+ }
+
+ // Select this button
+ setSelected(true);
+
+ // Handle perspective activation
+ if (perspective instanceof ExplorerPerspective explorerPerspective
+ && mainPerspectivesComposite != null
+ && !mainPerspectivesComposite.isDisposed()) {
+ StackLayout layout = (StackLayout)
mainPerspectivesComposite.getLayout();
+ if (layout.topControl == explorerPerspective.getControl()) {
+ explorerPerspective.toggleFileExplorerPanel();
+ return;
+ }
+ }
+ setActivePerspective(perspective);
+ });
+
+ // Also attach listeners to the image label for better hit detection
(RAP only)
+ if (imageLabel != null) {
+ imageLabel.addListener(
+ SWT.MouseEnter,
+ e -> {
+ isHovered = true;
+ updateColors.run();
+ });
+ imageLabel.addListener(
+ SWT.MouseExit,
+ e -> {
+ isHovered = false;
+ updateColors.run();
+ });
+ imageLabel.addListener(
+ SWT.MouseDown,
+ e -> {
+ // Deselect all other buttons
+ for (SidebarButton btn : sidebarButtons) {
+ btn.setSelected(false);
+ }
+
+ // Select this button
+ setSelected(true);
+
+ // Handle perspective activation
+ if (perspective instanceof ExplorerPerspective
explorerPerspective
+ && mainPerspectivesComposite != null
+ && !mainPerspectivesComposite.isDisposed()) {
+ StackLayout layout = (StackLayout)
mainPerspectivesComposite.getLayout();
+ if (layout.topControl == explorerPerspective.getControl()) {
+ explorerPerspective.toggleFileExplorerPanel();
+ return;
+ }
+ }
+ setActivePerspective(perspective);
+ });
+ }
+
+ // Store the update method for later use
+ composite.setData("updateColors", updateColors);
+ }
+
+ public void setSelected(boolean selected) {
+ this.isSelected = selected;
+ if (!composite.isDisposed()) {
+ Runnable updateColors = (Runnable) composite.getData("updateColors");
+ if (updateColors != null) {
+ updateColors.run();
}
}
}
- return false;
}
@GuiKeyboardShortcut(key = SWT.F1)
diff --git
a/ui/src/main/java/org/apache/hop/ui/hopgui/perspective/explorer/ExplorerPerspective.java
b/ui/src/main/java/org/apache/hop/ui/hopgui/perspective/explorer/ExplorerPerspective.java
index 130b0d8a1d..3990d52213 100644
---
a/ui/src/main/java/org/apache/hop/ui/hopgui/perspective/explorer/ExplorerPerspective.java
+++
b/ui/src/main/java/org/apache/hop/ui/hopgui/perspective/explorer/ExplorerPerspective.java
@@ -231,8 +231,8 @@ public class ExplorerPerspective implements
IHopPerspective, TabClosable {
return "explorer-perspective";
}
- @GuiKeyboardShortcut(control = true, shift = true, key = 'e')
- @GuiOsxKeyboardShortcut(command = true, shift = true, key = 'e')
+ @GuiKeyboardShortcut(control = true, shift = true, key = 'd')
+ @GuiOsxKeyboardShortcut(command = true, shift = true, key = 'd')
@Override
public void activate() {
hopGui.setActivePerspective(this);
@@ -269,7 +269,7 @@ public class ExplorerPerspective implements
IHopPerspective, TabClosable {
createTree(sash);
createTabFolder(sash);
- sash.setWeights(new int[] {20, 80});
+ sash.setWeights(20, 80);
// Set initial file explorer panel visibility from configuration
Boolean visibleByDefault =
diff --git
a/ui/src/main/java/org/apache/hop/ui/hopgui/perspective/search/HopSearchPerspective.java
b/ui/src/main/java/org/apache/hop/ui/hopgui/perspective/search/HopSearchPerspective.java
index 13ec9fe713..22c43cae55 100644
---
a/ui/src/main/java/org/apache/hop/ui/hopgui/perspective/search/HopSearchPerspective.java
+++
b/ui/src/main/java/org/apache/hop/ui/hopgui/perspective/search/HopSearchPerspective.java
@@ -24,6 +24,8 @@ import java.util.List;
import java.util.Map;
import org.apache.hop.core.Const;
import org.apache.hop.core.gui.plugin.GuiPlugin;
+import org.apache.hop.core.gui.plugin.key.GuiKeyboardShortcut;
+import org.apache.hop.core.gui.plugin.key.GuiOsxKeyboardShortcut;
import org.apache.hop.core.plugins.IPlugin;
import org.apache.hop.core.plugins.PluginRegistry;
import org.apache.hop.core.search.ISearchResult;
@@ -93,6 +95,8 @@ public class HopSearchPerspective implements IHopPerspective {
}
@Override
+ @GuiKeyboardShortcut(control = true, key = 'f')
+ @GuiOsxKeyboardShortcut(command = true, key = 'f')
public void activate() {
// Someone clicked on the search icon of used CTRL-F
//