@@ -90,6 +90,7 @@ public enum State {READY, STARTED, ENDED, LOADED, STOPPED}
9090 private JPopupMenu exportFillsMenu ;
9191 private final JCheckBox transparentCheckbox ;
9292 private final JCheckBox storeExtraNodesCheckbox ;
93+ private final JCheckBox splitFillsCheckbox ;
9394
9495
9596 /**
@@ -109,7 +110,7 @@ public FillManagerUI(final SNT plugin) {
109110 listModel = new DefaultListModel <>();
110111 fillList = new JList <>(listModel );
111112 fillList .setCellRenderer (new FMCellRenderer ());
112- fillList .setVisibleRowCount (5 );
113+ fillList .setVisibleRowCount (7 );
113114 fillList .setPrototypeCellValue (PrototypeFill .instance );
114115 fillList .setComponentPopupMenu (createListPopupMenu ());
115116 gUtils = new GuiUtils (this );
@@ -147,16 +148,18 @@ public FillManagerUI(final SNT plugin) {
147148
148149 final JPanel distancePanel = new JPanel (new GridBagLayout ());
149150 final GridBagConstraints gdb = GuiUtils .defaultGbc ();
150- cursorThresholdChoice = new JRadioButton ("Set by clicking on traced structure (preferred)" ); // placeholder default
151+ gdb .insets = new Insets (0 , MARGIN , 0 , MARGIN ); // Consistent left margin
152+ gdb .anchor = GridBagConstraints .LINE_START ;
151153
152- final JPanel t1Panel = leftAlignedPanel ();
153- t1Panel . add ( cursorThresholdChoice );
154- distancePanel .add (t1Panel , gdb );
155- ++ gdb .gridy ;
154+ // Row 1: Cursor threshold choice
155+ cursorThresholdChoice = new JRadioButton ( "Set by clicking on traced structure (preferred)" );
156+ distancePanel .add (cursorThresholdChoice , gdb );
157+ gdb .gridy ++ ;
156158
159+ // Row 2: Manual threshold with text field and apply button
157160 manualThresholdChoice = new JRadioButton ("Specify manually:" );
158161 manualThresholdInputField = new JTextField ("" , 6 );
159- manualThresholdInputField .addActionListener (this ); // respond to Enter key
162+ manualThresholdInputField .addActionListener (this );
160163 manualThresholdInputField .addFocusListener (new java .awt .event .FocusAdapter () {
161164 @ Override
162165 public void focusGained (java .awt .event .FocusEvent e ) {
@@ -167,30 +170,34 @@ public void focusGained(java.awt.event.FocusEvent e) {
167170 });
168171 manualThresholdApplyButton = GuiUtils .Buttons .smallButton ("Apply" );
169172 manualThresholdApplyButton .addActionListener (this );
170- final JPanel t2Panel = leftAlignedPanel ();
171- t2Panel .add (manualThresholdChoice );
172- t2Panel .add (manualThresholdInputField );
173- t2Panel .add (manualThresholdApplyButton );
174- distancePanel .add (t2Panel , gdb );
175- ++gdb .gridy ;
176173
174+ final JPanel manualPanel = new JPanel (new FlowLayout (FlowLayout .LEADING , 0 , 0 ));
175+ manualPanel .add (manualThresholdChoice );
176+ manualPanel .add (manualThresholdInputField );
177+ manualPanel .add (manualThresholdApplyButton );
178+ distancePanel .add (manualPanel , gdb );
179+ gdb .gridy ++;
180+
181+ // Row 3: Explored threshold with apply button
177182 exploredThresholdChoice = new JRadioButton ("Use explored maximum" );
178183 exploredThresholdApplyButton = GuiUtils .Buttons .smallButton ("Apply" );
179184 exploredThresholdApplyButton .addActionListener (this );
180- final JPanel t3Panel = leftAlignedPanel ();
181- t3Panel .add (exploredThresholdChoice );
182- t3Panel .add (exploredThresholdApplyButton );
183- distancePanel .add (t3Panel , gdb );
184- ++gdb .gridy ;
185185
186+ final JPanel exploredPanel = new JPanel (new FlowLayout (FlowLayout .LEADING , 0 , 0 ));
187+ exploredPanel .add (exploredThresholdChoice );
188+ exploredPanel .add (exploredThresholdApplyButton );
189+ distancePanel .add (exploredPanel , gdb );
190+ gdb .gridy ++;
191+
192+ // Row 4: Defaults button
186193 final JButton defaults = GuiUtils .Buttons .smallButton ("Defaults" );
187- defaults .addActionListener ( e -> {
194+ defaults .addActionListener (e -> {
188195 plugin .setFillThreshold (-1 );
189196 cursorThresholdChoice .setSelected (true );
190197 });
191- final JPanel defaultsPanel = leftAlignedPanel ();
192- defaultsPanel .add (defaults );
193- distancePanel . add ( defaultsPanel , gdb );
198+ gdb . fill = GridBagConstraints . NONE ; // Don't stretch to fill width
199+ distancePanel .add (defaults , gdb );
200+
194201 add (distancePanel , c );
195202 ++c .gridy ;
196203
@@ -208,21 +215,28 @@ public void focusGained(java.awt.event.FocusEvent e) {
208215
209216 addSeparator (" Performance Impacting Options:" , c );
210217
218+ splitFillsCheckbox = new JCheckBox ("One fill per path" , plugin .getPrefs ().getBoolean (SNTPrefs .SPLIT_FILLS_KEY , true ));
219+ splitFillsCheckbox .setToolTipText ( "When enabled, each path is filled independently, allowing unique labels in exported images.\n " +
220+ "Disable for faster bulk filling of many paths." );
221+ splitFillsCheckbox .addActionListener (e -> plugin .getPrefs ().set (SNTPrefs .SPLIT_FILLS_KEY , splitFillsCheckbox .isSelected ()));
222+
223+ storeExtraNodesCheckbox = new JCheckBox ("Store above-threshold nodes" );
224+ storeExtraNodesCheckbox .addActionListener (e -> plugin .setStoreExtraFillNodes (storeExtraNodesCheckbox .isSelected ()));
225+ storeExtraNodesCheckbox .setToolTipText ("Enabling this option lets you resume progress with the same fill,\n but may impact performance" );
226+
211227 transparentCheckbox = new JCheckBox ("Transparent overlay" );
212228 transparentCheckbox .setToolTipText ("Enabling this option allows you better inspect fills,\n but may slow down filling" );
213229 transparentCheckbox .addActionListener (e -> plugin .setFillTransparent (transparentCheckbox .isSelected ()));
214- final JPanel transparencyPanel = leftAlignedPanel ();
215- transparencyPanel .add (transparentCheckbox );
216- add (transparencyPanel , c );
230+
231+ final int prevLeftMargin = c .insets .left ;
232+ c .insets .left = MARGIN ;
233+ add (splitFillsCheckbox , c );
217234 c .gridy ++;
218- storeExtraNodesCheckbox = new JCheckBox (" Store above-threshold nodes" );
219- storeExtraNodesCheckbox .addActionListener (e -> plugin .setStoreExtraFillNodes (storeExtraNodesCheckbox .isSelected ()));
220- storeExtraNodesCheckbox .setToolTipText ("Enabling this option lets you resume progress with the same fill,\n but may impact performance" );
221- final JPanel storeExtraNodesPanel = leftAlignedPanel ();
222- storeExtraNodesPanel .add (storeExtraNodesCheckbox );
223- add (storeExtraNodesPanel , c );
235+ add (storeExtraNodesCheckbox , c );
224236 c .gridy ++;
225-
237+ add (transparentCheckbox , c );
238+ ++c .gridy ;
239+ c .insets .left = prevLeftMargin ;
226240 GuiUtils .addSeparator ((JComponent ) getContentPane (), " Stored Fill(s):" , true , c );
227241 ++c .gridy ;
228242
@@ -249,7 +263,7 @@ public void focusGained(java.awt.event.FocusEvent e) {
249263 });
250264 reloadFill = new JButton ("Reload" );
251265 reloadFill .addActionListener (e -> {
252- if (!noFillsError ()) reload ("Reload" );
266+ if (!noFillsError () && ! noValidImgError () ) reload ("Reload" );
253267 });
254268
255269 assembleExportFillsMenu ();
@@ -325,8 +339,8 @@ private void setPlaceholderStatusLabels() {
325339 }
326340
327341 protected void updateSettingsString () {
328- String sb = "Image: " + ((plugin .isTracingOnSecondaryImageActive ()) ? "Secondary" : "Main" ) +
329- "; Cost func.: " + plugin .getCostType ();
342+ final String sb = "<HTML> Image: <i> " + ((plugin .isTracingOnSecondaryImageActive ()) ? "Secondary" : "Main" ) +
343+ "</i> · Cost ƒ(x): <i> " + plugin .getCostType ();
330344 fillTypeLabel .setText (sb );
331345 }
332346
@@ -486,6 +500,18 @@ public void setFillTransparent(final boolean transparent) {
486500 plugin .setFillTransparent (transparent );
487501 }
488502
503+ /**
504+ * Sets whether each path should be filled independently.
505+ *
506+ * @param split true to enable "one fill per path", false for bulk filling
507+ */
508+ public void setSplitFills (final boolean split ) {
509+ if (this .splitFillsCheckbox != null ) {
510+ SwingUtilities .invokeLater (() -> this .splitFillsCheckbox .setSelected (split ));
511+ }
512+ plugin .getPrefs ().set (SNTPrefs .SPLIT_FILLS_KEY , split );
513+ }
514+
489515 /* (non-Javadoc)
490516 * @see PathAndFillListener#setPathList(java.lang.String[], Path, boolean)
491517 */
@@ -643,17 +669,21 @@ public void execute() {
643669 return ;
644670 }
645671 try {
672+ final boolean split = plugin .getPrefs ().getBoolean (SNTPrefs .SPLIT_FILLS_KEY , splitFillsCheckbox != null
673+ && splitFillsCheckbox .isSelected ());
646674 if (plugin .fillerSet .isEmpty ()) {
647675 if (plugin .getUI ().getPathManager ().selectionExists ()) {
648- plugin .initPathsToFill (new HashSet <>(plugin .getUI ().getPathManager ().getSelectedPaths (false )));
676+ plugin .initPathsToFill (
677+ new HashSet <>(plugin .getUI ().getPathManager ().getSelectedPaths (false )), split );
649678 applyCheckboxSelections ();
650679 plugin .startFilling ();
651680 } else {
652681 final int ans = gUtils .yesNoDialog ("There are no paths selected in Path Manager. Would you like to "
653682 + "fill all paths? Alternatively, you can dismiss this prompt, select a subset in the Path "
654683 + "Manager list, and rerun. " , "Fill All Paths?" , "Yes. Fill All" , "No. I'll Select A Subset" );
655684 if (ans == JOptionPane .YES_OPTION ) {
656- plugin .initPathsToFill (new HashSet <>(plugin .getUI ().getPathManager ().getSelectedPaths (true )));
685+ plugin .initPathsToFill (
686+ new HashSet <>(plugin .getUI ().getPathManager ().getSelectedPaths (true )), split );
657687 applyCheckboxSelections ();
658688 plugin .startFilling ();
659689 }
0 commit comments