From da3ac6bff050fba428d612a0b1e249faa04187cc Mon Sep 17 00:00:00 2001 From: Andrei Kudryavtsev Date: Mon, 4 Aug 2025 22:09:22 +0500 Subject: [PATCH] fix(gui): various tabs related fixes (PR #2595) * 1. Fix tab rendering when preview state changed * 2. Fix selected tab after closing currently active tab * 3. Fix tab selection when pinning currently active tab * 4. Make current preview tab permanent on double click * 5. Fix preview tab font not reloading on settings change --- .../jadx/gui/ui/tab/ITabStatesListener.java | 2 + .../java/jadx/gui/ui/tab/LogTabStates.java | 5 +++ .../java/jadx/gui/ui/tab/TabComponent.java | 36 +++++++++------- .../main/java/jadx/gui/ui/tab/TabbedPane.java | 42 +++++++++++++++---- .../java/jadx/gui/ui/tab/TabsController.java | 12 ++++++ 5 files changed, 76 insertions(+), 21 deletions(-) diff --git a/jadx-gui/src/main/java/jadx/gui/ui/tab/ITabStatesListener.java b/jadx-gui/src/main/java/jadx/gui/ui/tab/ITabStatesListener.java index f1eeae03a..2e537ff88 100644 --- a/jadx-gui/src/main/java/jadx/gui/ui/tab/ITabStatesListener.java +++ b/jadx-gui/src/main/java/jadx/gui/ui/tab/ITabStatesListener.java @@ -63,4 +63,6 @@ public interface ITabStatesListener { default void onTabSave(TabBlueprint blueprint, EditorViewState viewState) { } + default void onTabPreviewChange(TabBlueprint blueprint) { + } } diff --git a/jadx-gui/src/main/java/jadx/gui/ui/tab/LogTabStates.java b/jadx-gui/src/main/java/jadx/gui/ui/tab/LogTabStates.java index 441f41084..33e1d4127 100644 --- a/jadx-gui/src/main/java/jadx/gui/ui/tab/LogTabStates.java +++ b/jadx-gui/src/main/java/jadx/gui/ui/tab/LogTabStates.java @@ -79,4 +79,9 @@ public class LogTabStates implements ITabStatesListener { public void onTabVisibilityChange(TabBlueprint blueprint) { LOG.debug("onTabVisibilityChange: blueprint={}", blueprint); } + + @Override + public void onTabPreviewChange(TabBlueprint blueprint) { + LOG.debug("onTabPreviewChange: blueprint={}", blueprint); + } } diff --git a/jadx-gui/src/main/java/jadx/gui/ui/tab/TabComponent.java b/jadx-gui/src/main/java/jadx/gui/ui/tab/TabComponent.java index 86fd20f8d..e2451ad58 100644 --- a/jadx-gui/src/main/java/jadx/gui/ui/tab/TabComponent.java +++ b/jadx-gui/src/main/java/jadx/gui/ui/tab/TabComponent.java @@ -64,7 +64,13 @@ public class TabComponent extends JPanel { } private Font getLabelFont() { - return tabsController.getMainWindow().getSettings().getFont().deriveFont(Font.BOLD); + Font font = tabsController.getMainWindow().getSettings().getFont(); + int style = font.getStyle(); + style |= Font.BOLD; + if (getBlueprint().isPreviewTab()) { + style ^= Font.ITALIC; // flip italic bit to distinguish preview + } + return font.deriveFont(style); } private void init() { @@ -75,14 +81,12 @@ public class TabComponent extends JPanel { icon = new OverlayIcon(node.getIcon()); label = new NodeLabel(buildTabTitle(node), node.disableHtml()); - makeLabelFont(); String toolTip = contentPanel.getTabTooltip(); if (toolTip != null) { setToolTipText(toolTip); } label.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 10)); label.setIcon(icon); - updateBookmarkIcon(); if (node instanceof JEditableNode) { ((JEditableNode) node).addChangeListener(c -> label.setText(buildTabTitle(node))); } @@ -122,6 +126,9 @@ public class TabComponent extends JPanel { menu.show(e.getComponent(), e.getX(), e.getY()); } else if (SwingUtilities.isLeftMouseButton(e)) { tabsController.selectTab(node); + if (e.getClickCount() == 2) { + tabsController.setTabPreview(node, false); + } } } }; @@ -129,11 +136,18 @@ public class TabComponent extends JPanel { addListenerForDnd(); add(label); - updateCloseOrPinButton(); setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0)); + + update(); } - public void updateCloseOrPinButton() { + public void update() { + updateCloseOrPinButton(); + updateBookmarkIcon(); + updateFont(); + } + + private void updateCloseOrPinButton() { if (getBlueprint().isPinned()) { if (closeBtn.isShowing()) { remove(closeBtn); @@ -151,7 +165,7 @@ public class TabComponent extends JPanel { } } - public void updateBookmarkIcon() { + private void updateBookmarkIcon() { icon.clear(); if (getBlueprint().isBookmarked()) { @@ -174,14 +188,8 @@ public class TabComponent extends JPanel { tabsController.setTabBookmarked(getNode(), bookmarked); } - private void makeLabelFont() { - boolean previewTab = getBlueprint().isPreviewTab(); - if (previewTab) { - Font newLabelFont = new Font(label.getFont().getName(), Font.ITALIC, label.getFont().getSize()); - label.setFont(newLabelFont); - } else { - label.setFont(getLabelFont()); - } + private void updateFont() { + label.setFont(getLabelFont()); } private void addListenerForDnd() { diff --git a/jadx-gui/src/main/java/jadx/gui/ui/tab/TabbedPane.java b/jadx-gui/src/main/java/jadx/gui/ui/tab/TabbedPane.java index 06a888d97..96a967d8c 100644 --- a/jadx-gui/src/main/java/jadx/gui/ui/tab/TabbedPane.java +++ b/jadx-gui/src/main/java/jadx/gui/ui/tab/TabbedPane.java @@ -442,13 +442,28 @@ public class TabbedPane extends JTabbedPane implements ITabStatesListener { @Override public void onTabClose(TabBlueprint blueprint) { - ContentPanel contentPanel = getTabByNode(blueprint.getNode()); - if (contentPanel == null) { + ContentPanel contentPanelToClose = getTabByNode(blueprint.getNode()); + if (contentPanelToClose == null) { return; } - tabsMap.remove(contentPanel.getNode()); - remove(contentPanel); - contentPanel.dispose(); + + ContentPanel currentContentPanel = getSelectedContentPanel(); + if (currentContentPanel == contentPanelToClose) { + if (lastTab != null && lastTab.getNode() != null) { + selectTab(lastTab); + } else if (getTabCount() > 1) { + int removalIdx = indexOfComponent(contentPanelToClose); + if (removalIdx > 0) { // select left tab + setSelectedIndex(removalIdx - 1); + } else if (removalIdx == 0) { // select right tab + setSelectedIndex(removalIdx + 1); + } + } + } + + tabsMap.remove(contentPanelToClose.getNode()); + remove(contentPanelToClose); + contentPanelToClose.dispose(); } @Override @@ -465,9 +480,13 @@ public class TabbedPane extends JTabbedPane implements ITabStatesListener { if (tabComponent == null) { return; } + boolean restoreSelection = contentPanel == getSelectedContentPanel(); remove(contentPanel); add(contentPanel, position); setTabComponentAt(position, tabComponent); + if (restoreSelection) { + setSelectedIndex(position); + } } @Override @@ -476,7 +495,7 @@ public class TabbedPane extends JTabbedPane implements ITabStatesListener { if (tabComponent == null) { return; } - tabComponent.updateCloseOrPinButton(); + tabComponent.update(); } @Override @@ -485,7 +504,7 @@ public class TabbedPane extends JTabbedPane implements ITabStatesListener { if (tabComponent == null) { return; } - tabComponent.updateBookmarkIcon(); + tabComponent.update(); } @Override @@ -498,6 +517,15 @@ public class TabbedPane extends JTabbedPane implements ITabStatesListener { } } + @Override + public void onTabPreviewChange(TabBlueprint blueprint) { + TabComponent tabComponent = getTabComponentByNode(blueprint.getNode()); + if (tabComponent == null) { + return; + } + tabComponent.update(); + } + @Override public void onTabRestore(TabBlueprint blueprint, EditorViewState viewState) { ContentPanel contentPanel = getTabByNode(blueprint.getNode()); diff --git a/jadx-gui/src/main/java/jadx/gui/ui/tab/TabsController.java b/jadx-gui/src/main/java/jadx/gui/ui/tab/TabsController.java index ef4f93f25..5d756f752 100644 --- a/jadx-gui/src/main/java/jadx/gui/ui/tab/TabsController.java +++ b/jadx-gui/src/main/java/jadx/gui/ui/tab/TabsController.java @@ -281,6 +281,18 @@ public class TabsController { } } + public void setTabPreview(JNode node, boolean isPreview) { + TabBlueprint blueprint = getTabByNode(node); + setTabPreviewInternal(blueprint, isPreview); + } + + private void setTabPreviewInternal(TabBlueprint blueprint, boolean isPreview) { + if (blueprint != null && blueprint.isPreviewTab() != isPreview) { + blueprint.setPreviewTab(isPreview); + listeners.forEach(l -> l.onTabPreviewChange(blueprint)); + } + } + private void removeTabIfNotReferenced(TabBlueprint blueprint) { if (blueprint.isHidden() && !blueprint.isReferenced()) { tabsMap.remove(blueprint.getNode());