Skip to content

Commit 6a66e7c

Browse files
committed
Allow search panels to be docked anywhere
1 parent eed6c13 commit 6a66e7c

File tree

4 files changed

+89
-20
lines changed

4 files changed

+89
-20
lines changed

gradle/libs.versions.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ cdi-impl = "6.0.2.Final"
1010
cfr = "0.152"
1111
dex-translator = "1.1.1"
1212
diffutils = "4.16"
13-
docking = "0.11.1"
13+
docking = "0.14.0"
1414
downgrader = "1.3.3"
1515
extra-collections = "1.7.0"
1616
extra-observables = "1.3.0"

recaf-ui/src/main/java/software/coley/recaf/services/navigation/Actions.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2245,6 +2245,7 @@ private <T extends AbstractSearchPane> T openSearchPane(@Nonnull String titleId,
22452245
stage.show();
22462246
stage.requestFocus();
22472247
}
2248+
dockable.setDragGroupMask(DockingManager.GROUP_ANYWHERE);
22482249
dockable.addCloseListener((_, _) -> paneProvider.destroy(content));
22492250
dockable.setContextMenuFactory(d -> {
22502251
ContextMenu menu = new ContextMenu();

recaf-ui/src/main/java/software/coley/recaf/ui/docking/DockingManager.java

Lines changed: 84 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,31 @@
11
package software.coley.recaf.ui.docking;
22

33
import jakarta.annotation.Nonnull;
4+
import jakarta.annotation.Nullable;
45
import jakarta.enterprise.context.ApplicationScoped;
56
import jakarta.enterprise.inject.Instance;
67
import jakarta.inject.Inject;
78
import javafx.beans.value.ObservableValue;
89
import javafx.geometry.Orientation;
910
import javafx.geometry.Side;
1011
import javafx.scene.Node;
12+
import javafx.scene.Scene;
1113
import javafx.scene.control.ContextMenu;
1214
import javafx.scene.control.Label;
1315
import javafx.scene.control.MenuItem;
1416
import javafx.scene.control.SplitPane;
17+
import javafx.stage.Stage;
1518
import org.kordamp.ikonli.Ikon;
1619
import org.kordamp.ikonli.carbonicons.CarbonIcons;
1720
import org.slf4j.Logger;
1821
import software.coley.bentofx.Bento;
1922
import software.coley.bentofx.building.DockBuilding;
23+
import software.coley.bentofx.building.StageBuilding;
2024
import software.coley.bentofx.control.DragDropStage;
2125
import software.coley.bentofx.control.Headers;
2226
import software.coley.bentofx.dockable.Dockable;
2327
import software.coley.bentofx.dockable.DockableIconFactory;
28+
import software.coley.bentofx.dockable.DragDropBehavior;
2429
import software.coley.bentofx.layout.DockContainer;
2530
import software.coley.bentofx.layout.container.DockContainerBranch;
2631
import software.coley.bentofx.layout.container.DockContainerLeaf;
@@ -79,6 +84,10 @@ public class DockingManager {
7984
public static final String ID_CONTAINER_WORKSPACE_PRIMARY = "layout-workspace-primary";
8085
/** Bento group ID for tool tabs. */
8186
public static final int GROUP_TOOLS = 1;
87+
/** Bento group ID for things that can go anywhere, except for in {@link #GROUP_NEVER_RECEIVE}. */
88+
public static final int GROUP_ANYWHERE = -1;
89+
/** Bento group ID for things that can never have things dragged on them. */
90+
public static final int GROUP_NEVER_RECEIVE = -25565;
8291

8392
private final Actions actions;
8493
private final Instance<LoggingPane> loggingPaneProvider;
@@ -88,7 +97,57 @@ public class DockingManager {
8897
private final ResourceSummaryServiceConfig resourceSummaryConfig;
8998

9099
// The primary bento instance
91-
private final Bento bento = new Bento();
100+
private final Bento bento = new Bento() {
101+
@Nonnull
102+
@Override
103+
protected DockBuilding newDockBuilding() {
104+
return new DockBuilding(this) {
105+
@Nonnull
106+
@Override
107+
public DockContainerLeaf leaf(@Nonnull String identifier) {
108+
DockContainerLeaf leaf = super.leaf(identifier);
109+
leaf.setMenuFactory(DockingManager.this::buildMenu);
110+
return leaf;
111+
}
112+
};
113+
}
114+
115+
@Nonnull
116+
@Override
117+
protected StageBuilding newStageBuilding() {
118+
return new StageBuilding(this) {
119+
@Override
120+
protected void initializeFromSource(@Nonnull Scene sourceScene, @Nonnull Scene newScene,
121+
@Nullable Stage sourceStage, @Nonnull DragDropStage newStage,
122+
boolean sourceIsOwner) {
123+
// Always pass 'false' for 'sourceIsOwner'. Window ownership is generally something you would
124+
// want to specify, but it makes the window behave like a dialog which cannot be minimized.
125+
super.initializeFromSource(sourceScene, newScene, sourceStage, newStage, false);
126+
}
127+
};
128+
}
129+
130+
@Nonnull
131+
@Override
132+
protected DragDropBehavior newDragDropBehavior() {
133+
return new DragDropBehavior() {
134+
@Override
135+
public boolean canReceiveDockable(@Nonnull DockContainerLeaf targetContainer, @Nullable Side targetSide,
136+
@Nonnull Dockable dockable) {
137+
// If we see the 'never receive' ID in a container then we bail.
138+
if (targetContainer.getDockables().stream().anyMatch(d -> d.getDragGroupMask() == GROUP_NEVER_RECEIVE))
139+
return false;
140+
141+
// If the dockable being dragged can go 'anywhere' then we let it go anywhere.
142+
if (dockable.getDragGroupMask() == GROUP_ANYWHERE)
143+
return true;
144+
145+
// Otherwise we fall back to the default behavior.
146+
return super.canReceiveDockable(targetContainer, targetSide, dockable);
147+
}
148+
};
149+
}
150+
};
92151

93152
@Inject
94153
public DockingManager(@Nonnull WorkspaceManager workspaceManager,
@@ -202,7 +261,7 @@ public Dockable newToolDockable(@Nonnull String translationKey, @Nonnull Dockabl
202261
dockable.setNode(content);
203262
dockable.setIconFactory(iconFactory);
204263
dockable.setClosable(false);
205-
dockable.setDragGroup(GROUP_TOOLS);
264+
dockable.setDragGroupMask(GROUP_TOOLS);
206265
return dockable;
207266
}
208267

@@ -299,15 +358,33 @@ public Dockable newTranslatableDockable(@Nonnull ObservableValue<String> titleBi
299358
return dockable;
300359
}
301360

361+
/**
362+
* @return Newly created leaf container.
363+
*/
364+
@Nonnull
365+
public DockContainerLeaf newLeafContainer() {
366+
return newLeafContainer(UUID.randomUUID().toString());
367+
}
368+
369+
/**
370+
* @param id
371+
* ID of the container to create.
372+
*
373+
* @return Newly created leaf container.
374+
*/
375+
@Nonnull
376+
public DockContainerLeaf newLeafContainer(@Nonnull String id) {
377+
return bento.dockBuilding().leaf(id);
378+
}
379+
302380
@Nonnull
303381
private DockContainerLeaf newWelcomeContainer() {
304382
Dockable welcome = newTranslatableDockable("welcome.title", CarbonIcons.EARTH_FILLED, welcomePaneProvider.get());
305383
welcome.setClosable(false);
306384

307-
DockContainerLeaf leaf = bento.dockBuilding().leaf(ID_CONTAINER_ROOT_TOP);
385+
DockContainerLeaf leaf = newLeafContainer(ID_CONTAINER_ROOT_TOP);
308386
leaf.setCanSplit(false);
309387
leaf.setPruneWhenEmpty(false);
310-
leaf.setMenuFactory(this::buildMenu);
311388
leaf.addDockable(welcome);
312389
return leaf;
313390
}
@@ -316,17 +393,15 @@ private DockContainerLeaf newWelcomeContainer() {
316393
private DockContainerBranch newWorkspaceContainer() {
317394
// Container to hold:
318395
// - Workspace explorer
319-
DockContainerLeaf explorer = bento.dockBuilding().leaf(ID_CONTAINER_WORKSPACE_TOOLS);
320-
explorer.setMenuFactory(this::buildMenu);
396+
DockContainerLeaf explorer = newLeafContainer(ID_CONTAINER_WORKSPACE_TOOLS);
321397
explorer.setCanSplit(false);
322398
explorer.addDockables(newToolDockable("workspace.title", CarbonIcons.TREE_VIEW, workspaceExplorerProvider.get()));
323399
SplitPane.setResizableWithParent(explorer.asRegion(), false);
324400

325401
// Container to hold:
326402
// - Tabs for displaying open classes/files in the workspace
327-
DockContainerLeaf primary = bento.dockBuilding().leaf(ID_CONTAINER_WORKSPACE_PRIMARY);
403+
DockContainerLeaf primary = newLeafContainer(ID_CONTAINER_WORKSPACE_PRIMARY);
328404
primary.setPruneWhenEmpty(false);
329-
primary.setMenuFactory(this::buildMenu);
330405

331406
// Combining the two into a branch
332407
DockContainerBranch branch = bento.dockBuilding().branch(ID_CONTAINER_ROOT_TOP);
@@ -343,10 +418,9 @@ private DockContainerBranch newWorkspaceContainer() {
343418

344419
@Nonnull
345420
private DockContainerLeaf newBottomContainer() {
346-
DockContainerLeaf leaf = bento.dockBuilding().leaf(ID_CONTAINER_ROOT_BOTTOM);
421+
DockContainerLeaf leaf = newLeafContainer(ID_CONTAINER_ROOT_BOTTOM);
347422
leaf.setCanSplit(false);
348423
leaf.setSide(Side.BOTTOM);
349-
leaf.setMenuFactory(this::buildMenu);
350424
leaf.addDockables(newToolDockable("logging.title", CarbonIcons.TERMINAL, loggingPaneProvider.get()));
351425
SplitPane.setResizableWithParent(leaf, false);
352426
return leaf;

recaf-ui/src/main/java/software/coley/recaf/ui/pane/editing/AbstractContentPane.java

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,7 @@
1111
import javafx.scene.layout.Region;
1212
import org.kordamp.ikonli.Ikon;
1313
import software.coley.bentofx.Bento;
14-
import software.coley.bentofx.building.ContentWrapperFactory;
1514
import software.coley.bentofx.building.DockBuilding;
16-
import software.coley.bentofx.building.HeaderFactory;
17-
import software.coley.bentofx.building.HeadersFactory;
18-
import software.coley.bentofx.control.ContentWrapper;
19-
import software.coley.bentofx.control.Header;
20-
import software.coley.bentofx.control.HeaderPane;
21-
import software.coley.bentofx.control.Headers;
2215
import software.coley.bentofx.dockable.Dockable;
2316
import software.coley.bentofx.dockable.DockableIconFactory;
2417
import software.coley.bentofx.layout.container.DockContainerLeaf;
@@ -32,6 +25,7 @@
3225
import software.coley.recaf.services.navigation.Navigable;
3326
import software.coley.recaf.services.navigation.UpdatableNavigable;
3427
import software.coley.recaf.ui.control.FontIconView;
28+
import software.coley.recaf.ui.docking.DockingManager;
3529
import software.coley.recaf.ui.docking.EmbeddedBento;
3630

3731
import java.util.ArrayList;
@@ -84,7 +78,7 @@ protected AbstractContentPane(@Nonnull Side toolTabSide) {
8478
dockable.setNode(displayWrapper);
8579
dockable.setCanBeDragged(false);
8680
dockable.setClosable(false);
87-
dockable.setDragGroup(-1);
81+
dockable.setDragGroupMask(DockingManager.GROUP_NEVER_RECEIVE);
8882

8983
// The display container contains the primary content display.
9084
//
@@ -223,7 +217,7 @@ public void addSideTab(@Nonnull ObservableValue<String> binding, @Nonnull Ikon i
223217
*/
224218
public void addSideTab(@Nonnull ObservableValue<String> binding, @Nonnull DockableIconFactory iconFactory, @Nullable Node content) {
225219
Dockable dockable = bento.dockBuilding().dockable();
226-
dockable.setDragGroup(-1);// Prevent being used as a drag-drop target
220+
dockable.setDragGroupMask(DockingManager.GROUP_NEVER_RECEIVE); // Prevent being used as a drag-drop target
227221
dockable.setCanBeDragged(false); // Prevent being used as a drag-drop
228222
dockable.setClosable(false);
229223
dockable.setIconFactory(iconFactory);

0 commit comments

Comments
 (0)