mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2026-06-02 01:37:11 +08:00
Merge remote-tracking branch 'origin/GP-6323-dragonmacher-program-tabs-focus--SQUASHED'
This commit is contained in:
+2
@@ -41,4 +41,6 @@ public interface DebuggerProgramLocationActionContext extends ActionContext {
|
|||||||
Address getAddress();
|
Address getAddress();
|
||||||
|
|
||||||
CodeUnit getCodeUnit();
|
CodeUnit getCodeUnit();
|
||||||
|
|
||||||
|
boolean isActiveProgram();
|
||||||
}
|
}
|
||||||
|
|||||||
+14
@@ -55,4 +55,18 @@ public class DebuggerListingActionContext extends ListingActionContext
|
|||||||
|
|
||||||
return super.hasSelection();
|
return super.hasSelection();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Overridden to signal that this navigatable's program may not be the same as the globally
|
||||||
|
* active program. This is done to signal that this navigatable can supply default context.
|
||||||
|
*
|
||||||
|
* @return false
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean isActiveProgram() {
|
||||||
|
// The active program for the debugger listing is the on in the 'main listing'. We cannot
|
||||||
|
// use Navigatable.isConnected() here, since that always returns false for the debugger.
|
||||||
|
DebuggerListingProvider dlp = (DebuggerListingProvider) getComponentProvider();
|
||||||
|
return dlp.isMainListing();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+49
-5
@@ -33,8 +33,7 @@ import javax.swing.event.ChangeListener;
|
|||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.jdom.Element;
|
import org.jdom.Element;
|
||||||
|
|
||||||
import docking.ActionContext;
|
import docking.*;
|
||||||
import docking.WindowPosition;
|
|
||||||
import docking.action.DockingAction;
|
import docking.action.DockingAction;
|
||||||
import docking.action.ToggleDockingAction;
|
import docking.action.ToggleDockingAction;
|
||||||
import docking.action.builder.ToggleActionBuilder;
|
import docking.action.builder.ToggleActionBuilder;
|
||||||
@@ -173,15 +172,15 @@ public class DebuggerListingProvider extends CodeViewerProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void specChanged(LocationTrackingSpec spec) {
|
protected void specChanged(LocationTrackingSpec lts) {
|
||||||
if (isMainListing()) {
|
if (isMainListing()) {
|
||||||
plugin.firePluginEvent(new TrackingChangedPluginEvent(getName(), spec));
|
plugin.firePluginEvent(new TrackingChangedPluginEvent(getName(), lts));
|
||||||
}
|
}
|
||||||
updateTitle();
|
updateTitle();
|
||||||
trackingLabel.setText("");
|
trackingLabel.setText("");
|
||||||
trackingLabel.setToolTipText("");
|
trackingLabel.setToolTipText("");
|
||||||
trackingLabel.setForeground(Colors.FOREGROUND);
|
trackingLabel.setForeground(Colors.FOREGROUND);
|
||||||
trackingSpecChangeListeners.invoke().locationTrackingSpecChanged(spec);
|
trackingSpecChangeListeners.invoke().locationTrackingSpecChanged(lts);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -348,6 +347,8 @@ public class DebuggerListingProvider extends CodeViewerProvider {
|
|||||||
|
|
||||||
private long countAddressesInIndex;
|
private long countAddressesInIndex;
|
||||||
|
|
||||||
|
private TabContextListener contextListener;
|
||||||
|
|
||||||
public DebuggerListingProvider(DebuggerListingPlugin plugin, FormatManager formatManager,
|
public DebuggerListingProvider(DebuggerListingPlugin plugin, FormatManager formatManager,
|
||||||
boolean isConnected) {
|
boolean isConnected) {
|
||||||
super(plugin, formatManager, isConnected);
|
super(plugin, formatManager, isConnected);
|
||||||
@@ -379,6 +380,9 @@ public class DebuggerListingProvider extends CodeViewerProvider {
|
|||||||
|
|
||||||
if (isConnected) {
|
if (isConnected) {
|
||||||
traceTabs = new DebuggerTraceTabPanel(plugin);
|
traceTabs = new DebuggerTraceTabPanel(plugin);
|
||||||
|
contextListener = new TabContextListener();
|
||||||
|
DockingWindowManager dwm = tool.getWindowManager();
|
||||||
|
dwm.addContextListener(contextListener);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
traceTabs = null;
|
traceTabs = null;
|
||||||
@@ -1049,4 +1053,44 @@ public class DebuggerListingProvider extends CodeViewerProvider {
|
|||||||
}
|
}
|
||||||
return new DebuggerByteSource(tool, current.getView(), current.getTarget(), readsMemTrait);
|
return new DebuggerByteSource(tool, current.getView(), current.getTarget(), readsMemTrait);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class TabContextListener implements DockingContextListener {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void contextChanged(ActionContext localContext) {
|
||||||
|
|
||||||
|
DockingWindowManager dwm = tool.getWindowManager();
|
||||||
|
DebuggerProgramLocationActionContext defaultContext =
|
||||||
|
(DebuggerProgramLocationActionContext) dwm
|
||||||
|
.getDefaultActionContext(DebuggerProgramLocationActionContext.class);
|
||||||
|
Trace myTrace = null;
|
||||||
|
if (defaultContext != null) {
|
||||||
|
TraceProgramView tpv = defaultContext.getProgram();
|
||||||
|
myTrace = tpv.getTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(localContext instanceof DebuggerProgramLocationActionContext dlac)) {
|
||||||
|
|
||||||
|
// Future: We would like to make the debugger be the default context in this case,
|
||||||
|
// but we need a way to have the static and dynamic views to decide who is in charge.
|
||||||
|
// For now, assume it should always be the static non-debugger listing view, which
|
||||||
|
// means making the trace tabs inactive.
|
||||||
|
traceTabs.setActive(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
TraceProgramView localTraceProgramView = dlac.getProgram();
|
||||||
|
Trace localTrace = localTraceProgramView.getTrace();
|
||||||
|
if (myTrace != localTrace || !dlac.isActiveProgram()) {
|
||||||
|
// A different trace is in the local context; deactivate out tabs.
|
||||||
|
traceTabs.setActive(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Signal that the trace from our default context is the active trace.
|
||||||
|
traceTabs.setActive(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,15 +11,6 @@ color.bg.listing.highlighter.middle.mouse = color.palette.yellow
|
|||||||
color.bg.listing.highlighter.scoped.read = color.palette.darkkhaki
|
color.bg.listing.highlighter.scoped.read = color.palette.darkkhaki
|
||||||
color.bg.listing.highlighter.scoped.write = color.palette.lightgreen
|
color.bg.listing.highlighter.scoped.write = color.palette.lightgreen
|
||||||
|
|
||||||
color.bg.listing.tabs.selected = [color]system.color.bg.selected.view
|
|
||||||
color.bg.listing.tabs.unselected = [color]system.color.bg.control
|
|
||||||
color.bg.listing.tabs.highlighted = color.palette.lightcornflowerblue
|
|
||||||
color.bg.listing.tabs.list = [color]system.color.bg.tooltip
|
|
||||||
color.bg.listing.tabs.more.tabs.hover = color.bg.listing.tabs.selected
|
|
||||||
color.fg.listing.tabs.text.selected = [color]system.color.fg.selected.view
|
|
||||||
color.fg.listing.tabs.text.unselected = color.fg
|
|
||||||
color.fg.listing.tabs.list = color.fg
|
|
||||||
|
|
||||||
color.bg.listing.header.active.field = color.palette.tan
|
color.bg.listing.header.active.field = color.palette.tan
|
||||||
color.fg.listing.header.active.field = color.fg
|
color.fg.listing.header.active.field = color.fg
|
||||||
|
|
||||||
|
|||||||
@@ -48,4 +48,16 @@ public class NavigatableActionContext extends ProgramLocationActionContext
|
|||||||
public Navigatable getNavigatable() {
|
public Navigatable getNavigatable() {
|
||||||
return navigatable;
|
return navigatable;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Overridden to signal that this navigatable's program may not be the same as the globally
|
||||||
|
* active program. This is done to signal that this navigatable can supply default context.
|
||||||
|
*
|
||||||
|
* @return false
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean isActiveProgram() {
|
||||||
|
// signal that our program may be different than the active program
|
||||||
|
return navigatable.isConnected();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -49,4 +49,16 @@ public class ProgramActionContext extends DefaultActionContext {
|
|||||||
public Program getProgram() {
|
public Program getProgram() {
|
||||||
return program;
|
return program;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the program in this context is the globally active program in the tool. This
|
||||||
|
* is generally true for all context. Some context providers may be working with a different
|
||||||
|
* program than the active program or they may be using the active program with restricted
|
||||||
|
* address views. In this latter case, this method should return false.
|
||||||
|
* @return true if the program is the active program; false means the program may not be the
|
||||||
|
* active program
|
||||||
|
*/
|
||||||
|
public boolean isActiveProgram() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+54
-7
@@ -19,14 +19,15 @@ import java.awt.event.KeyEvent;
|
|||||||
|
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
|
|
||||||
import docking.ActionContext;
|
import docking.*;
|
||||||
import docking.DockingUtils;
|
|
||||||
import docking.action.*;
|
import docking.action.*;
|
||||||
import docking.action.builder.ActionBuilder;
|
import docking.action.builder.ActionBuilder;
|
||||||
import docking.tool.ToolConstants;
|
import docking.tool.ToolConstants;
|
||||||
import docking.widgets.tab.GTabPanel;
|
import docking.widgets.tab.GTabPanel;
|
||||||
import generic.theme.GIcon;
|
import generic.theme.GIcon;
|
||||||
import ghidra.app.CorePluginPackage;
|
import ghidra.app.CorePluginPackage;
|
||||||
|
import ghidra.app.context.ListingActionContext;
|
||||||
|
import ghidra.app.context.ProgramActionContext;
|
||||||
import ghidra.app.events.*;
|
import ghidra.app.events.*;
|
||||||
import ghidra.app.plugin.PluginCategoryNames;
|
import ghidra.app.plugin.PluginCategoryNames;
|
||||||
import ghidra.app.services.CodeViewerService;
|
import ghidra.app.services.CodeViewerService;
|
||||||
@@ -87,6 +88,7 @@ public class MultiTabPlugin extends Plugin
|
|||||||
private DockingAction goToPreviousProgramAction;
|
private DockingAction goToPreviousProgramAction;
|
||||||
|
|
||||||
private Timer selectHighlightedProgramTimer;
|
private Timer selectHighlightedProgramTimer;
|
||||||
|
private TabContextListener contextListener = new TabContextListener();
|
||||||
|
|
||||||
public MultiTabPlugin(PluginTool tool) {
|
public MultiTabPlugin(PluginTool tool) {
|
||||||
super(tool);
|
super(tool);
|
||||||
@@ -267,6 +269,9 @@ public class MultiTabPlugin extends Plugin
|
|||||||
progService = tool.getService(ProgramManager.class);
|
progService = tool.getService(ProgramManager.class);
|
||||||
cvService = tool.getService(CodeViewerService.class);
|
cvService = tool.getService(CodeViewerService.class);
|
||||||
cvService.setNorthComponent(tabPanel);
|
cvService.setNorthComponent(tabPanel);
|
||||||
|
|
||||||
|
DockingWindowManager dwm = tool.getWindowManager();
|
||||||
|
dwm.addContextListener(contextListener);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initOptions() {
|
private void initOptions() {
|
||||||
@@ -294,11 +299,7 @@ public class MultiTabPlugin extends Plugin
|
|||||||
return EMPTY8_ICON;
|
return EMPTY8_ICON;
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean removeProgram(Program program) {
|
private void programSelected(Program program) {
|
||||||
return progService.closeProgram(program, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
void programSelected(Program program) {
|
|
||||||
if (program != progService.getCurrentProgram()) {
|
if (program != progService.getCurrentProgram()) {
|
||||||
progService.setCurrentProgram(program);
|
progService.setCurrentProgram(program);
|
||||||
cvService.requestFocus();
|
cvService.requestFocus();
|
||||||
@@ -405,4 +406,50 @@ public class MultiTabPlugin extends Plugin
|
|||||||
tabPanel.refreshTab((Program) domainObj);
|
tabPanel.refreshTab((Program) domainObj);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//=================================================================================================
|
||||||
|
// Inner Classes
|
||||||
|
//=================================================================================================
|
||||||
|
|
||||||
|
private class TabContextListener implements DockingContextListener {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void contextChanged(ActionContext localContext) {
|
||||||
|
|
||||||
|
/*
|
||||||
|
Goal: We would like to have the program tabs paint as gray when the default context
|
||||||
|
is not being driven by the active program. This should happen whenever the
|
||||||
|
focus is in a component that has a program that can be used by tool actions.
|
||||||
|
|
||||||
|
The tool uses the active program as a fallback/default for actions when the
|
||||||
|
local context will not work. When a local context has a program different
|
||||||
|
than the active program, then we want to signal to users visually that the
|
||||||
|
focused component is the one providing the program for the current context.
|
||||||
|
*/
|
||||||
|
|
||||||
|
DockingWindowManager dwm = tool.getWindowManager();
|
||||||
|
Program myProgram = null;
|
||||||
|
ProgramActionContext defaultContext =
|
||||||
|
(ListingActionContext) dwm.getDefaultActionContext(ProgramActionContext.class);
|
||||||
|
if (defaultContext != null) {
|
||||||
|
myProgram = defaultContext.getProgram();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(localContext instanceof ProgramActionContext pac)) {
|
||||||
|
tabPanel.setActive(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Program localProgram = pac.getProgram();
|
||||||
|
if (myProgram != localProgram || !pac.isActiveProgram()) {
|
||||||
|
// A different program is in the local context; deactivate out tabs.
|
||||||
|
tabPanel.setActive(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Signal that the program from our default context is the active program.
|
||||||
|
tabPanel.setActive(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -48,16 +48,20 @@ color.fg.fieldpanel = color.fg
|
|||||||
color.bg.fieldpanel.selection = color.bg.selection
|
color.bg.fieldpanel.selection = color.bg.selection
|
||||||
color.bg.fieldpanel.highlight = color.bg.highlight
|
color.bg.fieldpanel.highlight = color.bg.highlight
|
||||||
|
|
||||||
color.bg.widget.tabs.selected = [color]system.color.bg.selected.view
|
color.bg.widget.tabs.selected.active = [color]system.color.bg.selected.view
|
||||||
color.fg.widget.tabs.selected = [color]system.color.fg.selected.view
|
color.bg.widget.tabs.selected.inactive = color.palette.gray
|
||||||
color.bg.widget.tabs.unselected = [color]system.color.bg.control
|
color.bg.widget.tabs.unselected = [color]system.color.bg.control
|
||||||
color.fg.widget.tabs.unselected = color.fg
|
|
||||||
color.bg.widget.tabs.highlighted = color.palette.lightcornflowerblue
|
color.bg.widget.tabs.highlighted = color.palette.lightcornflowerblue
|
||||||
|
|
||||||
color.bg.widget.tabs.list = [color]system.color.bg.tooltip
|
color.fg.widget.tabs.selected.active = [color]system.color.fg.selected.view
|
||||||
color.bg.widget.tabs.more.tabs.hover = color.bg.widget.tabs.selected
|
color.fg.widget.tabs.selected.inactive = color.fg.widget.tabs.selected.active
|
||||||
|
color.fg.widget.tabs.unselected = color.fg
|
||||||
|
|
||||||
color.fg.widget.tabs.list = color.fg
|
color.fg.widget.tabs.list = color.fg
|
||||||
|
color.bg.widget.tabs.list = [color]system.color.bg.tooltip
|
||||||
|
color.bg.widget.tabs.more.tabs.hover = color.bg.widget.tabs.selected.active
|
||||||
|
|
||||||
|
|
||||||
color.bg.formatted.field.error = color.palette.lightcoral
|
color.bg.formatted.field.error = color.palette.lightcoral
|
||||||
color.bg.formatted.field.editing = color.bg.filterfield
|
color.bg.formatted.field.editing = color.bg.filterfield
|
||||||
@@ -189,6 +193,9 @@ font.wizard.border.title = sansserif-plain-10
|
|||||||
|
|
||||||
[Dark Defaults]
|
[Dark Defaults]
|
||||||
|
|
||||||
|
color.bg.currentline = #393D64 // gray purple
|
||||||
|
|
||||||
|
color.bg.filechooser.shortcut = [color]system.color.bg.view
|
||||||
|
|
||||||
color.fg.filterfield = color.palette.darkslategray
|
color.fg.filterfield = color.palette.darkslategray
|
||||||
|
|
||||||
@@ -197,9 +204,8 @@ color.bg.find.highlight.active = #BC7474 // rosybrown
|
|||||||
|
|
||||||
color.bg.highlight = #67582A // olivish
|
color.bg.highlight = #67582A // olivish
|
||||||
|
|
||||||
color.bg.currentline = #393D64 // gray purple
|
color.bg.widget.tabs.selected.inactive = #696969 // dimgray
|
||||||
|
|
||||||
color.bg.filechooser.shortcut = [color]system.color.bg.view
|
|
||||||
|
|
||||||
|
|
||||||
[CDE/Motif]
|
[CDE/Motif]
|
||||||
|
|||||||
@@ -41,25 +41,28 @@ public class GTab<T> extends JPanel {
|
|||||||
private final static Icon EMPTY16_ICON = Icons.EMPTY_ICON;
|
private final static Icon EMPTY16_ICON = Icons.EMPTY_ICON;
|
||||||
private final static Icon CLOSE_ICON = new GIcon("icon.widget.tabs.close");
|
private final static Icon CLOSE_ICON = new GIcon("icon.widget.tabs.close");
|
||||||
private final static Icon HIGHLIGHT_CLOSE_ICON = new GIcon("icon.widget.tabs.close.highlight");
|
private final static Icon HIGHLIGHT_CLOSE_ICON = new GIcon("icon.widget.tabs.close.highlight");
|
||||||
private final static Color TAB_FG_COLOR = new GColor("color.fg.widget.tabs.unselected");
|
|
||||||
private final static Color SELECTED_TAB_FG_COLOR = new GColor("color.fg.widget.tabs.selected");
|
|
||||||
private final static Color HIGHLIGHTED_TAB_BG_COLOR =
|
|
||||||
new GColor("color.bg.widget.tabs.highlighted");
|
|
||||||
|
|
||||||
final static Color TAB_BG_COLOR = new GColor("color.bg.widget.tabs.unselected");
|
//@formatter:off
|
||||||
final static Color SELECTED_TAB_BG_COLOR = new GColor("color.bg.widget.tabs.selected");
|
private final static Color FG_COLOR_UNSELECTED = new GColor("color.fg.widget.tabs.unselected");
|
||||||
|
private final static Color FG_COLOR_SELECTED_INACTIVE = new GColor("color.fg.widget.tabs.selected.inactive");
|
||||||
|
private final static Color FG_COLOR_SELECTED_ACTIVE = new GColor("color.fg.widget.tabs.selected.active");
|
||||||
|
|
||||||
|
private final static Color BG_COLOR_HIGHLIGHTED = new GColor("color.bg.widget.tabs.highlighted");
|
||||||
|
final static Color BG_COLOR_UNSELECTED = new GColor("color.bg.widget.tabs.unselected");
|
||||||
|
final static Color BG_COLOR_SELECTED_INACTIVE = new GColor("color.bg.widget.tabs.selected.inactive");
|
||||||
|
final static Color BG_COLOR_SELECTED_ACTIVE = new GColor("color.bg.widget.tabs.selected.active");
|
||||||
|
//@formatter:on
|
||||||
|
|
||||||
private GTabPanel<T> tabPanel;
|
private GTabPanel<T> tabPanel;
|
||||||
private T value;
|
private T value;
|
||||||
private boolean selected;
|
|
||||||
private JLabel closeLabel;
|
private JLabel closeLabel;
|
||||||
private JLabel nameLabel;
|
private JLabel nameLabel;
|
||||||
|
private boolean isSelected;
|
||||||
|
|
||||||
GTab(GTabPanel<T> gTabPanel, T value, boolean selected) {
|
GTab(GTabPanel<T> gTabPanel, T value, boolean selected) {
|
||||||
super(new HorizontalLayout(10));
|
super(new HorizontalLayout(10));
|
||||||
this.tabPanel = gTabPanel;
|
this.tabPanel = gTabPanel;
|
||||||
this.value = value;
|
this.value = value;
|
||||||
this.selected = selected;
|
|
||||||
|
|
||||||
setBorder(selected ? SELECTED_TAB_BORDER : TAB_BORDER);
|
setBorder(selected ? SELECTED_TAB_BORDER : TAB_BORDER);
|
||||||
|
|
||||||
@@ -87,8 +90,12 @@ public class GTab<T> extends JPanel {
|
|||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setSelected(boolean selected) {
|
boolean isSelected() {
|
||||||
this.selected = selected;
|
return isSelected;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setSelected(boolean selected) {
|
||||||
|
this.isSelected = selected;
|
||||||
initializeTabColors(false);
|
initializeTabColors(false);
|
||||||
setBorder(selected ? SELECTED_TAB_BORDER : TAB_BORDER);
|
setBorder(selected ? SELECTED_TAB_BORDER : TAB_BORDER);
|
||||||
}
|
}
|
||||||
@@ -100,8 +107,8 @@ public class GTab<T> extends JPanel {
|
|||||||
repaint();
|
repaint();
|
||||||
}
|
}
|
||||||
|
|
||||||
void setHighlight(boolean b) {
|
void setHighlight(boolean isHighlighted) {
|
||||||
initializeTabColors(b);
|
initializeTabColors(isHighlighted);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void installMouseListener(Container c, GTabMouseListener listener) {
|
private void installMouseListener(Container c, GTabMouseListener listener) {
|
||||||
@@ -129,18 +136,30 @@ public class GTab<T> extends JPanel {
|
|||||||
closeLabel.setBackground(bg);
|
closeLabel.setBackground(bg);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Color getBackgroundColor(boolean isHighlighted) {
|
Color getBackgroundColor(boolean isHighlighted) {
|
||||||
if (isHighlighted) {
|
if (isHighlighted) {
|
||||||
return HIGHLIGHTED_TAB_BG_COLOR;
|
return BG_COLOR_HIGHLIGHTED;
|
||||||
}
|
}
|
||||||
return selected ? SELECTED_TAB_BG_COLOR : TAB_BG_COLOR;
|
|
||||||
|
if (!isSelected) {
|
||||||
|
return BG_COLOR_UNSELECTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean isActive = tabPanel.isActive();
|
||||||
|
return isActive ? BG_COLOR_SELECTED_ACTIVE : BG_COLOR_SELECTED_INACTIVE;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Color getForegroundColor(boolean isHighlighted) {
|
private Color getForegroundColor(boolean isHighlighted) {
|
||||||
if (isHighlighted || selected) {
|
if (isHighlighted) {
|
||||||
return SELECTED_TAB_FG_COLOR;
|
return FG_COLOR_SELECTED_ACTIVE;
|
||||||
}
|
}
|
||||||
return TAB_FG_COLOR;
|
|
||||||
|
if (!isSelected) {
|
||||||
|
return FG_COLOR_UNSELECTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean isActive = tabPanel.isActive();
|
||||||
|
return isActive ? FG_COLOR_SELECTED_ACTIVE : FG_COLOR_SELECTED_INACTIVE;
|
||||||
}
|
}
|
||||||
|
|
||||||
private class GTabMouseListener extends MouseAdapter {
|
private class GTabMouseListener extends MouseAdapter {
|
||||||
@@ -151,7 +170,7 @@ public class GTab<T> extends JPanel {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void mouseExited(MouseEvent e) {
|
public void mouseExited(MouseEvent e) {
|
||||||
closeLabel.setIcon(selected ? CLOSE_ICON : EMPTY16_ICON);
|
closeLabel.setIcon(isSelected ? CLOSE_ICON : EMPTY16_ICON);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -169,9 +188,8 @@ public class GTab<T> extends JPanel {
|
|||||||
tabPanel.closeTab(value);
|
tabPanel.closeTab(value);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!selected) {
|
|
||||||
tabPanel.selectTab(value);
|
tabPanel.selectTab(value);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -51,6 +51,7 @@ import utility.function.Dummy;
|
|||||||
*/
|
*/
|
||||||
public class GTabPanel<T> extends JPanel {
|
public class GTabPanel<T> extends JPanel {
|
||||||
|
|
||||||
|
private boolean isActive;
|
||||||
private T selectedValue;
|
private T selectedValue;
|
||||||
private T highlightedValue;
|
private T highlightedValue;
|
||||||
private boolean ignoreFocusLost;
|
private boolean ignoreFocusLost;
|
||||||
@@ -117,6 +118,7 @@ public class GTabPanel<T> extends JPanel {
|
|||||||
@Override
|
@Override
|
||||||
public void focusGained(FocusEvent e) {
|
public void focusGained(FocusEvent e) {
|
||||||
updateTabColors();
|
updateTabColors();
|
||||||
|
repaint();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -126,8 +128,9 @@ public class GTabPanel<T> extends JPanel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
highlightedValue = null;
|
highlightedValue = null;
|
||||||
updateAccessibleName();
|
|
||||||
updateTabColors();
|
updateTabColors();
|
||||||
|
updateAccessibleName();
|
||||||
|
repaint();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -184,6 +187,16 @@ public class GTabPanel<T> extends JPanel {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Color getSelectedTabColor() {
|
||||||
|
|
||||||
|
if (selectedValue == null) {
|
||||||
|
return GTab.BG_COLOR_UNSELECTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
GTab<T> tab = getTab(selectedValue);
|
||||||
|
return tab.getBackgroundColor(false);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the currently selected tab. If the panel is not empty, there will always be a
|
* Returns the currently selected tab. If the panel is not empty, there will always be a
|
||||||
* selected tab.
|
* selected tab.
|
||||||
@@ -196,43 +209,93 @@ public class GTabPanel<T> extends JPanel {
|
|||||||
/**
|
/**
|
||||||
* Returns the currently highlighted tab if a tab is highlighted. Note: the selected tab can
|
* Returns the currently highlighted tab if a tab is highlighted. Note: the selected tab can
|
||||||
* never be highlighted.
|
* never be highlighted.
|
||||||
* @return the currently highlighted tab or null if no tab is highligted
|
* @return the currently highlighted tab or null if no tab is highlighted
|
||||||
*/
|
*/
|
||||||
public T getHighlightedTabValue() {
|
public T getHighlightedTabValue() {
|
||||||
return highlightedValue;
|
return highlightedValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Makes the tab for the given value be the selected tab.
|
* Sets this panel to be active. When active, this panel will paint differently than when
|
||||||
|
* inactive.
|
||||||
|
* @param isActive true if active
|
||||||
|
*/
|
||||||
|
public void setActive(boolean isActive) {
|
||||||
|
this.isActive = isActive;
|
||||||
|
doUpdateSelectedTab(selectedValue);
|
||||||
|
repaint();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* True if this panel is active.
|
||||||
|
* @return true if active
|
||||||
|
*/
|
||||||
|
public boolean isActive() {
|
||||||
|
return isActive;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Makes the tab for the given value be the selected and active tab. If the value is null, then
|
||||||
|
* the tabs will be rebuilt and no tab will be selected.
|
||||||
|
*
|
||||||
* @param value the value whose tab is to be selected
|
* @param value the value whose tab is to be selected
|
||||||
*/
|
*/
|
||||||
public void selectTab(T value) {
|
public void selectTab(T value) {
|
||||||
if (value == selectedValue) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (value != null && !allValues.contains(value)) {
|
if (value != null && !allValues.contains(value)) {
|
||||||
throw new IllegalArgumentException(
|
throw new IllegalArgumentException(
|
||||||
"Attempted to set selected value to non added value");
|
"Attempted to set selected value to non-added value");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isAlreadySelected(value)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This method is called for things like user clicks. Anytime we select the tab from the
|
||||||
|
// API, also make this panel active. This is easier on clients in that they do not have to
|
||||||
|
// both select and activate this panel.
|
||||||
|
isActive = true;
|
||||||
|
doUpdateSelectedTab(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isAlreadySelected(T value) {
|
||||||
|
if (value != selectedValue) {
|
||||||
|
return false; // different values; can't ignore
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value == null) {
|
||||||
|
return true; // new value and current value are null; nothing to update
|
||||||
|
}
|
||||||
|
|
||||||
|
GTab<T> oldTab = getTab(selectedValue);
|
||||||
|
if (oldTab == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return oldTab.isSelected();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void doUpdateSelectedTab(T newValue) {
|
||||||
|
|
||||||
closeTabList();
|
closeTabList();
|
||||||
highlightedValue = null;
|
highlightedValue = null;
|
||||||
|
|
||||||
T oldValue = selectedValue;
|
T oldValue = selectedValue;
|
||||||
selectedValue = value;
|
selectedValue = newValue;
|
||||||
|
|
||||||
if (isVisibleTab(selectedValue)) {
|
if (isVisibleTab(selectedValue)) {
|
||||||
GTab<T> oldTab = getTab(oldValue);
|
GTab<T> oldTab = getTab(oldValue);
|
||||||
if (oldTab != null) {
|
if (oldTab != null) {
|
||||||
oldTab.setSelected(false);
|
oldTab.setSelected(false);
|
||||||
}
|
}
|
||||||
GTab<T> newTab = getTab(value);
|
GTab<T> newTab = getTab(newValue);
|
||||||
newTab.setSelected(true);
|
newTab.setSelected(true);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
rebuildTabs();
|
rebuildTabs();
|
||||||
}
|
}
|
||||||
|
|
||||||
selectedTabConsumer.accept(value);
|
if (oldValue != newValue) {
|
||||||
|
selectedTabConsumer.accept(newValue);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -274,6 +337,7 @@ public class GTabPanel<T> extends JPanel {
|
|||||||
highlightedValue = value == selectedValue ? null : value;
|
highlightedValue = value == selectedValue ? null : value;
|
||||||
updateTabColors();
|
updateTabColors();
|
||||||
updateAccessibleName();
|
updateAccessibleName();
|
||||||
|
repaint();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -535,7 +599,7 @@ public class GTabPanel<T> extends JPanel {
|
|||||||
|
|
||||||
if (shouldShowTabs()) {
|
if (shouldShowTabs()) {
|
||||||
setFocusable(true);
|
setFocusable(true);
|
||||||
setBorder(new GTabPanelBorder());
|
setBorder(new GTabPanelBorder(this));
|
||||||
populateTabs();
|
populateTabs();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -567,24 +631,33 @@ public class GTabPanel<T> extends JPanel {
|
|||||||
removeAll();
|
removeAll();
|
||||||
|
|
||||||
// reserve space for the selected tab
|
// reserve space for the selected tab
|
||||||
GTab<T> selectedTab = selectedValue != null ? new GTab<>(this, selectedValue, true) : null;
|
GTab<T> selectedTab = null;
|
||||||
|
if (selectedValue != null) {
|
||||||
|
selectedTab = new GTab<>(this, selectedValue, true);
|
||||||
|
}
|
||||||
|
|
||||||
availableWidth -= getParentedComponentWidth(selectedTab);
|
availableWidth -= getParentedComponentWidth(selectedTab);
|
||||||
|
|
||||||
boolean selectedTabAdded = false;
|
boolean selectedTabAdded = false;
|
||||||
for (T value : allValues) {
|
for (T value : allValues) {
|
||||||
boolean isSelectedValue = value == selectedValue;
|
boolean isSelectedValue = value == selectedValue;
|
||||||
GTab<T> nextTab = isSelectedValue ? selectedTab : new GTab<>(this, value, false);
|
GTab<T> nextTab = selectedTab;
|
||||||
|
if (!isSelectedValue) {
|
||||||
|
nextTab = new GTab<>(this, value, false);
|
||||||
|
}
|
||||||
|
|
||||||
int tabWidth = isSelectedValue ? 0 : getParentedComponentWidth(nextTab);
|
int tabWidth = isSelectedValue ? 0 : getParentedComponentWidth(nextTab);
|
||||||
if (tabWidth > availableWidth) {
|
if (tabWidth > availableWidth) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
allTabs.add(nextTab);
|
allTabs.add(nextTab);
|
||||||
add(nextTab);
|
add(nextTab);
|
||||||
selectedTabAdded |= isSelectedValue;
|
selectedTabAdded |= isSelectedValue;
|
||||||
availableWidth -= tabWidth;
|
availableWidth -= tabWidth;
|
||||||
}
|
}
|
||||||
|
|
||||||
// if we ran out of space before adding the selected tab, add it now if it fits since
|
// If we ran out of space before adding the selected tab, add it now if it fits since
|
||||||
// we always want the selected tab visible and we reserved space for it (unless there
|
// we always want the selected tab visible and we reserved space for it (unless there
|
||||||
// wasn't space for any tabs)
|
// wasn't space for any tabs)
|
||||||
if (selectedTab != null && !selectedTabAdded && availableWidth >= 0) {
|
if (selectedTab != null && !selectedTabAdded && availableWidth >= 0) {
|
||||||
|
|||||||
@@ -25,9 +25,11 @@ import javax.swing.border.EmptyBorder;
|
|||||||
public class GTabPanelBorder extends EmptyBorder {
|
public class GTabPanelBorder extends EmptyBorder {
|
||||||
public static final int MARGIN_SIZE = 2;
|
public static final int MARGIN_SIZE = 2;
|
||||||
public static final int BOTTOM_SOLID_COLOR_SIZE = 3;
|
public static final int BOTTOM_SOLID_COLOR_SIZE = 3;
|
||||||
|
private GTabPanel<?> tabPanel;
|
||||||
|
|
||||||
public GTabPanelBorder() {
|
public GTabPanelBorder(GTabPanel<?> tabPanel) {
|
||||||
super(0, 0, BOTTOM_SOLID_COLOR_SIZE, 0);
|
super(0, 0, BOTTOM_SOLID_COLOR_SIZE, 0);
|
||||||
|
this.tabPanel = tabPanel;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -40,9 +42,10 @@ public class GTabPanelBorder extends EmptyBorder {
|
|||||||
Color oldColor = g.getColor();
|
Color oldColor = g.getColor();
|
||||||
g.translate(x, y);
|
g.translate(x, y);
|
||||||
|
|
||||||
Color highlight = GTab.TAB_BG_COLOR.brighter().brighter();
|
Color highlight = GTab.BG_COLOR_UNSELECTED.brighter().brighter();
|
||||||
|
|
||||||
g.setColor(GTab.SELECTED_TAB_BG_COLOR);
|
Color color = tabPanel.getSelectedTabColor();
|
||||||
|
g.setColor(color);
|
||||||
g.fillRect(insets.left, h - insets.bottom, w - insets.right - 1, insets.bottom);
|
g.fillRect(insets.left, h - insets.bottom, w - insets.right - 1, insets.bottom);
|
||||||
|
|
||||||
g.setColor(highlight);
|
g.setColor(highlight);
|
||||||
|
|||||||
Reference in New Issue
Block a user