mirror of
https://github.com/xpipe-io/xpipe.git
synced 2024-11-22 07:30:24 +00:00
Rework keyboard usage
This commit is contained in:
parent
9ad5b6f7f5
commit
3f01bb0028
17 changed files with 240 additions and 125 deletions
|
@ -2,6 +2,7 @@ package io.xpipe.app.browser;
|
|||
|
||||
import io.xpipe.app.fxcomps.SimpleComp;
|
||||
import io.xpipe.app.fxcomps.impl.IconButtonComp;
|
||||
import io.xpipe.app.util.InputHelper;
|
||||
import javafx.application.Platform;
|
||||
import javafx.scene.control.Menu;
|
||||
import javafx.scene.layout.Region;
|
||||
|
@ -36,7 +37,7 @@ public class BrowserQuickAccessButtonComp extends SimpleComp {
|
|||
struc.get().requestFocus();
|
||||
});
|
||||
});
|
||||
BrowserQuickAccessContextMenu.onRight(struc.get(), false, keyEvent -> {
|
||||
InputHelper.onRight(struc.get(), false, keyEvent -> {
|
||||
cm.showMenu(struc.get());
|
||||
keyEvent.consume();
|
||||
});
|
||||
|
|
|
@ -3,12 +3,11 @@ package io.xpipe.app.browser;
|
|||
import io.xpipe.app.browser.icon.FileIconManager;
|
||||
import io.xpipe.app.fxcomps.impl.PrettyImageHelper;
|
||||
import io.xpipe.app.util.BooleanAnimationTimer;
|
||||
import io.xpipe.app.util.InputHelper;
|
||||
import io.xpipe.app.util.ThreadHelper;
|
||||
import io.xpipe.core.store.FileKind;
|
||||
import javafx.application.Platform;
|
||||
import javafx.beans.property.SimpleBooleanProperty;
|
||||
import javafx.event.EventHandler;
|
||||
import javafx.event.EventTarget;
|
||||
import javafx.geometry.Side;
|
||||
import javafx.scene.Node;
|
||||
import javafx.scene.control.ContextMenu;
|
||||
|
@ -24,38 +23,11 @@ import java.util.ArrayList;
|
|||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class BrowserQuickAccessContextMenu extends ContextMenu {
|
||||
|
||||
static void onLeft(EventTarget target, boolean filter, Consumer<KeyEvent> r) {
|
||||
EventHandler<KeyEvent> keyEventEventHandler = event -> {
|
||||
if (event.getCode() == KeyCode.LEFT || event.getCode() == KeyCode.NUMPAD4) {
|
||||
r.accept(event);
|
||||
}
|
||||
};
|
||||
if (filter) {
|
||||
target.addEventFilter(KeyEvent.KEY_PRESSED, keyEventEventHandler);
|
||||
} else {
|
||||
target.addEventHandler(KeyEvent.KEY_PRESSED, keyEventEventHandler);
|
||||
}
|
||||
}
|
||||
|
||||
static void onRight(EventTarget target, boolean filter, Consumer<KeyEvent> r) {
|
||||
EventHandler<KeyEvent> keyEventEventHandler = event -> {
|
||||
if (event.getCode() == KeyCode.RIGHT || event.getCode() == KeyCode.NUMPAD6) {
|
||||
r.accept(event);
|
||||
}
|
||||
};
|
||||
if (filter) {
|
||||
target.addEventFilter(KeyEvent.KEY_PRESSED, keyEventEventHandler);
|
||||
} else {
|
||||
target.addEventHandler(KeyEvent.KEY_PRESSED, keyEventEventHandler);
|
||||
}
|
||||
}
|
||||
|
||||
@Getter
|
||||
class QuickAccessMenu {
|
||||
private final BrowserEntry browserEntry;
|
||||
|
@ -107,7 +79,7 @@ public class BrowserQuickAccessContextMenu extends ContextMenu {
|
|||
var empty = new MenuItem("...");
|
||||
empty.setDisable(true);
|
||||
menu.getItems().add(empty);
|
||||
onRight(empty, true, keyEvent -> {
|
||||
InputHelper.onRight(empty, true, keyEvent -> {
|
||||
keyEvent.consume();
|
||||
});
|
||||
}
|
||||
|
@ -233,7 +205,7 @@ public class BrowserQuickAccessContextMenu extends ContextMenu {
|
|||
this.browserActionMenu.setOnAction(e -> {
|
||||
hide();
|
||||
});
|
||||
onLeft(this.browserActionMenu, true, keyEvent -> {
|
||||
InputHelper.onLeft(this.browserActionMenu, true, keyEvent -> {
|
||||
this.browserActionMenu.hide();
|
||||
keyEvent.consume();
|
||||
});
|
||||
|
@ -271,7 +243,7 @@ public class BrowserQuickAccessContextMenu extends ContextMenu {
|
|||
getItems().getFirst().getStyleableNode().requestFocus();
|
||||
});
|
||||
});
|
||||
onLeft(this, false, e -> {
|
||||
InputHelper.onLeft(this, false, e -> {
|
||||
hide();
|
||||
e.consume();
|
||||
});
|
||||
|
|
|
@ -20,6 +20,8 @@ import javafx.beans.binding.Bindings;
|
|||
import javafx.beans.property.Property;
|
||||
import javafx.css.PseudoClass;
|
||||
import javafx.scene.control.Button;
|
||||
import javafx.scene.input.KeyCode;
|
||||
import javafx.scene.input.KeyCodeCombination;
|
||||
import javafx.scene.layout.*;
|
||||
import javafx.scene.paint.Color;
|
||||
|
||||
|
@ -67,8 +69,11 @@ public class SideMenuBarComp extends Comp<CompStructure<VBox>> {
|
|||
Platform.getPreferences().accentColorProperty());
|
||||
|
||||
var selected = PseudoClass.getPseudoClass("selected");
|
||||
entries.forEach(e -> {
|
||||
var b = new IconButtonComp(e.icon(), () -> value.setValue(e)).apply(new FancyTooltipAugment<>(e.name()));
|
||||
for (int i = 0; i < entries.size(); i++) {
|
||||
var e = entries.get(i);
|
||||
var b = new IconButtonComp(e.icon(), () -> value.setValue(e));
|
||||
b.shortcut(new KeyCodeCombination(KeyCode.values()[KeyCode.DIGIT1.ordinal() + i]));
|
||||
b.apply(new FancyTooltipAugment<>(e.name()));
|
||||
b.apply(struc -> {
|
||||
AppFont.setSize(struc.get(), 2);
|
||||
struc.get().pseudoClassStateChanged(selected, value.getValue().equals(e));
|
||||
|
@ -99,7 +104,7 @@ public class SideMenuBarComp extends Comp<CompStructure<VBox>> {
|
|||
});
|
||||
b.accessibleText(e.name());
|
||||
vbox.getChildren().add(b.createRegion());
|
||||
});
|
||||
}
|
||||
|
||||
Augment<CompStructure<Button>> simpleBorders = struc -> {
|
||||
struc.get()
|
||||
|
@ -127,6 +132,7 @@ public class SideMenuBarComp extends Comp<CompStructure<VBox>> {
|
|||
}
|
||||
UserReportComp.show(event.build());
|
||||
})
|
||||
.shortcut(new KeyCodeCombination(KeyCode.values()[KeyCode.DIGIT1.ordinal() + entries.size()]))
|
||||
.apply(new FancyTooltipAugment<>("reportIssue"))
|
||||
.apply(simpleBorders)
|
||||
.accessibleTextKey("reportIssue");
|
||||
|
@ -138,6 +144,7 @@ public class SideMenuBarComp extends Comp<CompStructure<VBox>> {
|
|||
|
||||
{
|
||||
var b = new IconButtonComp("mdi2g-github", () -> Hyperlinks.open(Hyperlinks.GITHUB))
|
||||
.shortcut(new KeyCodeCombination(KeyCode.values()[KeyCode.DIGIT1.ordinal() + entries.size() + 1]))
|
||||
.apply(new FancyTooltipAugment<>("visitGithubRepository"))
|
||||
.apply(simpleBorders)
|
||||
.accessibleTextKey("visitGithubRepository");
|
||||
|
@ -149,6 +156,7 @@ public class SideMenuBarComp extends Comp<CompStructure<VBox>> {
|
|||
|
||||
{
|
||||
var b = new IconButtonComp("mdi2d-discord", () -> Hyperlinks.open(Hyperlinks.DISCORD))
|
||||
.shortcut(new KeyCodeCombination(KeyCode.values()[KeyCode.DIGIT1.ordinal() + entries.size() + 2]))
|
||||
.apply(new FancyTooltipAugment<>("discord"))
|
||||
.apply(simpleBorders)
|
||||
.accessibleTextKey("discord");
|
||||
|
@ -160,6 +168,7 @@ public class SideMenuBarComp extends Comp<CompStructure<VBox>> {
|
|||
|
||||
{
|
||||
var b = new IconButtonComp("mdi2u-update", () -> UpdateAvailableAlert.showIfNeeded())
|
||||
.shortcut(new KeyCodeCombination(KeyCode.values()[KeyCode.DIGIT1.ordinal() + entries.size() + 3]))
|
||||
.apply(new FancyTooltipAugment<>("updateAvailableTooltip"))
|
||||
.accessibleTextKey("updateAvailableTooltip");
|
||||
b.apply(struc -> {
|
||||
|
|
|
@ -1,19 +1,20 @@
|
|||
package io.xpipe.app.comp.store;
|
||||
|
||||
import io.xpipe.app.fxcomps.SimpleComp;
|
||||
import io.xpipe.app.fxcomps.Comp;
|
||||
import io.xpipe.app.fxcomps.CompStructure;
|
||||
import io.xpipe.app.fxcomps.impl.IconButtonComp;
|
||||
import io.xpipe.app.fxcomps.impl.PrettyImageHelper;
|
||||
import javafx.geometry.Side;
|
||||
import javafx.scene.Node;
|
||||
import javafx.scene.control.Button;
|
||||
import javafx.scene.control.ContextMenu;
|
||||
import javafx.scene.control.Menu;
|
||||
import javafx.scene.control.MenuItem;
|
||||
import javafx.scene.layout.Region;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public class StoreQuickAccessButtonComp extends SimpleComp {
|
||||
public class StoreQuickAccessButtonComp extends Comp<CompStructure<Button>> {
|
||||
|
||||
private final StoreSection section;
|
||||
private final Consumer<StoreEntryWrapper> action;
|
||||
|
@ -23,17 +24,6 @@ public class StoreQuickAccessButtonComp extends SimpleComp {
|
|||
this.action = action;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Region createSimple() {
|
||||
var button = new IconButtonComp("mdi2c-chevron-double-right");
|
||||
button.apply(struc -> {
|
||||
struc.get().setOnAction(event -> {
|
||||
showMenu(struc.get());
|
||||
});
|
||||
});
|
||||
return button.createRegion();
|
||||
}
|
||||
|
||||
private void showMenu(Node anchor) {
|
||||
var cm = createMenu();
|
||||
if (cm == null) {
|
||||
|
@ -94,4 +84,15 @@ public class StoreQuickAccessButtonComp extends SimpleComp {
|
|||
});
|
||||
return m;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompStructure<Button> createBase() {
|
||||
var button = new IconButtonComp("mdi2c-chevron-double-right");
|
||||
button.apply(struc -> {
|
||||
struc.get().setOnAction(event -> {
|
||||
showMenu(struc.get());
|
||||
});
|
||||
});
|
||||
return button.createStructure();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,6 +13,10 @@ import io.xpipe.app.storage.DataStoreColor;
|
|||
import io.xpipe.app.util.ThreadHelper;
|
||||
import javafx.beans.binding.Bindings;
|
||||
import javafx.css.PseudoClass;
|
||||
import javafx.scene.control.Button;
|
||||
import javafx.scene.input.KeyCode;
|
||||
import javafx.scene.input.KeyCodeCombination;
|
||||
import javafx.scene.input.KeyEvent;
|
||||
import javafx.scene.layout.VBox;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
@ -35,17 +39,7 @@ public class StoreSectionComp extends Comp<CompStructure<VBox>> {
|
|||
this.topLevel = topLevel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompStructure<VBox> createBase() {
|
||||
var expanded = BindingsHelper.persist(Bindings.createBooleanBinding(
|
||||
() -> {
|
||||
return section.getWrapper().getExpanded().get()
|
||||
&& section.getShownChildren().size() > 0;
|
||||
},
|
||||
section.getWrapper().getExpanded(),
|
||||
section.getShownChildren()));
|
||||
var root = StoreEntryComp.customSection(section, topLevel);
|
||||
|
||||
private Comp<CompStructure<Button>> createQuickAccessButton() {
|
||||
var quickAccessDisabled = BindingsHelper.persist(Bindings.createBooleanBinding(
|
||||
() -> {
|
||||
return section.getShownChildren().isEmpty();
|
||||
|
@ -62,7 +56,14 @@ public class StoreSectionComp extends Comp<CompStructure<VBox>> {
|
|||
.apply(struc -> struc.get().setMinWidth(30))
|
||||
.apply(struc -> struc.get().setPrefWidth(30))
|
||||
.maxHeight(100)
|
||||
.disable(quickAccessDisabled);
|
||||
.disable(quickAccessDisabled)
|
||||
.focusTraversableForAccessibility()
|
||||
.dislayOnlyShortcut(new KeyCodeCombination(KeyCode.RIGHT))
|
||||
.tooltipKey("accessSubConnections");
|
||||
return quickAccessButton;
|
||||
}
|
||||
|
||||
private Comp<CompStructure<Button>> createExpandButton() {
|
||||
var expandButton = new IconButtonComp(
|
||||
Bindings.createStringBinding(
|
||||
() -> section.getWrapper().getExpanded().get()
|
||||
|
@ -73,10 +74,13 @@ public class StoreSectionComp extends Comp<CompStructure<VBox>> {
|
|||
section.getShownChildren()),
|
||||
() -> {
|
||||
section.getWrapper().toggleExpanded();
|
||||
})
|
||||
});
|
||||
expandButton
|
||||
.apply(struc -> struc.get().setMinWidth(30))
|
||||
.apply(struc -> struc.get().setPrefWidth(30))
|
||||
.focusTraversable()
|
||||
.focusTraversableForAccessibility()
|
||||
.dislayOnlyShortcut(new KeyCodeCombination(KeyCode.SPACE))
|
||||
.tooltipKey("expand")
|
||||
.accessibleText(Bindings.createStringBinding(
|
||||
() -> {
|
||||
return "Expand " + section.getWrapper().getName().getValue();
|
||||
|
@ -87,13 +91,38 @@ public class StoreSectionComp extends Comp<CompStructure<VBox>> {
|
|||
.styleClass("expand-button")
|
||||
.maxHeight(100)
|
||||
.vgrow();
|
||||
return expandButton;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompStructure<VBox> createBase() {
|
||||
var entryButton = StoreEntryComp.customSection(section, topLevel);
|
||||
var quickAccessButton = createQuickAccessButton();
|
||||
var expandButton = createExpandButton();
|
||||
var buttonList = new ArrayList<Comp<?>>();
|
||||
if (root.isFullSize()) {
|
||||
if (entryButton.isFullSize()) {
|
||||
buttonList.add(quickAccessButton);
|
||||
}
|
||||
buttonList.add(expandButton);
|
||||
var buttons = new VerticalComp(buttonList);
|
||||
List<Comp<?>> topEntryList = List.of(buttons, root.hgrow());
|
||||
var topEntryList = new HorizontalComp(List.of(buttons, entryButton.hgrow()));
|
||||
topEntryList.apply(struc -> {
|
||||
var mainButton = struc.get().getChildren().get(1);
|
||||
mainButton.addEventFilter(KeyEvent.KEY_PRESSED, event -> {
|
||||
if (event.getCode() == KeyCode.SPACE) {
|
||||
section.getWrapper().toggleExpanded();
|
||||
event.consume();
|
||||
}
|
||||
if (event.getCode() == KeyCode.RIGHT) {
|
||||
var ref = (VBox) struc.get().getChildren().getFirst();
|
||||
if (entryButton.isFullSize()) {
|
||||
var btn = (Button) ref.getChildren().getFirst();
|
||||
btn.fire();
|
||||
}
|
||||
event.consume();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Optimization for large sections. If there are more than 20 children, only add the nodes to the scene if the
|
||||
// section is actually expanded
|
||||
|
@ -109,18 +138,23 @@ public class StoreSectionComp extends Comp<CompStructure<VBox>> {
|
|||
.minHeight(0)
|
||||
.hgrow();
|
||||
|
||||
return new VerticalComp(List.of(
|
||||
new HorizontalComp(topEntryList)
|
||||
.apply(struc -> struc.get().setFillHeight(true)),
|
||||
var expanded = BindingsHelper.persist(Bindings.createBooleanBinding(
|
||||
() -> {
|
||||
return section.getWrapper().getExpanded().get()
|
||||
&& section.getShownChildren().size() > 0;
|
||||
},
|
||||
section.getWrapper().getExpanded(),
|
||||
section.getShownChildren()));
|
||||
var full = new VerticalComp(List.of(
|
||||
topEntryList,
|
||||
Comp.separator().hide(BindingsHelper.persist(expanded.not())),
|
||||
new HorizontalComp(List.of(content))
|
||||
.styleClass("content")
|
||||
.apply(struc -> struc.get().setFillHeight(true))
|
||||
.hide(BindingsHelper.persist(Bindings.or(
|
||||
Bindings.not(section.getWrapper().getExpanded()),
|
||||
Bindings.size(section.getShownChildren())
|
||||
.isEqualTo(0))))))
|
||||
.styleClass("store-entry-section-comp")
|
||||
Bindings.size(section.getShownChildren()).isEqualTo(0))))));
|
||||
return full.styleClass("store-entry-section-comp")
|
||||
.apply(struc -> {
|
||||
struc.get().setFillWidth(true);
|
||||
SimpleChangeListener.apply(expanded, val -> {
|
||||
|
@ -128,15 +162,18 @@ public class StoreSectionComp extends Comp<CompStructure<VBox>> {
|
|||
});
|
||||
struc.get().pseudoClassStateChanged(EVEN, section.getDepth() % 2 == 0);
|
||||
struc.get().pseudoClassStateChanged(ODD, section.getDepth() % 2 != 0);
|
||||
})
|
||||
.apply(struc -> SimpleChangeListener.apply(section.getWrapper().getColor(), val -> {
|
||||
struc.get().pseudoClassStateChanged(ROOT, topLevel);
|
||||
struc.get().pseudoClassStateChanged(SUB, !topLevel);
|
||||
|
||||
SimpleChangeListener.apply(section.getWrapper().getColor(), val -> {
|
||||
if (!topLevel) {
|
||||
return;
|
||||
}
|
||||
|
||||
var newList = new ArrayList<>(struc.get().getStyleClass());
|
||||
newList.removeIf(s -> Arrays.stream(DataStoreColor.values())
|
||||
.anyMatch(dataStoreColor -> dataStoreColor.getId().equals(s)));
|
||||
.anyMatch(
|
||||
dataStoreColor -> dataStoreColor.getId().equals(s)));
|
||||
newList.remove("none");
|
||||
newList.add("color-box");
|
||||
if (val != null) {
|
||||
|
@ -145,10 +182,7 @@ public class StoreSectionComp extends Comp<CompStructure<VBox>> {
|
|||
newList.add("none");
|
||||
}
|
||||
struc.get().getStyleClass().setAll(newList);
|
||||
}))
|
||||
.apply(struc -> {
|
||||
struc.get().pseudoClassStateChanged(ROOT, topLevel);
|
||||
struc.get().pseudoClassStateChanged(SUB, !topLevel);
|
||||
});
|
||||
})
|
||||
.createStructure();
|
||||
}
|
||||
|
|
|
@ -4,10 +4,12 @@ import io.xpipe.app.comp.base.LoadingOverlayComp;
|
|||
import io.xpipe.app.fxcomps.Comp;
|
||||
import io.xpipe.app.issue.TrackEvent;
|
||||
import io.xpipe.app.prefs.AppPrefs;
|
||||
import io.xpipe.app.util.InputHelper;
|
||||
import io.xpipe.app.util.ThreadHelper;
|
||||
import io.xpipe.core.process.OsType;
|
||||
import javafx.application.Platform;
|
||||
import javafx.beans.value.ObservableValue;
|
||||
import javafx.css.PseudoClass;
|
||||
import javafx.geometry.Insets;
|
||||
import javafx.geometry.Rectangle2D;
|
||||
import javafx.scene.Node;
|
||||
|
@ -216,7 +218,7 @@ public class AppWindowHelper {
|
|||
public static void setupStylesheets(Scene scene) {
|
||||
AppStyle.addStylesheets(scene);
|
||||
|
||||
scene.addEventHandler(KeyEvent.KEY_PRESSED, event -> {
|
||||
scene.addEventFilter(KeyEvent.KEY_PRESSED, event -> {
|
||||
if (AppProperties.get().isDeveloperMode() && event.getCode().equals(KeyCode.F3)) {
|
||||
AppStyle.reloadStylesheets(scene);
|
||||
TrackEvent.debug("Reloaded stylesheets");
|
||||
|
@ -224,6 +226,24 @@ public class AppWindowHelper {
|
|||
}
|
||||
});
|
||||
TrackEvent.debug("Set stylesheet reload listener");
|
||||
|
||||
InputHelper.onNavigationInput(scene, (kb) -> {
|
||||
var r = scene.getRoot();
|
||||
if (r != null) {
|
||||
r.pseudoClassStateChanged(PseudoClass.getPseudoClass("key-navigation"), kb);
|
||||
r.pseudoClassStateChanged(PseudoClass.getPseudoClass("normal-navigation"), !kb);
|
||||
r.pseudoClassStateChanged(PseudoClass.getPseudoClass("accessibility-navigation"), false);
|
||||
}
|
||||
});
|
||||
|
||||
Platform.accessibilityActiveProperty().addListener((observable, oldValue, newValue) -> {
|
||||
var r = scene.getRoot();
|
||||
if (r != null) {
|
||||
r.pseudoClassStateChanged(PseudoClass.getPseudoClass("key-navigation"), false);
|
||||
r.pseudoClassStateChanged(PseudoClass.getPseudoClass("normal-navigation"), false);
|
||||
r.pseudoClassStateChanged(PseudoClass.getPseudoClass("accessibility-navigation"), true);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static void setupContent(
|
||||
|
|
|
@ -24,6 +24,7 @@ public class AppUserDirectoryCheck {
|
|||
+ ". Please make sure that you have the appropriate permissions and no Antivirus program is blocking the access. "
|
||||
+ "In case you use cloud storage, verify that your cloud storage is working and you are logged in."))
|
||||
.term()
|
||||
.expected()
|
||||
.handle();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,14 +4,15 @@ import atlantafx.base.controls.Spacer;
|
|||
import io.xpipe.app.core.AppI18n;
|
||||
import io.xpipe.app.fxcomps.augment.Augment;
|
||||
import io.xpipe.app.fxcomps.augment.GrowAugment;
|
||||
import io.xpipe.app.fxcomps.impl.FancyTooltipAugment;
|
||||
import io.xpipe.app.fxcomps.util.Shortcuts;
|
||||
import io.xpipe.app.fxcomps.util.SimpleChangeListener;
|
||||
import javafx.application.Platform;
|
||||
import javafx.beans.value.ObservableValue;
|
||||
import javafx.geometry.Insets;
|
||||
import javafx.geometry.Orientation;
|
||||
import javafx.scene.control.ButtonBase;
|
||||
import javafx.scene.control.Separator;
|
||||
import javafx.scene.control.Tooltip;
|
||||
import javafx.scene.input.KeyCombination;
|
||||
import javafx.scene.layout.HBox;
|
||||
import javafx.scene.layout.Priority;
|
||||
|
@ -20,6 +21,7 @@ import javafx.scene.layout.VBox;
|
|||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
|
@ -124,6 +126,10 @@ public abstract class Comp<S extends CompStructure<?>> {
|
|||
return apply(struc -> struc.get().setFocusTraversable(b));
|
||||
}
|
||||
|
||||
public Comp<S> focusTraversableForAccessibility() {
|
||||
return apply(struc -> struc.get().focusTraversableProperty().bind(Platform.accessibilityActiveProperty()));
|
||||
}
|
||||
|
||||
public Comp<S> visible(ObservableValue<Boolean> o) {
|
||||
return apply(struc -> struc.get().visibleProperty().bind(o));
|
||||
}
|
||||
|
@ -179,8 +185,22 @@ public abstract class Comp<S extends CompStructure<?>> {
|
|||
return apply(struc -> Shortcuts.addShortcut((ButtonBase) struc.get(), shortcut));
|
||||
}
|
||||
|
||||
public Comp<S> tooltip(Supplier<String> text) {
|
||||
return apply(r -> Tooltip.install(r.get(), new Tooltip(text.get())));
|
||||
public Comp<S> dislayOnlyShortcut(KeyCombination shortcut) {
|
||||
return apply(struc -> Shortcuts.addDisplayShortcut(struc.get(), shortcut));
|
||||
}
|
||||
|
||||
public Comp<S> tooltip(ObservableValue<String> text) {
|
||||
return apply(new FancyTooltipAugment<>(text));
|
||||
}
|
||||
|
||||
public Comp<S> tooltipKey(String key) {
|
||||
return apply(new FancyTooltipAugment<>(key));
|
||||
}
|
||||
|
||||
public <T1 extends CompStructure<?>, T2 extends CompStructure<?>>void applyMultiple(Comp<T1> c1, Comp<T2> c2, BiConsumer<T1,T2> consumer) {
|
||||
c1.apply(struc -> {
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
public Region createRegion() {
|
||||
|
|
|
@ -25,8 +25,8 @@ public class FancyTooltipAugment<S extends CompStructure<?>> implements Augment<
|
|||
var region = struc.get();
|
||||
var tt = new Tooltip();
|
||||
var toDisplay = text.getValue();
|
||||
if (Shortcuts.getShortcut(region) != null) {
|
||||
toDisplay = toDisplay + " (" + Shortcuts.getShortcut(region).getDisplayText() + ")";
|
||||
if (Shortcuts.getDisplayShortcut(region) != null) {
|
||||
toDisplay = toDisplay + "\n\n(Shortcut: " + Shortcuts.getDisplayShortcut(region).getDisplayText() + ")";
|
||||
}
|
||||
tt.textProperty().setValue(toDisplay);
|
||||
tt.setStyle("-fx-font-size: 11pt;");
|
||||
|
|
|
@ -15,7 +15,11 @@ import java.util.function.Consumer;
|
|||
|
||||
public class Shortcuts {
|
||||
|
||||
private static final Map<Region, KeyCombination> SHORTCUTS = new HashMap<>();
|
||||
private static final Map<Region, KeyCombination> DISPLAY_SHORTCUTS = new HashMap<>();
|
||||
|
||||
public static void addDisplayShortcut(Region region, KeyCombination comb) {
|
||||
DISPLAY_SHORTCUTS.put(region, comb);
|
||||
}
|
||||
|
||||
public static <T extends ButtonBase> void addShortcut(T region, KeyCombination comb) {
|
||||
addShortcut(region, comb, ButtonBase::fire);
|
||||
|
@ -35,8 +39,8 @@ public class Shortcuts {
|
|||
}
|
||||
};
|
||||
|
||||
DISPLAY_SHORTCUTS.put(region, comb);
|
||||
AtomicReference<Scene> scene = new AtomicReference<>();
|
||||
SHORTCUTS.put(region, comb);
|
||||
SimpleChangeListener.apply(region.sceneProperty(), s -> {
|
||||
if (Objects.equals(s, scene.get())) {
|
||||
return;
|
||||
|
@ -44,18 +48,19 @@ public class Shortcuts {
|
|||
|
||||
if (scene.get() != null) {
|
||||
scene.get().removeEventFilter(KeyEvent.KEY_PRESSED, filter);
|
||||
SHORTCUTS.remove(region);
|
||||
DISPLAY_SHORTCUTS.remove(region);
|
||||
scene.set(null);
|
||||
}
|
||||
|
||||
if (s != null) {
|
||||
scene.set(s);
|
||||
DISPLAY_SHORTCUTS.put(region, comb);
|
||||
s.addEventFilter(KeyEvent.KEY_PRESSED, filter);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static KeyCombination getShortcut(Region region) {
|
||||
return SHORTCUTS.get(region);
|
||||
public static KeyCombination getDisplayShortcut(Region region) {
|
||||
return DISPLAY_SHORTCUTS.get(region);
|
||||
}
|
||||
}
|
||||
|
|
50
app/src/main/java/io/xpipe/app/util/InputHelper.java
Normal file
50
app/src/main/java/io/xpipe/app/util/InputHelper.java
Normal file
|
@ -0,0 +1,50 @@
|
|||
package io.xpipe.app.util;
|
||||
|
||||
import javafx.event.EventHandler;
|
||||
import javafx.event.EventTarget;
|
||||
import javafx.scene.input.KeyCode;
|
||||
import javafx.scene.input.KeyEvent;
|
||||
import javafx.scene.input.MouseEvent;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public class InputHelper {
|
||||
|
||||
public static void onLeft(EventTarget target, boolean filter, Consumer<KeyEvent> r) {
|
||||
EventHandler<KeyEvent> keyEventEventHandler = event -> {
|
||||
if (event.getCode() == KeyCode.LEFT || event.getCode() == KeyCode.NUMPAD4) {
|
||||
r.accept(event);
|
||||
}
|
||||
};
|
||||
if (filter) {
|
||||
target.addEventFilter(KeyEvent.KEY_PRESSED, keyEventEventHandler);
|
||||
} else {
|
||||
target.addEventHandler(KeyEvent.KEY_PRESSED, keyEventEventHandler);
|
||||
}
|
||||
}
|
||||
|
||||
public static void onRight(EventTarget target, boolean filter, Consumer<KeyEvent> r) {
|
||||
EventHandler<KeyEvent> keyEventEventHandler = event -> {
|
||||
if (event.getCode() == KeyCode.RIGHT || event.getCode() == KeyCode.NUMPAD6) {
|
||||
r.accept(event);
|
||||
}
|
||||
};
|
||||
if (filter) {
|
||||
target.addEventFilter(KeyEvent.KEY_PRESSED, keyEventEventHandler);
|
||||
} else {
|
||||
target.addEventHandler(KeyEvent.KEY_PRESSED, keyEventEventHandler);
|
||||
}
|
||||
}
|
||||
|
||||
public static void onNavigationInput(EventTarget target, Consumer<Boolean> r) {
|
||||
target.addEventFilter(KeyEvent.KEY_PRESSED, event -> {
|
||||
var c = event.getCode();
|
||||
var list = List.of(KeyCode.LEFT, KeyCode.RIGHT, KeyCode.UP, KeyCode.DOWN, KeyCode.SPACE, KeyCode.TAB, KeyCode.NUMPAD2, KeyCode.NUMPAD4, KeyCode.NUMPAD6, KeyCode.NUMPAD8);
|
||||
r.accept(list.stream().anyMatch(keyCode -> keyCode == c));
|
||||
});
|
||||
target.addEventFilter(MouseEvent.MOUSE_PRESSED, event -> {
|
||||
r.accept(false);
|
||||
});
|
||||
}
|
||||
}
|
|
@ -111,6 +111,8 @@ newLine=Newline
|
|||
crlf=CRLF (Windows)
|
||||
lf=LF (Linux)
|
||||
none=None
|
||||
expand=Expand
|
||||
accessSubConnections=Access sub connections
|
||||
common=Common
|
||||
key=Key
|
||||
color=Color
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
-fx-opacity: 1.0;
|
||||
}
|
||||
|
||||
.category-button:hover, .category-button:focused {
|
||||
.category-button:hover, .root:key-navigation .category-button:focused {
|
||||
-fx-background-color: -color-bg-default;
|
||||
}
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
-fx-opacity: 1.0;
|
||||
}
|
||||
|
||||
.sidebar-comp .icon-button-comp:hover, .sidebar-comp .icon-button-comp:focused {
|
||||
.sidebar-comp .icon-button-comp:hover, .root:key-navigation .sidebar-comp .icon-button-comp:focused {
|
||||
-fx-background-color: -color-neutral-muted;
|
||||
}
|
||||
|
||||
|
|
|
@ -64,7 +64,7 @@
|
|||
-fx-background-color: derive(-color-neutral-muted, 25%);
|
||||
}
|
||||
|
||||
.store-entry-comp:hover, .store-entry-comp:focused {
|
||||
.store-entry-comp:hover, .root:key-navigation .store-entry-comp:focused {
|
||||
-fx-background-color: -color-neutral-muted;
|
||||
}
|
||||
|
||||
|
@ -76,7 +76,7 @@
|
|||
-fx-opacity: 1.0;
|
||||
}
|
||||
|
||||
.expand-button:hover, .expand-button:focused, .quick-access-button:hover, .quick-access-button:focused {
|
||||
.expand-button:hover, .root:key-navigation .expand-button:focused, .quick-access-button:hover, .root:key-navigation .quick-access-button:focused {
|
||||
-fx-background-color: -color-neutral-muted;
|
||||
}
|
||||
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
-fx-background-radius: 0;
|
||||
}
|
||||
|
||||
.store-section-mini-comp .item:hover, .store-section-mini-comp .item:focused {
|
||||
.store-section-mini-comp .item:hover, .root:key-navigation .store-section-mini-comp .item:focused {
|
||||
-fx-background-color: -color-accent-subtle;
|
||||
}
|
||||
|
||||
|
@ -37,7 +37,7 @@
|
|||
-fx-border-color: -color-border-subtle;
|
||||
}
|
||||
|
||||
.store-section-mini-comp .expand-button:hover, .store-section-mini-comp .expand-button:focused {
|
||||
.store-section-mini-comp .expand-button:hover, .root:key-navigation .store-section-mini-comp .expand-button:focused {
|
||||
-fx-background-color: -color-neutral-muted;
|
||||
}
|
||||
|
||||
|
@ -53,7 +53,7 @@
|
|||
-fx-background-radius: 4 0 0 4;
|
||||
}
|
||||
|
||||
.store-section-mini-comp .quick-access-button:hover, .store-section-mini-comp .quick-access-button:focused {
|
||||
.store-section-mini-comp .quick-access-button:hover, .root:key-navigation .store-section-mini-comp .quick-access-button:focused {
|
||||
-fx-background-color: -color-neutral-muted;
|
||||
}
|
||||
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
-fx-border-color: -color-neutral-emphasis;
|
||||
}
|
||||
|
||||
.edit-button.icon-button-comp:hover, .edit-button.icon-button-comp:focused {
|
||||
.edit-button.icon-button-comp:hover, .root:key-navigation .edit-button.icon-button-comp:focused {
|
||||
-fx-background-color: -color-accent-muted;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue