fix(gui): limit tabs title length, fix tooltips (#2771)

This commit is contained in:
Skylot
2026-02-02 21:09:15 +00:00
parent c1fc73a524
commit 07b3e5a7c0
5 changed files with 62 additions and 38 deletions
@@ -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();
@@ -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.
* <p>
* If <code>null</code> 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();
}
@@ -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();
}
}
@@ -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) {
@@ -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("<", "&lt;").replace(">", "&gt;");
}
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";