11package software .coley .recaf .ui .docking ;
22
33import jakarta .annotation .Nonnull ;
4+ import jakarta .annotation .Nullable ;
45import jakarta .enterprise .context .ApplicationScoped ;
56import jakarta .enterprise .inject .Instance ;
67import jakarta .inject .Inject ;
78import javafx .beans .value .ObservableValue ;
89import javafx .geometry .Orientation ;
910import javafx .geometry .Side ;
1011import javafx .scene .Node ;
12+ import javafx .scene .Scene ;
1113import javafx .scene .control .ContextMenu ;
1214import javafx .scene .control .Label ;
1315import javafx .scene .control .MenuItem ;
1416import javafx .scene .control .SplitPane ;
17+ import javafx .stage .Stage ;
1518import org .kordamp .ikonli .Ikon ;
1619import org .kordamp .ikonli .carbonicons .CarbonIcons ;
1720import org .slf4j .Logger ;
1821import software .coley .bentofx .Bento ;
1922import software .coley .bentofx .building .DockBuilding ;
23+ import software .coley .bentofx .building .StageBuilding ;
2024import software .coley .bentofx .control .DragDropStage ;
2125import software .coley .bentofx .control .Headers ;
2226import software .coley .bentofx .dockable .Dockable ;
2327import software .coley .bentofx .dockable .DockableIconFactory ;
28+ import software .coley .bentofx .dockable .DragDropBehavior ;
2429import software .coley .bentofx .layout .DockContainer ;
2530import software .coley .bentofx .layout .container .DockContainerBranch ;
2631import 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 ;
0 commit comments