Skip to content

Commit 0d25ecf

Browse files
committed
Make Edit > Options > Desktop... a lot nicer
When an option is toggleable, dialog has a checkbox. If not, dialog has a message declaring the state.
1 parent 92594cf commit 0d25ecf

File tree

1 file changed

+114
-84
lines changed

1 file changed

+114
-84
lines changed

src/main/java/org/scijava/desktop/options/OptionsDesktop.java

Lines changed: 114 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -29,24 +29,23 @@
2929

3030
package org.scijava.desktop.options;
3131

32-
import java.io.IOException;
33-
import java.util.List;
34-
import java.util.function.Function;
32+
import java.util.concurrent.Callable;
33+
import java.util.stream.Stream;
3534

35+
import org.scijava.ItemVisibility;
3636
import org.scijava.desktop.DesktopIntegrationProvider;
3737
import org.scijava.log.LogService;
38+
import org.scijava.module.ModuleItem;
39+
import org.scijava.module.MutableModuleItem;
3840
import org.scijava.options.OptionsPlugin;
39-
import org.scijava.platform.Platform;
4041
import org.scijava.platform.PlatformService;
4142
import org.scijava.plugin.Parameter;
4243
import org.scijava.plugin.Plugin;
43-
import org.scijava.plugin.PluginInfo;
44-
import org.scijava.plugin.SciJavaPlugin;
4544

