mirror of
https://github.com/xpipe-io/xpipe.git
synced 2024-07-01 04:21:11 +12:00
Accessibility improvements
This commit is contained in:
parent
544597c267
commit
fcc47b9038
|
@ -6,6 +6,7 @@ import io.xpipe.app.comp.storage.store.StoreViewState;
|
||||||
import io.xpipe.app.fxcomps.SimpleComp;
|
import io.xpipe.app.fxcomps.SimpleComp;
|
||||||
import io.xpipe.app.fxcomps.impl.IconButtonComp;
|
import io.xpipe.app.fxcomps.impl.IconButtonComp;
|
||||||
import io.xpipe.app.fxcomps.impl.PrettyImageComp;
|
import io.xpipe.app.fxcomps.impl.PrettyImageComp;
|
||||||
|
import io.xpipe.app.fxcomps.util.PlatformThread;
|
||||||
import io.xpipe.core.store.DataStore;
|
import io.xpipe.core.store.DataStore;
|
||||||
import io.xpipe.core.store.ShellStore;
|
import io.xpipe.core.store.ShellStore;
|
||||||
import javafx.application.Platform;
|
import javafx.application.Platform;
|
||||||
|
@ -13,6 +14,7 @@ import javafx.beans.property.*;
|
||||||
import javafx.collections.SetChangeListener;
|
import javafx.collections.SetChangeListener;
|
||||||
import javafx.css.PseudoClass;
|
import javafx.css.PseudoClass;
|
||||||
import javafx.geometry.Point2D;
|
import javafx.geometry.Point2D;
|
||||||
|
import javafx.scene.AccessibleRole;
|
||||||
import javafx.scene.Node;
|
import javafx.scene.Node;
|
||||||
import javafx.scene.control.TreeCell;
|
import javafx.scene.control.TreeCell;
|
||||||
import javafx.scene.control.TreeItem;
|
import javafx.scene.control.TreeItem;
|
||||||
|
@ -47,7 +49,7 @@ final class BrowserBookmarkList extends SimpleComp {
|
||||||
return new StoreCell();
|
return new StoreCell();
|
||||||
});
|
});
|
||||||
|
|
||||||
model.getSelected().addListener((observable, oldValue, newValue) -> {
|
PlatformThread.sync(model.getSelected()).addListener((observable, oldValue, newValue) -> {
|
||||||
if (newValue == null) {
|
if (newValue == null) {
|
||||||
view.getSelectionModel().clearSelection();
|
view.getSelectionModel().clearSelection();
|
||||||
return;
|
return;
|
||||||
|
@ -95,6 +97,7 @@ final class BrowserBookmarkList extends SimpleComp {
|
||||||
|
|
||||||
private StoreCell() {
|
private StoreCell() {
|
||||||
disableProperty().bind(busy);
|
disableProperty().bind(busy);
|
||||||
|
setAccessibleRole(AccessibleRole.BUTTON);
|
||||||
setGraphic(imageView);
|
setGraphic(imageView);
|
||||||
addEventHandler(DragEvent.DRAG_OVER, mouseEvent -> {
|
addEventHandler(DragEvent.DRAG_OVER, mouseEvent -> {
|
||||||
if (getItem() == null) {
|
if (getItem() == null) {
|
||||||
|
@ -131,7 +134,8 @@ final class BrowserBookmarkList extends SimpleComp {
|
||||||
})
|
})
|
||||||
.apply(struc -> struc.get().setPrefWidth(25))
|
.apply(struc -> struc.get().setPrefWidth(25))
|
||||||
.grow(false, true)
|
.grow(false, true)
|
||||||
.styleClass("expand-button");
|
.styleClass("expand-button")
|
||||||
|
.apply(struc -> struc.get().setFocusTraversable(false));
|
||||||
|
|
||||||
setDisclosureNode(button.createRegion());
|
setDisclosureNode(button.createRegion());
|
||||||
}
|
}
|
||||||
|
@ -145,12 +149,16 @@ final class BrowserBookmarkList extends SimpleComp {
|
||||||
// and cells are emptied on each change, leading to unnecessary changes
|
// and cells are emptied on each change, leading to unnecessary changes
|
||||||
// img.set(null);
|
// img.set(null);
|
||||||
setGraphic(null);
|
setGraphic(null);
|
||||||
|
setFocusTraversable(false);
|
||||||
|
setAccessibleText(null);
|
||||||
} else {
|
} else {
|
||||||
setText(item.getName());
|
setText(item.getName());
|
||||||
img.set(item.getEntry()
|
img.set(item.getEntry()
|
||||||
.getProvider()
|
.getProvider()
|
||||||
.getDisplayIconFileName(item.getEntry().getStore()));
|
.getDisplayIconFileName(item.getEntry().getStore()));
|
||||||
setGraphic(imageView);
|
setGraphic(imageView);
|
||||||
|
setFocusTraversable(true);
|
||||||
|
setAccessibleText(item.getName() + " " + item.getEntry().getProvider().getDisplayName());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -165,7 +165,8 @@ public class BrowserComp extends SimpleComp {
|
||||||
// Handle selection from model
|
// Handle selection from model
|
||||||
model.getSelected().addListener((observable, oldValue, newValue) -> {
|
model.getSelected().addListener((observable, oldValue, newValue) -> {
|
||||||
PlatformThread.runLaterIfNeeded(() -> {
|
PlatformThread.runLaterIfNeeded(() -> {
|
||||||
tabs.getSelectionModel().select(model.getOpenFileSystems().indexOf(newValue));
|
var tab = tabs.getTabs().get(model.getOpenFileSystems().indexOf(newValue));
|
||||||
|
tabs.getSelectionModel().select(tab);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -258,6 +259,7 @@ public class BrowserComp extends SimpleComp {
|
||||||
new FancyTooltipAugment<>(new SimpleStringProperty(model.getName())).augment(label);
|
new FancyTooltipAugment<>(new SimpleStringProperty(model.getName())).augment(label);
|
||||||
GrowAugment.create(true, false).augment(new SimpleCompStructure<>(label));
|
GrowAugment.create(true, false).augment(new SimpleCompStructure<>(label));
|
||||||
tab.setContent(new OpenFileSystemComp(model).createSimple());
|
tab.setContent(new OpenFileSystemComp(model).createSimple());
|
||||||
|
tab.setText(model.getName());
|
||||||
return tab;
|
return tab;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,6 +26,7 @@ import javafx.collections.ListChangeListener;
|
||||||
import javafx.css.PseudoClass;
|
import javafx.css.PseudoClass;
|
||||||
import javafx.geometry.Bounds;
|
import javafx.geometry.Bounds;
|
||||||
import javafx.geometry.Pos;
|
import javafx.geometry.Pos;
|
||||||
|
import javafx.scene.AccessibleRole;
|
||||||
import javafx.scene.Node;
|
import javafx.scene.Node;
|
||||||
import javafx.scene.control.*;
|
import javafx.scene.control.*;
|
||||||
import javafx.scene.control.skin.TableViewSkin;
|
import javafx.scene.control.skin.TableViewSkin;
|
||||||
|
@ -100,10 +101,12 @@ final class BrowserFileListComp extends SimpleComp {
|
||||||
modeCol.setSortable(false);
|
modeCol.setSortable(false);
|
||||||
|
|
||||||
var table = new TableView<BrowserEntry>();
|
var table = new TableView<BrowserEntry>();
|
||||||
|
table.setAccessibleText("Directory contents");
|
||||||
table.setPlaceholder(new Region());
|
table.setPlaceholder(new Region());
|
||||||
table.getStyleClass().add(Styles.STRIPED);
|
table.getStyleClass().add(Styles.STRIPED);
|
||||||
table.getColumns().setAll(filenameCol, sizeCol, modeCol, mtimeCol);
|
table.getColumns().setAll(filenameCol, sizeCol, modeCol, mtimeCol);
|
||||||
table.getSortOrder().add(filenameCol);
|
table.getSortOrder().add(filenameCol);
|
||||||
|
table.setFocusTraversable(true);
|
||||||
table.setSortPolicy(param -> {
|
table.setSortPolicy(param -> {
|
||||||
var comp = table.getComparator();
|
var comp = table.getComparator();
|
||||||
if (comp == null) {
|
if (comp == null) {
|
||||||
|
@ -229,6 +232,12 @@ final class BrowserFileListComp extends SimpleComp {
|
||||||
|
|
||||||
table.setRowFactory(param -> {
|
table.setRowFactory(param -> {
|
||||||
TableRow<BrowserEntry> row = new TableRow<>();
|
TableRow<BrowserEntry> row = new TableRow<>();
|
||||||
|
row.accessibleTextProperty().bind(Bindings.createStringBinding(() -> {
|
||||||
|
return row.getItem() != null ? row.getItem().getFileName() : null;
|
||||||
|
}, row.itemProperty()));
|
||||||
|
row.focusTraversableProperty().bind(Bindings.createBooleanBinding(() -> {
|
||||||
|
return row.getItem() != null;
|
||||||
|
}, row.itemProperty()));
|
||||||
new ContextMenuAugment<>(event -> {
|
new ContextMenuAugment<>(event -> {
|
||||||
if (row.getItem() == null) {
|
if (row.getItem() == null) {
|
||||||
return event.getButton() == MouseButton.SECONDARY;
|
return event.getButton() == MouseButton.SECONDARY;
|
||||||
|
@ -405,6 +414,11 @@ final class BrowserFileListComp extends SimpleComp {
|
||||||
private final BooleanProperty updating = new SimpleBooleanProperty();
|
private final BooleanProperty updating = new SimpleBooleanProperty();
|
||||||
|
|
||||||
public FilenameCell(Property<BrowserEntry> editing) {
|
public FilenameCell(Property<BrowserEntry> editing) {
|
||||||
|
accessibleTextProperty().bind(Bindings.createStringBinding(() -> {
|
||||||
|
return getItem() != null ? getItem() : null;
|
||||||
|
}, itemProperty()));
|
||||||
|
setAccessibleRole(AccessibleRole.TEXT);
|
||||||
|
|
||||||
editing.addListener((observable, oldValue, newValue) -> {
|
editing.addListener((observable, oldValue, newValue) -> {
|
||||||
if (getTableRow().getItem() != null && getTableRow().getItem().equals(newValue)) {
|
if (getTableRow().getItem() != null && getTableRow().getItem().equals(newValue)) {
|
||||||
PlatformThread.runLaterIfNeeded(() -> textField.requestFocus());
|
PlatformThread.runLaterIfNeeded(() -> textField.requestFocus());
|
||||||
|
|
|
@ -86,7 +86,7 @@ public class BrowserFileListCompEntry {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prevent dropping items onto themselves
|
// Prevent dropping items onto themselves
|
||||||
if (item != null && BrowserClipboard.currentDragClipboard.getEntries().contains(item)) {
|
if (item != null && BrowserClipboard.currentDragClipboard.getEntries().contains(item.getRawFileEntry())) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -71,7 +71,7 @@ public class BrowserNavBar extends SimpleComp {
|
||||||
struc.get().setPromptText("Overview of " + model.getName());
|
struc.get().setPromptText("Overview of " + model.getName());
|
||||||
}).shortcut(new KeyCodeCombination(KeyCode.F, KeyCombination.SHORTCUT_DOWN), s -> {
|
}).shortcut(new KeyCodeCombination(KeyCode.F, KeyCombination.SHORTCUT_DOWN), s -> {
|
||||||
s.get().requestFocus();
|
s.get().requestFocus();
|
||||||
});
|
}).accessibleText("Current path");
|
||||||
|
|
||||||
var graphic = Bindings.createStringBinding(
|
var graphic = Bindings.createStringBinding(
|
||||||
() -> {
|
() -> {
|
||||||
|
@ -87,6 +87,7 @@ public class BrowserNavBar extends SimpleComp {
|
||||||
.createRegion();
|
.createRegion();
|
||||||
|
|
||||||
var graphicButton = new Button(null, breadcrumbsGraphic);
|
var graphicButton = new Button(null, breadcrumbsGraphic);
|
||||||
|
graphicButton.setAccessibleText("Directory options");
|
||||||
graphicButton.getStyleClass().add(Styles.LEFT_PILL);
|
graphicButton.getStyleClass().add(Styles.LEFT_PILL);
|
||||||
graphicButton.getStyleClass().add("path-graphic-button");
|
graphicButton.getStyleClass().add("path-graphic-button");
|
||||||
new ContextMenuAugment<>(
|
new ContextMenuAugment<>(
|
||||||
|
|
|
@ -45,6 +45,7 @@ public class OpenFileSystemComp extends SimpleComp {
|
||||||
var overview = new Button(null, new FontIcon("mdi2m-monitor"));
|
var overview = new Button(null, new FontIcon("mdi2m-monitor"));
|
||||||
overview.setOnAction(e -> model.cd(null));
|
overview.setOnAction(e -> model.cd(null));
|
||||||
overview.disableProperty().bind(model.getInOverview());
|
overview.disableProperty().bind(model.getInOverview());
|
||||||
|
overview.setAccessibleText("System overview");
|
||||||
|
|
||||||
var backBtn = BrowserAction.byId("back").toButton(model, List.of());
|
var backBtn = BrowserAction.byId("back").toButton(model, List.of());
|
||||||
var forthBtn = BrowserAction.byId("forward").toButton(model, List.of());
|
var forthBtn = BrowserAction.byId("forward").toButton(model, List.of());
|
||||||
|
@ -56,6 +57,7 @@ public class OpenFileSystemComp extends SimpleComp {
|
||||||
event -> event.getButton() == MouseButton.PRIMARY, () -> new BrowserContextMenu(model, null))
|
event -> event.getButton() == MouseButton.PRIMARY, () -> new BrowserContextMenu(model, null))
|
||||||
.augment(new SimpleCompStructure<>(menuButton));
|
.augment(new SimpleCompStructure<>(menuButton));
|
||||||
menuButton.disableProperty().bind(model.getInOverview());
|
menuButton.disableProperty().bind(model.getInOverview());
|
||||||
|
menuButton.setAccessibleText("Directory options");
|
||||||
|
|
||||||
var filter = new BrowserFilterComp(model, model.getFilter()).createStructure();
|
var filter = new BrowserFilterComp(model, model.getFilter()).createStructure();
|
||||||
Shortcuts.addShortcut(filter.toggleButton(), new KeyCodeCombination(KeyCode.F, KeyCombination.SHORTCUT_DOWN));
|
Shortcuts.addShortcut(filter.toggleButton(), new KeyCodeCombination(KeyCode.F, KeyCombination.SHORTCUT_DOWN));
|
||||||
|
|
|
@ -36,6 +36,7 @@ public interface LeafAction extends BrowserAction {
|
||||||
b.setGraphic(graphic);
|
b.setGraphic(graphic);
|
||||||
}
|
}
|
||||||
b.setMnemonicParsing(false);
|
b.setMnemonicParsing(false);
|
||||||
|
b.setAccessibleText(getName(model, selected));
|
||||||
|
|
||||||
b.setDisable(!isActive(model, selected));
|
b.setDisable(!isActive(model, selected));
|
||||||
model.getCurrentPath().addListener((observable, oldValue, newValue) -> {
|
model.getCurrentPath().addListener((observable, oldValue, newValue) -> {
|
||||||
|
|
|
@ -34,6 +34,7 @@ public class BigIconButton extends ButtonComp {
|
||||||
vbox.getChildren().add(label);
|
vbox.getChildren().add(label);
|
||||||
|
|
||||||
var b = new Button(null);
|
var b = new Button(null);
|
||||||
|
b.accessibleTextProperty().bind(getName());
|
||||||
b.setGraphic(vbox);
|
b.setGraphic(vbox);
|
||||||
b.setOnAction(e -> getListener().run());
|
b.setOnAction(e -> getListener().run());
|
||||||
b.getStyleClass().add("big-icon-button-comp");
|
b.getStyleClass().add("big-icon-button-comp");
|
||||||
|
|
|
@ -68,7 +68,7 @@ public class DataSourceTargetChoiceComp extends Comp<CompStructure<ComboBox<Node
|
||||||
var addMoreLabel = new Label(AppI18n.get("addMore"), new FontIcon("mdmz-plus"));
|
var addMoreLabel = new Label(AppI18n.get("addMore"), new FontIcon("mdmz-plus"));
|
||||||
|
|
||||||
var builder = new CustomComboBoxBuilder<DataSourceTarget>(
|
var builder = new CustomComboBoxBuilder<DataSourceTarget>(
|
||||||
selectedApplication, app -> createLabel(app), new Label(""), v -> true);
|
selectedApplication, app -> createLabel(app), dataSourceTarget -> dataSourceTarget.getName().getValue(), new Label(""), v -> true);
|
||||||
|
|
||||||
// builder.addFilter((v, s) -> v.getName().getValue().toLowerCase().contains(s));
|
// builder.addFilter((v, s) -> v.getName().getValue().toLowerCase().contains(s));
|
||||||
|
|
||||||
|
|
|
@ -72,7 +72,7 @@ public class DsProviderChoiceComp extends Comp<CompStructure<ComboBox<Node>>> im
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CompStructure<ComboBox<Node>> createBase() {
|
public CompStructure<ComboBox<Node>> createBase() {
|
||||||
var comboBox = new CustomComboBoxBuilder<>(provider, this::createGraphic, createDefaultNode(), v -> true);
|
var comboBox = new CustomComboBoxBuilder<>(provider, this::createGraphic, dataSourceProvider -> dataSourceProvider.getDisplayName(), createDefaultNode(), v -> true);
|
||||||
comboBox.add(null);
|
comboBox.add(null);
|
||||||
comboBox.addSeparator();
|
comboBox.addSeparator();
|
||||||
comboBox.addFilter((v, s) -> v.getDisplayName().toLowerCase().contains(s.toLowerCase()));
|
comboBox.addFilter((v, s) -> v.getDisplayName().toLowerCase().contains(s.toLowerCase()));
|
||||||
|
|
|
@ -30,7 +30,7 @@ public class DsStorageGroupSelector extends SimpleComp {
|
||||||
@Override
|
@Override
|
||||||
protected ComboBox<Node> createSimple() {
|
protected ComboBox<Node> createSimple() {
|
||||||
var comboBox = new CustomComboBoxBuilder<DataSourceCollection>(
|
var comboBox = new CustomComboBoxBuilder<DataSourceCollection>(
|
||||||
selected, DsStorageGroupSelector::createGraphic, createGraphic(null), v -> true);
|
selected, DsStorageGroupSelector::createGraphic, dataSourceCollection -> dataSourceCollection.getName(), createGraphic(null), v -> true);
|
||||||
|
|
||||||
DataStorage.get().getSourceCollections().stream()
|
DataStorage.get().getSourceCollections().stream()
|
||||||
.filter(dataSourceCollection ->
|
.filter(dataSourceCollection ->
|
||||||
|
|
|
@ -50,7 +50,7 @@ public class DsTypeChoiceComp extends Comp<CompStructure<StackPane>> {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var builder = new CustomComboBoxBuilder<>(selectedType, app -> createLabel(app), new Label(""), v -> true);
|
var builder = new CustomComboBoxBuilder<>(selectedType, app -> createLabel(app), dataSourceType -> dataSourceType.toString(), new Label(""), v -> true);
|
||||||
builder.add(provider.getValue().getPrimaryType());
|
builder.add(provider.getValue().getPrimaryType());
|
||||||
|
|
||||||
var list = Arrays.stream(DataSourceType.values())
|
var list = Arrays.stream(DataSourceType.values())
|
||||||
|
|
|
@ -49,7 +49,7 @@ public class DsStoreProviderChoiceComp extends Comp<CompStructure<ComboBox<Node>
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CompStructure<ComboBox<Node>> createBase() {
|
public CompStructure<ComboBox<Node>> createBase() {
|
||||||
var comboBox = new CustomComboBoxBuilder<>(provider, this::createGraphic, createDefaultNode(), v -> true);
|
var comboBox = new CustomComboBoxBuilder<>(provider, this::createGraphic, dataStoreProvider -> dataStoreProvider.getDisplayName(), createDefaultNode(), v -> true);
|
||||||
getProviders().stream()
|
getProviders().stream()
|
||||||
.filter(p -> AppPrefs.get().developerShowHiddenProviders().get() || p.shouldShow())
|
.filter(p -> AppPrefs.get().developerShowHiddenProviders().get() || p.shouldShow())
|
||||||
.forEach(comboBox::add);
|
.forEach(comboBox::add);
|
||||||
|
|
|
@ -148,7 +148,11 @@ public class StoreEntryComp extends SimpleComp {
|
||||||
GrowAugment.create(true, false).augment(new SimpleCompStructure<>(grid));
|
GrowAugment.create(true, false).augment(new SimpleCompStructure<>(grid));
|
||||||
button.getStyleClass().add("store-entry-comp");
|
button.getStyleClass().add("store-entry-comp");
|
||||||
button.setMaxWidth(2000);
|
button.setMaxWidth(2000);
|
||||||
button.setFocusTraversable(false);
|
button.setFocusTraversable(true);
|
||||||
|
button.accessibleTextProperty().bind(Bindings.createStringBinding(() -> {
|
||||||
|
return entry.getName();
|
||||||
|
}, entry.nameProperty()));
|
||||||
|
button.accessibleHelpProperty().bind(entry.getInformation());
|
||||||
button.setOnAction(event -> {
|
button.setOnAction(event -> {
|
||||||
event.consume();
|
event.consume();
|
||||||
ThreadHelper.runFailableAsync(() -> {
|
ThreadHelper.runFailableAsync(() -> {
|
||||||
|
@ -214,6 +218,7 @@ public class StoreEntryComp extends SimpleComp {
|
||||||
private Comp<?> createSettingsButton() {
|
private Comp<?> createSettingsButton() {
|
||||||
var settingsButton = new IconButtonComp("mdomz-settings");
|
var settingsButton = new IconButtonComp("mdomz-settings");
|
||||||
settingsButton.styleClass("settings");
|
settingsButton.styleClass("settings");
|
||||||
|
settingsButton.accessibleText("Settings");
|
||||||
settingsButton.apply(new ContextMenuAugment<>(event -> event.getButton() == MouseButton.PRIMARY, () -> StoreEntryComp.this.createContextMenu()));
|
settingsButton.apply(new ContextMenuAugment<>(event -> event.getButton() == MouseButton.PRIMARY, () -> StoreEntryComp.this.createContextMenu()));
|
||||||
settingsButton.apply(GrowAugment.create(false, true));
|
settingsButton.apply(GrowAugment.create(false, true));
|
||||||
settingsButton.apply(s -> {
|
settingsButton.apply(s -> {
|
||||||
|
|
|
@ -36,6 +36,8 @@ public class StoreEntrySection extends Comp<CompStructure<VBox>> {
|
||||||
section.getWrapper().toggleExpanded();
|
section.getWrapper().toggleExpanded();
|
||||||
})
|
})
|
||||||
.apply(struc -> struc.get().setPrefWidth(40))
|
.apply(struc -> struc.get().setPrefWidth(40))
|
||||||
|
.focusTraversable()
|
||||||
|
.accessibleText("Expand")
|
||||||
.disable(BindingsHelper.persist(
|
.disable(BindingsHelper.persist(
|
||||||
Bindings.size(section.getChildren()).isEqualTo(0)))
|
Bindings.size(section.getChildren()).isEqualTo(0)))
|
||||||
.grow(false, true).styleClass("expand-button");
|
.grow(false, true).styleClass("expand-button");
|
||||||
|
|
|
@ -59,6 +59,14 @@ public abstract class Comp<S extends CompStructure<?>> {
|
||||||
return apply(struc -> VBox.setVgrow(struc.get(), Priority.ALWAYS));
|
return apply(struc -> VBox.setVgrow(struc.get(), Priority.ALWAYS));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Comp<S> focusTraversable() {
|
||||||
|
return apply(struc -> struc.get().setFocusTraversable(true));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Comp<S> focusTraversable(boolean b) {
|
||||||
|
return apply(struc -> struc.get().setFocusTraversable(b));
|
||||||
|
}
|
||||||
|
|
||||||
public Comp<S> visible(ObservableValue<Boolean> o) {
|
public Comp<S> visible(ObservableValue<Boolean> o) {
|
||||||
return apply(struc -> struc.get().visibleProperty().bind(o));
|
return apply(struc -> struc.get().visibleProperty().bind(o));
|
||||||
}
|
}
|
||||||
|
@ -90,6 +98,11 @@ public abstract class Comp<S extends CompStructure<?>> {
|
||||||
return apply(struc -> struc.get().getStyleClass().add(styleClass));
|
return apply(struc -> struc.get().getStyleClass().add(styleClass));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Comp<S> accessibleText(String text) {
|
||||||
|
return apply(struc -> struc.get().setAccessibleText(text));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public Comp<S> grow(boolean width, boolean height) {
|
public Comp<S> grow(boolean width, boolean height) {
|
||||||
return apply(GrowAugment.create(width, height));
|
return apply(GrowAugment.create(width, height));
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,7 +24,7 @@ public class CharsetChoiceComp extends SimpleComp {
|
||||||
return new Label(streamCharset.getCharset().displayName()
|
return new Label(streamCharset.getCharset().displayName()
|
||||||
+ (streamCharset.hasByteOrderMark() ? " (BOM)" : ""));
|
+ (streamCharset.hasByteOrderMark() ? " (BOM)" : ""));
|
||||||
},
|
},
|
||||||
new Label(AppI18n.get("app.none")),
|
streamCharset -> streamCharset.getNames().get(0), new Label(AppI18n.get("app.none")),
|
||||||
null);
|
null);
|
||||||
builder.addFilter((charset, filter) -> {
|
builder.addFilter((charset, filter) -> {
|
||||||
return charset.getCharset().displayName().contains(filter);
|
return charset.getCharset().displayName().contains(filter);
|
||||||
|
|
|
@ -59,8 +59,8 @@ public class DataStoreChoiceComp<T extends DataStore> extends SimpleComp {
|
||||||
|
|
||||||
protected Region createGraphic(T s) {
|
protected Region createGraphic(T s) {
|
||||||
var provider = DataStoreProviders.byStore(s);
|
var provider = DataStoreProviders.byStore(s);
|
||||||
var imgView =
|
var imgView = new PrettyImageComp(new SimpleStringProperty(provider.getDisplayIconFileName(s)), 16, 16)
|
||||||
new PrettyImageComp(new SimpleStringProperty(provider.getDisplayIconFileName(s)), 16, 16).createRegion();
|
.createRegion();
|
||||||
|
|
||||||
var name = DataStorage.get().getUsableStores().stream()
|
var name = DataStorage.get().getUsableStores().stream()
|
||||||
.filter(e -> e.equals(s))
|
.filter(e -> e.equals(s))
|
||||||
|
@ -77,6 +77,14 @@ public class DataStoreChoiceComp<T extends DataStore> extends SimpleComp {
|
||||||
return new Label(name, imgView);
|
return new Label(name, imgView);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String toName(DataStore store) {
|
||||||
|
if (mode == Mode.PROXY && store instanceof ShellStore && ShellStore.isLocal(store.asNeeded())) {
|
||||||
|
return AppI18n.get("none");
|
||||||
|
}
|
||||||
|
|
||||||
|
return XPipeDaemon.getInstance().getStoreName(store).orElse("?");
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
protected Region createSimple() {
|
protected Region createSimple() {
|
||||||
|
@ -88,6 +96,7 @@ public class DataStoreChoiceComp<T extends DataStore> extends SimpleComp {
|
||||||
.findFirst()
|
.findFirst()
|
||||||
.orElseThrow()
|
.orElseThrow()
|
||||||
.createRegion(),
|
.createRegion(),
|
||||||
|
t -> toName(t),
|
||||||
new Label(AppI18n.get("none")),
|
new Label(AppI18n.get("none")),
|
||||||
n -> true);
|
n -> true);
|
||||||
comboBox.setSelectedDisplay(t -> createGraphic(t));
|
comboBox.setSelectedDisplay(t -> createGraphic(t));
|
||||||
|
|
|
@ -60,7 +60,7 @@ public class FileSystemStoreChoiceComp extends SimpleComp {
|
||||||
});
|
});
|
||||||
|
|
||||||
var comboBox =
|
var comboBox =
|
||||||
new CustomComboBoxBuilder<FileSystemStore>(fileSystemProperty, this::createGraphic, null, v -> true);
|
new CustomComboBoxBuilder<FileSystemStore>(fileSystemProperty, this::createGraphic, store -> getName(store), null, v -> true);
|
||||||
comboBox.setSelectedDisplay(this::createDisplayGraphic);
|
comboBox.setSelectedDisplay(this::createDisplayGraphic);
|
||||||
DataStorage.get().getUsableStores().stream()
|
DataStorage.get().getUsableStores().stream()
|
||||||
.filter(e -> e instanceof FileSystemStore)
|
.filter(e -> e instanceof FileSystemStore)
|
||||||
|
|
|
@ -34,6 +34,7 @@ public class IconButtonComp extends Comp<CompStructure<JFXButton>> {
|
||||||
var button = new JFXButton();
|
var button = new JFXButton();
|
||||||
|
|
||||||
var fi = new FontIcon(icon.getValue());
|
var fi = new FontIcon(icon.getValue());
|
||||||
|
fi.setFocusTraversable(false);
|
||||||
icon.addListener((c, o, n) -> {
|
icon.addListener((c, o, n) -> {
|
||||||
fi.setIconLiteral(n);
|
fi.setIconLiteral(n);
|
||||||
});
|
});
|
||||||
|
|
|
@ -107,6 +107,8 @@ public class OptionsComp extends Comp<CompStructure<Pane>> {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (compRegion != null) {
|
if (compRegion != null) {
|
||||||
|
compRegion.accessibleTextProperty().bind(name.textProperty());
|
||||||
|
compRegion.accessibleHelpProperty().bind(description.textProperty());
|
||||||
line.getChildren().add(compRegion);
|
line.getChildren().add(compRegion);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -132,6 +134,7 @@ public class OptionsComp extends Comp<CompStructure<Pane>> {
|
||||||
line.getChildren().add(name);
|
line.getChildren().add(name);
|
||||||
|
|
||||||
if (compRegion != null) {
|
if (compRegion != null) {
|
||||||
|
compRegion.accessibleTextProperty().bind(name.textProperty());
|
||||||
compRegions.add(compRegion);
|
compRegions.add(compRegion);
|
||||||
line.getChildren().add(compRegion);
|
line.getChildren().add(compRegion);
|
||||||
HBox.setHgrow(compRegion, Priority.ALWAYS);
|
HBox.setHgrow(compRegion, Priority.ALWAYS);
|
||||||
|
|
|
@ -95,6 +95,7 @@ public class SvgView {
|
||||||
wv.setPageFill(Color.TRANSPARENT);
|
wv.setPageFill(Color.TRANSPARENT);
|
||||||
wv.getEngine().setJavaScriptEnabled(false);
|
wv.getEngine().setJavaScriptEnabled(false);
|
||||||
wv.setContextMenuEnabled(false);
|
wv.setContextMenuEnabled(false);
|
||||||
|
wv.setFocusTraversable(false);
|
||||||
|
|
||||||
wv.getEngine().loadContent(getHtml(svgContent.getValue()));
|
wv.getEngine().loadContent(getHtml(svgContent.getValue()));
|
||||||
svgContent.addListener((c, o, n) -> {
|
svgContent.addListener((c, o, n) -> {
|
||||||
|
|
|
@ -11,6 +11,7 @@ import com.dlsc.preferencesfx.formsfx.view.renderer.PreferencesFxGroupRenderer;
|
||||||
import com.dlsc.preferencesfx.util.PreferencesFxUtils;
|
import com.dlsc.preferencesfx.util.PreferencesFxUtils;
|
||||||
import io.xpipe.app.core.AppFont;
|
import io.xpipe.app.core.AppFont;
|
||||||
import io.xpipe.app.core.AppI18n;
|
import io.xpipe.app.core.AppI18n;
|
||||||
|
import io.xpipe.app.fxcomps.util.BindingsHelper;
|
||||||
import javafx.geometry.Insets;
|
import javafx.geometry.Insets;
|
||||||
import javafx.scene.control.Label;
|
import javafx.scene.control.Label;
|
||||||
import javafx.scene.layout.GridPane;
|
import javafx.scene.layout.GridPane;
|
||||||
|
@ -77,21 +78,37 @@ public class CustomFormRenderer extends PreferencesFxFormRenderer {
|
||||||
c.getFieldLabel().setMaxHeight(AppFont.getPixelSize(1));
|
c.getFieldLabel().setMaxHeight(AppFont.getPixelSize(1));
|
||||||
grid.add(c.getFieldLabel(), 0, i + rowAmount, 2, 1);
|
grid.add(c.getFieldLabel(), 0, i + rowAmount, 2, 1);
|
||||||
|
|
||||||
|
var canFocus = BindingsHelper.persist(c.getNode().disabledProperty().not());
|
||||||
|
|
||||||
var descriptionLabel = new Label();
|
var descriptionLabel = new Label();
|
||||||
descriptionLabel.setWrapText(true);
|
descriptionLabel.setWrapText(true);
|
||||||
descriptionLabel.disableProperty().bind(c.getFieldLabel().disabledProperty());
|
descriptionLabel
|
||||||
descriptionLabel.opacityProperty().bind(c.getFieldLabel().opacityProperty().multiply(0.8));
|
.disableProperty()
|
||||||
descriptionLabel.managedProperty().bind(c.getFieldLabel().managedProperty());
|
.bind(c.getFieldLabel().disabledProperty());
|
||||||
descriptionLabel.visibleProperty().bind(c.getFieldLabel().visibleProperty());
|
descriptionLabel
|
||||||
|
.opacityProperty()
|
||||||
|
.bind(c.getFieldLabel()
|
||||||
|
.opacityProperty()
|
||||||
|
.multiply(0.8));
|
||||||
|
descriptionLabel
|
||||||
|
.managedProperty()
|
||||||
|
.bind(c.getFieldLabel().managedProperty());
|
||||||
|
descriptionLabel
|
||||||
|
.visibleProperty()
|
||||||
|
.bind(c.getFieldLabel().visibleProperty());
|
||||||
descriptionLabel.setMaxHeight(USE_PREF_SIZE);
|
descriptionLabel.setMaxHeight(USE_PREF_SIZE);
|
||||||
if (AppI18n.getInstance().containsKey(descriptionKey)) {
|
if (AppI18n.getInstance().containsKey(descriptionKey)) {
|
||||||
rowAmount++;
|
rowAmount++;
|
||||||
descriptionLabel.textProperty().bind(AppI18n.observable(descriptionKey));
|
descriptionLabel.textProperty().bind(AppI18n.observable(descriptionKey));
|
||||||
|
descriptionLabel.focusTraversableProperty().bind(canFocus);
|
||||||
grid.add(descriptionLabel, 0, i + rowAmount, 2, 1);
|
grid.add(descriptionLabel, 0, i + rowAmount, 2, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
rowAmount++;
|
rowAmount++;
|
||||||
grid.add(c.getNode(), 0, i + rowAmount, 1, 1);
|
|
||||||
|
var node = c.getNode();
|
||||||
|
c.getFieldLabel().focusTraversableProperty().bind(canFocus);
|
||||||
|
grid.add(node, 0, i + rowAmount, 1, 1);
|
||||||
|
|
||||||
if (i == elements.size() - 1) {
|
if (i == elements.size() - 1) {
|
||||||
// additional styling for the last setting
|
// additional styling for the last setting
|
||||||
|
@ -101,7 +118,7 @@ public class CustomFormRenderer extends PreferencesFxFormRenderer {
|
||||||
var offset = preferencesGroup.getTitle() != null ? 15 : 0;
|
var offset = preferencesGroup.getTitle() != null ? 15 : 0;
|
||||||
|
|
||||||
GridPane.setMargin(descriptionLabel, new Insets(SPACING, 0, 0, offset));
|
GridPane.setMargin(descriptionLabel, new Insets(SPACING, 0, 0, offset));
|
||||||
GridPane.setMargin(c.getNode(), new Insets(SPACING, 0, 0, offset));
|
GridPane.setMargin(node, new Insets(SPACING, 0, 0, offset));
|
||||||
|
|
||||||
if (!((i == 0) && (nextRow > 0))) {
|
if (!((i == 0) && (nextRow > 0))) {
|
||||||
GridPane.setMargin(c.getFieldLabel(), new Insets(SPACING * 3, 0, 0, offset));
|
GridPane.setMargin(c.getFieldLabel(), new Insets(SPACING * 3, 0, 0, offset));
|
||||||
|
@ -110,7 +127,7 @@ public class CustomFormRenderer extends PreferencesFxFormRenderer {
|
||||||
}
|
}
|
||||||
|
|
||||||
c.getFieldLabel().getStyleClass().add(styleClass.toString() + "-label");
|
c.getFieldLabel().getStyleClass().add(styleClass.toString() + "-label");
|
||||||
c.getNode().getStyleClass().add(styleClass.toString() + "-node");
|
node.getStyleClass().add(styleClass.toString() + "-node");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (element instanceof NodeElement nodeElement) {
|
if (element instanceof NodeElement nodeElement) {
|
||||||
|
|
|
@ -3,6 +3,7 @@ package io.xpipe.app.util;
|
||||||
import io.xpipe.app.fxcomps.impl.FilterComp;
|
import io.xpipe.app.fxcomps.impl.FilterComp;
|
||||||
import io.xpipe.app.fxcomps.util.SimpleChangeListener;
|
import io.xpipe.app.fxcomps.util.SimpleChangeListener;
|
||||||
import javafx.application.Platform;
|
import javafx.application.Platform;
|
||||||
|
import javafx.beans.binding.Bindings;
|
||||||
import javafx.beans.property.Property;
|
import javafx.beans.property.Property;
|
||||||
import javafx.beans.property.SimpleStringProperty;
|
import javafx.beans.property.SimpleStringProperty;
|
||||||
import javafx.collections.FXCollections;
|
import javafx.collections.FXCollections;
|
||||||
|
@ -25,6 +26,7 @@ public class CustomComboBoxBuilder<T> {
|
||||||
|
|
||||||
private final Property<T> selected;
|
private final Property<T> selected;
|
||||||
private final Function<T, Node> nodeFunction;
|
private final Function<T, Node> nodeFunction;
|
||||||
|
private final Function<T, String> accessibleNameFunction;
|
||||||
private Function<T, Node> selectedDisplayNodeFunction;
|
private Function<T, Node> selectedDisplayNodeFunction;
|
||||||
private final Map<Node, T> nodeMap = new HashMap<>();
|
private final Map<Node, T> nodeMap = new HashMap<>();
|
||||||
private final Map<Node, Runnable> actionsMap = new HashMap<>();
|
private final Map<Node, Runnable> actionsMap = new HashMap<>();
|
||||||
|
@ -39,10 +41,11 @@ public class CustomComboBoxBuilder<T> {
|
||||||
private Function<T, Node> unknownNode;
|
private Function<T, Node> unknownNode;
|
||||||
|
|
||||||
public CustomComboBoxBuilder(
|
public CustomComboBoxBuilder(
|
||||||
Property<T> selected, Function<T, Node> nodeFunction, Node emptyNode, Predicate<T> veto) {
|
Property<T> selected, Function<T, Node> nodeFunction, Function<T, String> accessibleNameFunction, Node emptyNode, Predicate<T> veto) {
|
||||||
this.selected = selected;
|
this.selected = selected;
|
||||||
this.nodeFunction = nodeFunction;
|
this.nodeFunction = nodeFunction;
|
||||||
this.selectedDisplayNodeFunction = nodeFunction;
|
this.selectedDisplayNodeFunction = nodeFunction;
|
||||||
|
this.accessibleNameFunction = accessibleNameFunction;
|
||||||
this.emptyNode = emptyNode;
|
this.emptyNode = emptyNode;
|
||||||
this.veto = veto;
|
this.veto = veto;
|
||||||
}
|
}
|
||||||
|
@ -66,6 +69,7 @@ public class CustomComboBoxBuilder<T> {
|
||||||
|
|
||||||
public Node add(T val) {
|
public Node add(T val) {
|
||||||
var node = nodeFunction.apply(val);
|
var node = nodeFunction.apply(val);
|
||||||
|
node.setAccessibleText(accessibleNameFunction.apply(val));
|
||||||
nodeMap.put(node, val);
|
nodeMap.put(node, val);
|
||||||
nodes.add(node);
|
nodes.add(node);
|
||||||
if (filterPredicate != null) {
|
if (filterPredicate != null) {
|
||||||
|
@ -86,6 +90,7 @@ public class CustomComboBoxBuilder<T> {
|
||||||
var header = new Label(name);
|
var header = new Label(name);
|
||||||
header.setAlignment(Pos.CENTER);
|
header.setAlignment(Pos.CENTER);
|
||||||
var v = new VBox(spacer, header, new Separator(Orientation.HORIZONTAL));
|
var v = new VBox(spacer, header, new Separator(Orientation.HORIZONTAL));
|
||||||
|
v.setAccessibleText(name);
|
||||||
v.setAlignment(Pos.CENTER);
|
v.setAlignment(Pos.CENTER);
|
||||||
nodes.add(v);
|
nodes.add(v);
|
||||||
disabledNodes.add(v);
|
disabledNodes.add(v);
|
||||||
|
@ -105,6 +110,9 @@ public class CustomComboBoxBuilder<T> {
|
||||||
|
|
||||||
public ComboBox<Node> build() {
|
public ComboBox<Node> build() {
|
||||||
var cb = new ComboBox<Node>();
|
var cb = new ComboBox<Node>();
|
||||||
|
cb.accessibleTextProperty().bind(Bindings.createStringBinding(() -> {
|
||||||
|
return selected.getValue() != null ? accessibleNameFunction.apply(selected.getValue()) : null;
|
||||||
|
}, selected));
|
||||||
cb.getItems().addAll(nodes);
|
cb.getItems().addAll(nodes);
|
||||||
cb.setCellFactory((lv) -> {
|
cb.setCellFactory((lv) -> {
|
||||||
return new Cell();
|
return new Cell();
|
||||||
|
|
Loading…
Reference in a new issue