From 07b3e5a7c068142e2ff75f351793c65d57cee112 Mon Sep 17 00:00:00 2001 From: Skylot <118523+skylot@users.noreply.github.com> Date: Mon, 2 Feb 2026 21:09:15 +0000 Subject: [PATCH] fix(gui): limit tabs title length, fix tooltips (#2771) --- .../gui/ui/codearea/CodeContentPanel.java | 15 ----------- .../java/jadx/gui/ui/panel/ContentPanel.java | 17 ------------ .../java/jadx/gui/ui/tab/TabComponent.java | 25 ++++++++++++----- .../main/java/jadx/gui/ui/tab/TabbedPane.java | 16 +++++++++++ .../src/main/java/jadx/gui/utils/UiUtils.java | 27 +++++++++++++++++++ 5 files changed, 62 insertions(+), 38 deletions(-) diff --git a/jadx-gui/src/main/java/jadx/gui/ui/codearea/CodeContentPanel.java b/jadx-gui/src/main/java/jadx/gui/ui/codearea/CodeContentPanel.java index 0ddab125a..217de2d1b 100644 --- a/jadx-gui/src/main/java/jadx/gui/ui/codearea/CodeContentPanel.java +++ b/jadx-gui/src/main/java/jadx/gui/ui/codearea/CodeContentPanel.java @@ -46,21 +46,6 @@ public final class CodeContentPanel extends AbstractCodeContentPanel implements return getCodeArea(); } - @Override - public String getTabTooltip() { - String s = node.getName(); - JNode n = (JNode) node.getParent(); - while (n != null) { - String name = n.getName(); - if (name == null) { - break; - } - s = name + '/' + s; - n = (JNode) n.getParent(); - } - return '/' + s; - } - @Override public void saveEditorViewState(EditorViewState viewState) { int caretPos = codePanel.getCodeArea().getCaretPosition(); diff --git a/jadx-gui/src/main/java/jadx/gui/ui/panel/ContentPanel.java b/jadx-gui/src/main/java/jadx/gui/ui/panel/ContentPanel.java index 0d1673ba1..fcb14017b 100644 --- a/jadx-gui/src/main/java/jadx/gui/ui/panel/ContentPanel.java +++ b/jadx-gui/src/main/java/jadx/gui/ui/panel/ContentPanel.java @@ -2,12 +2,10 @@ package jadx.gui.ui.panel; import javax.swing.JPanel; -import org.jetbrains.annotations.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import jadx.gui.settings.JadxSettings; -import jadx.gui.treemodel.JClass; import jadx.gui.treemodel.JNode; import jadx.gui.ui.MainWindow; import jadx.gui.ui.tab.TabbedPane; @@ -47,21 +45,6 @@ public abstract class ContentPanel extends JPanel { LOG.warn("ContentPanel.scrollToPos method not implemented, class: {}", getClass().getSimpleName()); } - /** - * Allows to show a tool tip on the tab e.g. for displaying a long path of the - * selected entry inside the APK file. - *
- * If null is returned no tool tip will be displayed.
- */
- @Nullable
- public String getTabTooltip() {
- JClass jClass = getNode().getRootClass();
- if (jClass != null) {
- return jClass.getFullName();
- }
- return getNode().getName();
- }
-
public JadxSettings getSettings() {
return tabbedPane.getMainWindow().getSettings();
}
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 8f9407f76..3db7e430d 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
@@ -39,6 +39,8 @@ import jadx.gui.utils.ui.NodeLabel;
public class TabComponent extends JPanel {
private static final long serialVersionUID = -8147035487543610321L;
+ private static final int TAB_TITLE_MAX_LENGTH = 30;
+
private final TabbedPane tabbedPane;
private final TabsController tabsController;
private final ContentPanel contentPanel;
@@ -81,7 +83,7 @@ public class TabComponent extends JPanel {
icon = new OverlayIcon(node.getIcon());
label = new NodeLabel(buildTabTitle(node), node.disableHtml());
- String toolTip = contentPanel.getTabTooltip();
+ String toolTip = contentPanel.getNode().getTooltip();
if (toolTip != null) {
setToolTipText(toolTip);
}
@@ -208,11 +210,18 @@ public class TabComponent extends JPanel {
}
private String buildTabTitle(JNode node) {
- String tabTitle;
- if (node.getRootClass() != null) {
- tabTitle = node.getRootClass().getName();
- } else {
- tabTitle = node.makeLongStringHtml();
+ String tabTitle = node.makeStringHtml();
+ if (tabbedPane.tabWithTitleExists(tabTitle)) {
+ tabTitle = node.makeLongString();
+ }
+ String newTabTitle = UiUtils.limitStringLength(tabTitle, TAB_TITLE_MAX_LENGTH);
+ if (!newTabTitle.equals(tabTitle)) {
+ if (tabbedPane.tabWithTitleExists(newTabTitle)) {
+ // shorter version also exist => make longer version (last try)
+ tabTitle = UiUtils.limitStringLength(tabTitle, (int) (TAB_TITLE_MAX_LENGTH * 1.2));
+ } else {
+ tabTitle = newTabTitle;
+ }
}
if (node instanceof JEditableNode) {
if (((JEditableNode) node).isChanged()) {
@@ -361,4 +370,8 @@ public class TabComponent extends JPanel {
public JNode getNode() {
return contentPanel.getNode();
}
+
+ public String getTabTitle() {
+ return label.getText();
+ }
}
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 ef5b1404e..6dff674b2 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
@@ -315,6 +315,22 @@ public class TabbedPane extends JTabbedPane implements ITabStatesListener {
return (TabComponent) component;
}
+ public boolean tabWithTitleExists(String tabTitle) {
+ try {
+ for (int i = 0; i < getTabCount(); i++) {
+ Component component = getTabComponentAt(i);
+ if (component instanceof TabComponent) {
+ if (((TabComponent) component).getTabTitle().equals(tabTitle)) {
+ return true;
+ }
+ }
+ }
+ } catch (Exception e) {
+ LOG.warn("Failed to check tabs titles", e);
+ }
+ return false;
+ }
+
public void refresh(JNode node) {
ContentPanel panel = getTabByNode(node);
if (panel != null) {
diff --git a/jadx-gui/src/main/java/jadx/gui/utils/UiUtils.java b/jadx-gui/src/main/java/jadx/gui/utils/UiUtils.java
index 69e618a73..5ff3bc831 100644
--- a/jadx-gui/src/main/java/jadx/gui/utils/UiUtils.java
+++ b/jadx-gui/src/main/java/jadx/gui/utils/UiUtils.java
@@ -15,6 +15,7 @@ import java.awt.event.ActionEvent;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
+import java.io.File;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
@@ -158,6 +159,32 @@ public class UiUtils {
return str.replace("<", "<").replace(">", ">");
}
+ private static final String CUT_STR_REPLACE = "...";
+
+ public static String limitStringLength(String str, int maxLength) {
+ if (str.length() <= maxLength) {
+ return str;
+ }
+ char fileSepChar = File.separatorChar;
+ int firstFileSep = str.indexOf(fileSepChar);
+ if (firstFileSep != -1) {
+ // remove path parts
+ int lastFileSep = str.lastIndexOf(fileSepChar);
+ if (firstFileSep == lastFileSep) {
+ // single path char => cut before
+ str = CUT_STR_REPLACE + str.substring(lastFileSep - 1);
+ } else {
+ // cut middle
+ str = str.substring(0, firstFileSep + 1) + CUT_STR_REPLACE + str.substring(lastFileSep);
+ }
+ if (str.length() < maxLength) {
+ return str;
+ }
+ }
+ // cut end by default
+ return str.substring(0, maxLength - CUT_STR_REPLACE.length()) + CUT_STR_REPLACE;
+ }
+
public static String typeStr(ArgType type) {
if (type == null) {
return "null";