4645
/**
4746
* Options plugin for managing desktop integration features.
4847
* <p>
49-
* Provides a UI for enabling/disabling web links (URI schemes) and
48+
* Provides controls for enabling/disabling web links (URI schemes) and
5049
* desktop icons. Settings are applied directly to the OS (not persisted
5150
* to preferences), keeping the UI in sync with actual system state.
5251
* </p>
@@ -62,104 +61,135 @@ public class OptionsDesktop extends OptionsPlugin {
6261
@Parameter(required = false)
6362
private LogService log;
6463

65-
@Parameter(label = "Enable web links", persist = false, validater = "validateWebLinks", //
66-
description = "Allow handling of URI link schemes from web browsers")
67-
private boolean webLinksEnabled;
68-
69-
@Parameter(label = "Add desktop icon", persist = false, validater = "validateDesktopIcon", //
70-
description = "Install application icon in the system menu")
71-
private boolean desktopIconPresent;
72-
73-
@Parameter(label = "Enable file type associations", persist = false, validater = "validateFileExtensions", //
74-
description = "Register supported file extensions with the operating system")
75-
private boolean fileExtensionsEnabled;
64+
private MutableModuleItem<?> webLinksItem;
65+
private MutableModuleItem<?> desktopIconItem;
66+
private MutableModuleItem<?> fileTypesItem;
7667

7768
@Override
78-
public void load() {
79-
webLinksEnabled = true;
80-
desktopIconPresent = true;
81-
fileExtensionsEnabled = true;
82-
for (final Platform platform : platformService.getTargetPlatforms()) {
83-
if (!(platform instanceof DesktopIntegrationProvider)) continue;
84-
final DesktopIntegrationProvider dip = (DesktopIntegrationProvider) platform;
85-
// If any toggleable platform setting is off, uncheck that box.
86-
if (dip.isDesktopIconToggleable() && !dip.isDesktopIconPresent()) desktopIconPresent = false;
87-
if (dip.isWebLinksToggleable() && !dip.isWebLinksEnabled()) webLinksEnabled = false;
88-
if (dip.isFileExtensionsToggleable() && !dip.isFileExtensionsEnabled()) fileExtensionsEnabled = false;
69+
public void initialize() {
70+
// Create module inputs on first initialization.
71+
if (webLinksItem == null) {
72+
webLinksItem = createInput("webLinksEnabled",
73+
"Enable web links",
74+
"Allow handling of URI link schemes from web browsers",
75+
"Web links always enabled", "Web links always disabled",
76+
isWebLinksToggleable(), isWebLinksEnabled());
77+
}
78+
if (desktopIconItem == null) {
79+
desktopIconItem = createInput("desktopIconPresent",
80+
"Add desktop icon",
81+
"Install application icon in the system menu",
82+
"Icon always present", "Icon installation not implemented",
83+
isDesktopIconToggleable(), isDesktopIconPresent());
84+
}
85+
if (fileTypesItem == null) {
86+
fileTypesItem = createInput("fileTypesEnabled",
87+
"Enable file type associations",
88+
"Register supported file extensions with the operating system",
89+
"File types always handled", "File type registration not supported",
90+
isFileExtensionsToggleable(), isFileExtensionsEnabled());
8991
}
92+
93+
// Set module inputs to match current desktop integration values.
94+
setInputValue(webLinksItem, isWebLinksEnabled());
95+
setInputValue(desktopIconItem, isDesktopIconPresent());
96+
setInputValue(fileTypesItem, isFileExtensionsEnabled());
9097
}
9198

9299
@Override
93100
public void run() {
94-
for (final Platform platform : platformService.getTargetPlatforms()) {
95-
if (!(platform instanceof DesktopIntegrationProvider)) continue;
96-
final DesktopIntegrationProvider dip = (DesktopIntegrationProvider) platform;
97-
try {
98-
dip.setWebLinksEnabled(webLinksEnabled);
99-
dip.setDesktopIconPresent(desktopIconPresent);
100-
dip.setFileExtensionsEnabled(fileExtensionsEnabled);
101+
desktopPlatforms().forEach(p -> {
102+
if (p.isWebLinksToggleable()) {
103+
final Boolean enabled = getInputValue(webLinksItem);
104+
toggle(() -> { p.setWebLinksEnabled(enabled); return null; },
105+
enabled ? "enabling web links" : "disabling web links");
101106
}
102-
catch (final IOException e) {
103-
if (log != null) {
104-
log.error("Error applying desktop integration settings", e);
105-
}
107+
if (p.isDesktopIconToggleable()) {
108+
final Boolean enabled = getInputValue(desktopIconItem);
109+
toggle(() -> { p.setDesktopIconPresent(enabled); return null; },
110+
enabled ? "adding desktop icon" : "removing desktop icon");
106111
}
107-
}
112+
if (p.isFileExtensionsToggleable()) {
113+
final Boolean enabled = getInputValue(fileTypesItem);
114+
toggle(() -> { p.setFileExtensionsEnabled(enabled); return null; },
115+
enabled ? "enabling file type associations" :
116+
"removing file type associations");
117+
}
118+
});
108119
super.run();
109120
}
110121

111-
// -- Validators --
122+
// -- Helper methods --
112123

113-
public void validateWebLinks() {
114-
validateSetting(
115-
DesktopIntegrationProvider::isWebLinksToggleable,
116-
DesktopIntegrationProvider::isWebLinksEnabled,
117-
webLinksEnabled,
118-
"Web links setting");
124+
private MutableModuleItem<?> createInput(final String name,
125+
final String label, final String description,
126+
final String alwaysOnMessage, final String alwaysOffMessage,
127+
final boolean toggleable, final boolean enabled)
128+
{
129+
final MutableModuleItem<?> item;
130+
if (toggleable) {
131+
item = addInput(name, boolean.class);
132+
item.setLabel(label);
133+
}
134+
else {
135+
item = addInput(name, String.class);
136+
item.setVisibility(ItemVisibility.MESSAGE);
137+
item.setRequired(false);
138+
item.setLabel(enabled ?
139+
alwaysOnMessage + " for this platform ✅" :
140+
alwaysOffMessage + " for this platform ❌");
141+
}
142+
item.setPersisted(false);
143+
item.setDescription(description);
144+
return item;
119145
}
120146

121-
public void validateDesktopIcon() {
122-
validateSetting(
123-
DesktopIntegrationProvider::isDesktopIconToggleable,
124-
DesktopIntegrationProvider::isDesktopIconPresent,
125-
desktopIconPresent,
126-
"Desktop icon presence");
147+
private void setInputValue(final ModuleItem<?> item, boolean value) {
148+
if (item.getType() != boolean.class) return;
149+
setInput(item.getName(), value);
127150
}
128151

129-
public void validateFileExtensions() {
130-
validateSetting(
131-
DesktopIntegrationProvider::isFileExtensionsToggleable,
132-
DesktopIntegrationProvider::isFileExtensionsEnabled,
133-
fileExtensionsEnabled,
134-
"File extensions setting");
152+
private Boolean getInputValue(final ModuleItem<?> item) {
153+
if (item.getType() != boolean.class) return null;
154+
return (Boolean) getInput(item.getName());
135155
}
136156

137-
// -- Helper methods --
157+
private void toggle(final Callable<?> toggleAction, final String errorMessage) {
158+
try {
159+
toggleAction.call();
160+
}
161+
catch (final Exception e) {
162+
if (log != null) log.error("Error " + errorMessage, e);
163+
}
164+
}
138165

139-
private String name(Platform platform) {
140-
final List<PluginInfo<SciJavaPlugin>> infos =
141-
pluginService.getPluginsOfClass(platform.getClass());
142-
return infos.isEmpty() ? null : infos.get(0).getName();
166+
private boolean isDesktopIconToggleable() {
167+
return desktopPlatforms().anyMatch(p -> p.isDesktopIconToggleable());
143168
}
144169

145-
private void validateSetting(
146-
Function<DesktopIntegrationProvider, Boolean> mutable,
147-
Function<DesktopIntegrationProvider, Boolean> getter,
148-
boolean value, String settingDescription)
149-
{
150-
boolean toggleable = false;
151-
boolean enabled = false;
152-
Platform strictPlatform = null;
153-
for (final Platform platform : platformService.getTargetPlatforms()) {
154-
if (!(platform instanceof DesktopIntegrationProvider)) continue;
155-
final DesktopIntegrationProvider dip = (DesktopIntegrationProvider) platform;
156-
if (mutable.apply(dip)) toggleable = true;
157-
else if (strictPlatform == null) strictPlatform = platform;
158-
if (getter.apply(dip)) enabled = true;
159-
}
160-
if (!toggleable && enabled != value) {
161-
final String platformName = strictPlatform == null ? "this platform" : name(strictPlatform);
162-
throw new IllegalArgumentException(settingDescription + " cannot be changed on " + platformName + ".");
163-
}
170+
private boolean isDesktopIconPresent() {
171+
return desktopPlatforms().allMatch(p -> p.isDesktopIconPresent());
172+
}
173+
174+
private boolean isWebLinksToggleable() {
175+
return desktopPlatforms().anyMatch(p -> p.isWebLinksToggleable());
176+
}
177+
178+
private boolean isWebLinksEnabled() {
179+
return desktopPlatforms().allMatch(p -> p.isWebLinksEnabled());
180+
}
181+
182+
private boolean isFileExtensionsToggleable() {
183+
return desktopPlatforms().anyMatch(p -> p.isFileExtensionsToggleable());
184+
}
185+
186+
private boolean isFileExtensionsEnabled() {
187+
return desktopPlatforms().allMatch(p -> p.isFileExtensionsEnabled());
188+
}
189+
190+
private Stream<DesktopIntegrationProvider> desktopPlatforms() {
191+
return platformService.getTargetPlatforms().stream() //
192+
.filter(p -> p instanceof DesktopIntegrationProvider) //
193+
.map(p -> (DesktopIntegrationProvider) p);
164194
}
165195
}

0 commit comments

Comments
 (0)