11package org .basex .gui .layout ;
22
33import java .awt .*;
4+ import java .util .function .*;
45
56/**
67 * Project specific Split panel implementation.
@@ -22,6 +23,18 @@ public final class BaseXSplit extends BaseXBack implements LayoutManager {
2223 private double [] hiddenSize ;
2324 /** Cached sizes (when panel is hidden). */
2425 private double [] cachedSize ;
26+ /** Resize listener. */
27+ private Consumer <double []> resized ;
28+
29+ /** Index of the panel with a fixed pixel size ({@code -1}: proportional). */
30+ private int anchor = -1 ;
31+ /** Target pixel size of the anchored panel. */
32+ private int anchorSize ;
33+ /** Minimum pixel size for the anchored panel and its neighbors. */
34+ private int anchorMin ;
35+
36+ /** Panels have actually been resized during the current drag. */
37+ private boolean dragged ;
2538
2639 /**
2740 * Constructor.
@@ -56,6 +69,42 @@ public void init(final double[] vis, final double[] hidden) {
5669 hiddenSize = hidden ;
5770 }
5871
72+ /**
73+ * Sets the proportional panel sizes (sum must be 1.0).
74+ * @param sizes proportional sizes
75+ */
76+ public void sizes (final double [] sizes ) {
77+ propSize = sizes ;
78+ }
79+
80+ /**
81+ * Registers a listener.
82+ * @param listener resize listener
83+ */
84+ public void resized (final Consumer <double []> listener ) {
85+ resized = listener ;
86+ }
87+
88+ /**
89+ * Anchors a panel to a fixed pixel size.
90+ * @param index panel index to anchor
91+ * @param size target pixel size
92+ * @param min minimum pixel size
93+ */
94+ public void anchor (final int index , final int size , final int min ) {
95+ anchor = index ;
96+ anchorSize = size ;
97+ anchorMin = min ;
98+ }
99+
100+ /**
101+ * Returns the current pixel size of the anchored panel.
102+ * @return pixel size
103+ */
104+ public int anchorSize () {
105+ return anchorSize ;
106+ }
107+
59108 /**
60109 * Sets proportional panel sizes (sum must be 1.0).
61110 * @param show show/hide flag
@@ -84,6 +133,7 @@ public void visible(final boolean show) {
84133 void startDrag (final double p ) {
85134 dragPos = p ;
86135 dragSize = propSize .clone ();
136+ dragged = false ;
87137 }
88138
89139 /**
@@ -92,26 +142,68 @@ void startDrag(final double p) {
92142 * @param p current position
93143 */
94144 void drag (final BaseXSplitSep sep , final double p ) {
95- if (dragSize == null ) startDrag (p );
96-
97145 final Component [] m = getComponents ();
98146 final int r = propSize .length ;
99147 int q = 0 ;
100148 for (int n = 0 ; n < r - 1 ; ++n ) {
101149 if (m [(n << 1 ) + 1 ] == sep ) q = n + 1 ;
102150 }
103151 final double v = (dragPos - p ) / (horizontal ? getWidth () : getHeight ());
152+ final double min = anchor >= 0 ? (double ) anchorMin / splitSize () : 0.0001 ;
104153 for (int i = 0 ; i < q ; ++i ) {
105- if (dragSize [i ] - v / q < 0.0001 ) return ;
154+ if (dragSize [i ] - v / q < min ) return ;
106155 }
107156 for (int i = q ; i < r ; ++i ) {
108- if (dragSize [i ] + v / (r - q ) < 0.0001 ) return ;
157+ if (dragSize [i ] + v / (r - q ) < min ) return ;
109158 }
110159 for (int i = 0 ; i < q ; ++i ) propSize [i ] = dragSize [i ] - v / q ;
111160 for (int i = q ; i < r ; ++i ) propSize [i ] = dragSize [i ] + v / (r - q );
161+ dragged = true ;
112162 revalidate ();
113163 }
114164
165+ /**
166+ * Finishes a splitter drag: snapshots the anchored pixel size and notifies the listener once.
167+ */
168+ void endDrag () {
169+ if (dragged ) {
170+ // remember the dragged pixel size of the anchored panel
171+ if (anchor >= 0 ) {
172+ anchorSize = (int ) Math .round (propSize [anchor ] * splitSize ());
173+ }
174+ if (resized != null ) resized .accept (propSize );
175+ }
176+ dragged = false ;
177+ }
178+
179+ /**
180+ * Computes the proportional size of the anchored panel for the given available space.
181+ * The panel keeps its target pixel size, but shrinks down to {@code min} pixels (and no
182+ * further) when space runs short, always reserving {@code min} pixels for its neighbors.
183+ * @param target target pixel size
184+ * @param min minimum pixel size
185+ * @param size available space in pixels
186+ * @return proportional size (between 0 and 1)
187+ */
188+ static double anchorFraction (final int target , final int min , final int size ) {
189+ if (size <= 0 ) return 0 ;
190+ int a = Math .min (target , size - min );
191+ a = Math .max (a , Math .min (min , size ));
192+ return (double ) a / size ;
193+ }
194+
195+ /**
196+ * Returns the available space for the panels (total size minus the visible separators).
197+ * @return size in pixels
198+ */
199+ private int splitSize () {
200+ int seps = propSize .length - 1 ;
201+ for (final double d : propSize ) {
202+ if (d == 0 ) --seps ;
203+ }
204+ return (horizontal ? getWidth () : getHeight ()) - seps * SEPARATOR_SIZE ;
205+ }
206+
115207 @ Override
116208 public void addLayoutComponent (final String name , final Component comp ) { }
117209
@@ -150,6 +242,21 @@ public void layoutContainer(final Container parent) {
150242
151243 // set bounds of all components
152244 final int sz = (horizontal ? w : h ) - c * SEPARATOR_SIZE ;
245+
246+ // enforce a fixed pixel size for the anchored panel (skip while hidden or dragging)
247+ if (anchor >= 0 && !dragged && propSize [anchor ] != 0 && sz > 0 ) {
248+ final double frac = anchorFraction (anchorSize , anchorMin , sz );
249+ // distribute the remaining space among the other panels, keeping their relative sizes
250+ double rest = 0 ;
251+ for (int i = 0 ; i < propSize .length ; i ++) {
252+ if (i != anchor ) rest += propSize [i ];
253+ }
254+ for (int i = 0 ; i < propSize .length ; i ++) {
255+ propSize [i ] = i == anchor ? frac :
256+ rest > 0 ? propSize [i ] / rest * (1 - frac ) : (1 - frac ) / (propSize .length - 1 );
257+ }
258+ }
259+
153260 double posD = 0 ;
154261 boolean invisible = false ;
155262 for (c = 0 ; c < cl ; c ++) {
0 commit comments