alt: root page tab
This commit is contained in:
@@ -23,7 +23,6 @@ import com.google.gson.stream.JsonWriter;
|
|||||||
import javafx.beans.binding.Bindings;
|
import javafx.beans.binding.Bindings;
|
||||||
import javafx.beans.binding.ObjectBinding;
|
import javafx.beans.binding.ObjectBinding;
|
||||||
import javafx.scene.paint.Color;
|
import javafx.scene.paint.Color;
|
||||||
|
|
||||||
import org.jackhuang.hmcl.util.Logging;
|
import org.jackhuang.hmcl.util.Logging;
|
||||||
import org.jackhuang.hmcl.util.ResourceNotFoundError;
|
import org.jackhuang.hmcl.util.ResourceNotFoundError;
|
||||||
import org.jackhuang.hmcl.util.io.FileUtils;
|
import org.jackhuang.hmcl.util.io.FileUtils;
|
||||||
@@ -50,12 +49,14 @@ public class Theme {
|
|||||||
Color.web("#B71C1C") // red
|
Color.web("#B71C1C") // red
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private final Color paint;
|
||||||
private final String color;
|
private final String color;
|
||||||
private final String name;
|
private final String name;
|
||||||
|
|
||||||
Theme(String name, String color) {
|
Theme(String name, String color) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.color = color;
|
this.color = color;
|
||||||
|
this.paint = Color.web(color);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getName() {
|
public String getName() {
|
||||||
@@ -84,6 +85,7 @@ public class Theme {
|
|||||||
File temp = File.createTempFile("hmcl", ".css");
|
File temp = File.createTempFile("hmcl", ".css");
|
||||||
FileUtils.writeText(temp, IOUtils.readFullyAsString(ResourceNotFoundError.getResourceAsStream("/assets/css/custom.css"))
|
FileUtils.writeText(temp, IOUtils.readFullyAsString(ResourceNotFoundError.getResourceAsStream("/assets/css/custom.css"))
|
||||||
.replace("%base-color%", color)
|
.replace("%base-color%", color)
|
||||||
|
.replace("%base-rippler-color%", String.format("rgba(%d, %d, %d, 0.3)", (int)Math.ceil(paint.getRed() * 256), (int)Math.ceil(paint.getGreen() * 256), (int)Math.ceil(paint.getBlue() * 256)))
|
||||||
.replace("%font-color%", getColorDisplayName(getForegroundColor())));
|
.replace("%font-color%", getColorDisplayName(getForegroundColor())));
|
||||||
css = temp.toURI().toString();
|
css = temp.toURI().toString();
|
||||||
} catch (IOException | NullPointerException e) {
|
} catch (IOException | NullPointerException e) {
|
||||||
|
|||||||
@@ -100,7 +100,7 @@ public class AccountListItemSkin extends SkinBase<AccountListItem> {
|
|||||||
right.getChildren().add(btnRemove);
|
right.getChildren().add(btnRemove);
|
||||||
root.setRight(right);
|
root.setRight(right);
|
||||||
|
|
||||||
root.setStyle("-fx-background-color: white; -fx-padding: 8 8 8 0;");
|
root.setStyle("-fx-background-color: white; -fx-background-radius: 4; -fx-padding: 8 8 8 0;");
|
||||||
JFXDepthManager.setDepth(root, 1);
|
JFXDepthManager.setDepth(root, 1);
|
||||||
|
|
||||||
getChildren().setAll(root);
|
getChildren().setAll(root);
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import javafx.collections.ObservableList;
|
|||||||
import org.jackhuang.hmcl.auth.authlibinjector.AuthlibInjectorServer;
|
import org.jackhuang.hmcl.auth.authlibinjector.AuthlibInjectorServer;
|
||||||
import org.jackhuang.hmcl.ui.Controllers;
|
import org.jackhuang.hmcl.ui.Controllers;
|
||||||
import org.jackhuang.hmcl.ui.ListPage;
|
import org.jackhuang.hmcl.ui.ListPage;
|
||||||
|
import org.jackhuang.hmcl.ui.construct.Navigator;
|
||||||
import org.jackhuang.hmcl.ui.decorator.DecoratorPage;
|
import org.jackhuang.hmcl.ui.decorator.DecoratorPage;
|
||||||
import org.jackhuang.hmcl.util.javafx.MappedObservableList;
|
import org.jackhuang.hmcl.util.javafx.MappedObservableList;
|
||||||
|
|
||||||
@@ -37,6 +38,7 @@ public class AuthlibInjectorServersPage extends ListPage<AuthlibInjectorServerIt
|
|||||||
public AuthlibInjectorServersPage() {
|
public AuthlibInjectorServersPage() {
|
||||||
serverItems = MappedObservableList.create(config().getAuthlibInjectorServers(), this::createServerItem);
|
serverItems = MappedObservableList.create(config().getAuthlibInjectorServers(), this::createServerItem);
|
||||||
Bindings.bindContent(itemsProperty(), serverItems);
|
Bindings.bindContent(itemsProperty(), serverItems);
|
||||||
|
addEventHandler(Navigator.NavigationEvent.NAVIGATED, this::onDecoratorPageNavigating);
|
||||||
}
|
}
|
||||||
|
|
||||||
private AuthlibInjectorServerItem createServerItem(AuthlibInjectorServer server) {
|
private AuthlibInjectorServerItem createServerItem(AuthlibInjectorServer server) {
|
||||||
|
|||||||
@@ -30,10 +30,12 @@ public class AdvancedListItem extends Control {
|
|||||||
private final ObjectProperty<Image> image = new SimpleObjectProperty<>(this, "image");
|
private final ObjectProperty<Image> image = new SimpleObjectProperty<>(this, "image");
|
||||||
private final ObjectProperty<Node> rightGraphic = new SimpleObjectProperty<>(this, "rightGraphic");
|
private final ObjectProperty<Node> rightGraphic = new SimpleObjectProperty<>(this, "rightGraphic");
|
||||||
private final StringProperty title = new SimpleStringProperty(this, "title");
|
private final StringProperty title = new SimpleStringProperty(this, "title");
|
||||||
|
private final BooleanProperty active = new SimpleBooleanProperty(this, "active");
|
||||||
private final StringProperty subtitle = new SimpleStringProperty(this, "subtitle");
|
private final StringProperty subtitle = new SimpleStringProperty(this, "subtitle");
|
||||||
private final BooleanProperty actionButtonVisible = new SimpleBooleanProperty(this, "actionButtonVisible", true);
|
private final BooleanProperty actionButtonVisible = new SimpleBooleanProperty(this, "actionButtonVisible", true);
|
||||||
|
|
||||||
public AdvancedListItem() {
|
public AdvancedListItem() {
|
||||||
|
getStyleClass().add("advanced-list-item");
|
||||||
addEventHandler(MouseEvent.MOUSE_CLICKED, e -> fireEvent(new ActionEvent()));
|
addEventHandler(MouseEvent.MOUSE_CLICKED, e -> fireEvent(new ActionEvent()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -73,6 +75,18 @@ public class AdvancedListItem extends Control {
|
|||||||
this.title.set(title);
|
this.title.set(title);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isActive() {
|
||||||
|
return active.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
public BooleanProperty activeProperty() {
|
||||||
|
return active;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setActive(boolean active) {
|
||||||
|
this.active.set(active);
|
||||||
|
}
|
||||||
|
|
||||||
public String getSubtitle() {
|
public String getSubtitle() {
|
||||||
return subtitle.get();
|
return subtitle.get();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
*/
|
*/
|
||||||
package org.jackhuang.hmcl.ui.construct;
|
package org.jackhuang.hmcl.ui.construct;
|
||||||
|
|
||||||
|
import javafx.css.PseudoClass;
|
||||||
import javafx.geometry.Insets;
|
import javafx.geometry.Insets;
|
||||||
import javafx.geometry.Pos;
|
import javafx.geometry.Pos;
|
||||||
import javafx.scene.control.Label;
|
import javafx.scene.control.Label;
|
||||||
@@ -30,13 +31,19 @@ import javafx.scene.text.TextAlignment;
|
|||||||
import org.jackhuang.hmcl.ui.FXUtils;
|
import org.jackhuang.hmcl.ui.FXUtils;
|
||||||
|
|
||||||
public class AdvancedListItemSkin extends SkinBase<AdvancedListItem> {
|
public class AdvancedListItemSkin extends SkinBase<AdvancedListItem> {
|
||||||
|
private final PseudoClass SELECTED = PseudoClass.getPseudoClass("selected");
|
||||||
|
|
||||||
public AdvancedListItemSkin(AdvancedListItem skinnable) {
|
public AdvancedListItemSkin(AdvancedListItem skinnable) {
|
||||||
super(skinnable);
|
super(skinnable);
|
||||||
|
|
||||||
StackPane stackPane = new StackPane();
|
StackPane stackPane = new StackPane();
|
||||||
|
stackPane.getStyleClass().add("container");
|
||||||
RipplerContainer container = new RipplerContainer(stackPane);
|
RipplerContainer container = new RipplerContainer(stackPane);
|
||||||
|
|
||||||
|
FXUtils.onChangeAndOperate(skinnable.activeProperty(), active -> {
|
||||||
|
skinnable.pseudoClassStateChanged(SELECTED, active);
|
||||||
|
});
|
||||||
|
|
||||||
BorderPane root = new BorderPane();
|
BorderPane root = new BorderPane();
|
||||||
root.setPickOnBounds(false);
|
root.setPickOnBounds(false);
|
||||||
|
|
||||||
|
|||||||
@@ -117,9 +117,11 @@ public class ComponentList extends Control {
|
|||||||
|
|
||||||
protected static class Skin extends SkinBase<ComponentList> {
|
protected static class Skin extends SkinBase<ComponentList> {
|
||||||
private static final PseudoClass PSEUDO_CLASS_FIRST = PseudoClass.getPseudoClass("first");
|
private static final PseudoClass PSEUDO_CLASS_FIRST = PseudoClass.getPseudoClass("first");
|
||||||
|
private static final PseudoClass PSEUDO_CLASS_LAST = PseudoClass.getPseudoClass("last");
|
||||||
|
|
||||||
private final ObservableList<Node> list;
|
private final ObservableList<Node> list;
|
||||||
private final ObjectBinding<Node> firstItem;
|
private final ObjectBinding<Node> firstItem;
|
||||||
|
private final ObjectBinding<Node> lastItem;
|
||||||
|
|
||||||
protected Skin(ComponentList control) {
|
protected Skin(ComponentList control) {
|
||||||
super(control);
|
super(control);
|
||||||
@@ -140,6 +142,16 @@ public class ComponentList extends Control {
|
|||||||
if (!list.isEmpty())
|
if (!list.isEmpty())
|
||||||
list.get(0).pseudoClassStateChanged(PSEUDO_CLASS_FIRST, true);
|
list.get(0).pseudoClassStateChanged(PSEUDO_CLASS_FIRST, true);
|
||||||
|
|
||||||
|
lastItem = Bindings.valueAt(list, Bindings.subtract(Bindings.size(list), 1));
|
||||||
|
lastItem.addListener((observable, oldValue, newValue) -> {
|
||||||
|
if (newValue != null)
|
||||||
|
newValue.pseudoClassStateChanged(PSEUDO_CLASS_LAST, true);
|
||||||
|
if (oldValue != null)
|
||||||
|
oldValue.pseudoClassStateChanged(PSEUDO_CLASS_LAST, false);
|
||||||
|
});
|
||||||
|
if (!list.isEmpty())
|
||||||
|
list.get(list.size() - 1).pseudoClassStateChanged(PSEUDO_CLASS_LAST, true);
|
||||||
|
|
||||||
VBox vbox = new VBox();
|
VBox vbox = new VBox();
|
||||||
Bindings.bindContent(vbox.getChildren(), list);
|
Bindings.bindContent(vbox.getChildren(), list);
|
||||||
getChildren().setAll(vbox);
|
getChildren().setAll(vbox);
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/**
|
/*
|
||||||
* Hello Minecraft! Launcher
|
* Hello Minecraft! Launcher
|
||||||
* Copyright (C) 2020 huangyuhui <huanghongxun2008@126.com> and contributors
|
* Copyright (C) 2020 huangyuhui <huanghongxun2008@126.com> and contributors
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -17,7 +17,9 @@
|
|||||||
*/
|
*/
|
||||||
package org.jackhuang.hmcl.ui.construct;
|
package org.jackhuang.hmcl.ui.construct;
|
||||||
|
|
||||||
|
import javafx.beans.property.BooleanProperty;
|
||||||
import javafx.beans.property.ObjectProperty;
|
import javafx.beans.property.ObjectProperty;
|
||||||
|
import javafx.beans.property.SimpleBooleanProperty;
|
||||||
import javafx.beans.property.SimpleObjectProperty;
|
import javafx.beans.property.SimpleObjectProperty;
|
||||||
import javafx.event.Event;
|
import javafx.event.Event;
|
||||||
import javafx.event.EventHandler;
|
import javafx.event.EventHandler;
|
||||||
@@ -37,11 +39,13 @@ import java.util.logging.Level;
|
|||||||
public class Navigator extends TransitionPane {
|
public class Navigator extends TransitionPane {
|
||||||
private static final String PROPERTY_DIALOG_CLOSE_HANDLER = Navigator.class.getName() + ".closeListener";
|
private static final String PROPERTY_DIALOG_CLOSE_HANDLER = Navigator.class.getName() + ".closeListener";
|
||||||
|
|
||||||
|
private final BooleanProperty backable = new SimpleBooleanProperty(this, "backable");
|
||||||
private final Stack<Node> stack = new Stack<>();
|
private final Stack<Node> stack = new Stack<>();
|
||||||
private boolean initialized = false;
|
private boolean initialized = false;
|
||||||
|
|
||||||
public void init(Node init) {
|
public void init(Node init) {
|
||||||
stack.push(init);
|
stack.push(init);
|
||||||
|
backable.set(canGoBack());
|
||||||
getChildren().setAll(init);
|
getChildren().setAll(init);
|
||||||
|
|
||||||
fireEvent(new NavigationEvent(this, init, NavigationEvent.NAVIGATED));
|
fireEvent(new NavigationEvent(this, init, NavigationEvent.NAVIGATED));
|
||||||
@@ -62,6 +66,7 @@ public class Navigator extends TransitionPane {
|
|||||||
Logging.LOG.info("Navigate to " + node);
|
Logging.LOG.info("Navigate to " + node);
|
||||||
|
|
||||||
stack.push(node);
|
stack.push(node);
|
||||||
|
backable.set(canGoBack());
|
||||||
|
|
||||||
NavigationEvent navigating = new NavigationEvent(this, from, NavigationEvent.NAVIGATING);
|
NavigationEvent navigating = new NavigationEvent(this, from, NavigationEvent.NAVIGATING);
|
||||||
fireEvent(navigating);
|
fireEvent(navigating);
|
||||||
@@ -103,6 +108,7 @@ public class Navigator extends TransitionPane {
|
|||||||
Logging.LOG.info("Closed page " + from);
|
Logging.LOG.info("Closed page " + from);
|
||||||
|
|
||||||
stack.pop();
|
stack.pop();
|
||||||
|
backable.set(canGoBack());
|
||||||
Node node = stack.peek();
|
Node node = stack.peek();
|
||||||
|
|
||||||
NavigationEvent navigating = new NavigationEvent(this, from, NavigationEvent.NAVIGATING);
|
NavigationEvent navigating = new NavigationEvent(this, from, NavigationEvent.NAVIGATING);
|
||||||
@@ -131,6 +137,18 @@ public class Navigator extends TransitionPane {
|
|||||||
return stack.size() > 1;
|
return stack.size() > 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isBackable() {
|
||||||
|
return backable.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
public BooleanProperty backableProperty() {
|
||||||
|
return backable;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setBackable(boolean backable) {
|
||||||
|
this.backable.set(backable);
|
||||||
|
}
|
||||||
|
|
||||||
public int size() {
|
public int size() {
|
||||||
return stack.size();
|
return stack.size();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,249 @@
|
|||||||
|
/*
|
||||||
|
* Hello Minecraft! Launcher
|
||||||
|
* Copyright (C) 2020 huangyuhui <huanghongxun2008@126.com> and contributors
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package org.jackhuang.hmcl.ui.construct;
|
||||||
|
|
||||||
|
import javafx.beans.property.*;
|
||||||
|
import javafx.collections.ListChangeListener;
|
||||||
|
import javafx.collections.ObservableList;
|
||||||
|
import javafx.scene.AccessibleAttribute;
|
||||||
|
import javafx.scene.Node;
|
||||||
|
import javafx.scene.control.SingleSelectionModel;
|
||||||
|
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
public interface TabControl {
|
||||||
|
ObservableList<Tab> getTabs();
|
||||||
|
|
||||||
|
class TabControlSelectionModel extends SingleSelectionModel<Tab> {
|
||||||
|
private final TabControl tabHeader;
|
||||||
|
|
||||||
|
public TabControlSelectionModel(final TabControl t) {
|
||||||
|
if (t == null) {
|
||||||
|
throw new NullPointerException("TabPane can not be null");
|
||||||
|
}
|
||||||
|
this.tabHeader = t;
|
||||||
|
|
||||||
|
// watching for changes to the items list content
|
||||||
|
final ListChangeListener<Tab> itemsContentObserver = c -> {
|
||||||
|
while (c.next()) {
|
||||||
|
for (Tab tab : c.getRemoved()) {
|
||||||
|
if (tab != null && !tabHeader.getTabs().contains(tab)) {
|
||||||
|
if (tab.isSelected()) {
|
||||||
|
tab.setSelected(false);
|
||||||
|
final int tabIndex = c.getFrom();
|
||||||
|
|
||||||
|
// we always try to select the nearest, non-disabled
|
||||||
|
// tab from the position of the closed tab.
|
||||||
|
findNearestAvailableTab(tabIndex, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (c.wasAdded() || c.wasRemoved()) {
|
||||||
|
// The selected tab index can be out of sync with the list of tab if
|
||||||
|
// we add or remove tabs before the selected tab.
|
||||||
|
if (getSelectedIndex() != tabHeader.getTabs().indexOf(getSelectedItem())) {
|
||||||
|
clearAndSelect(tabHeader.getTabs().indexOf(getSelectedItem()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (getSelectedIndex() == -1 && getSelectedItem() == null && tabHeader.getTabs().size() > 0) {
|
||||||
|
// we go looking for the first non-disabled tab, as opposed to
|
||||||
|
// just selecting the first tab (fix for RT-36908)
|
||||||
|
findNearestAvailableTab(0, true);
|
||||||
|
} else if (tabHeader.getTabs().isEmpty()) {
|
||||||
|
clearSelection();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if (this.tabHeader.getTabs() != null) {
|
||||||
|
this.tabHeader.getTabs().addListener(itemsContentObserver);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// API Implementation
|
||||||
|
@Override public void select(int index) {
|
||||||
|
if (index < 0 || (getItemCount() > 0 && index >= getItemCount()) ||
|
||||||
|
(index == getSelectedIndex() && getModelItem(index).isSelected())) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unselect the old tab
|
||||||
|
if (getSelectedIndex() >= 0 && getSelectedIndex() < tabHeader.getTabs().size()) {
|
||||||
|
tabHeader.getTabs().get(getSelectedIndex()).setSelected(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
setSelectedIndex(index);
|
||||||
|
|
||||||
|
Tab tab = getModelItem(index);
|
||||||
|
if (tab != null) {
|
||||||
|
setSelectedItem(tab);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Select the new tab
|
||||||
|
if (getSelectedIndex() >= 0 && getSelectedIndex() < tabHeader.getTabs().size()) {
|
||||||
|
tabHeader.getTabs().get(getSelectedIndex()).setSelected(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Does this get all the change events */
|
||||||
|
((Node) tabHeader).notifyAccessibleAttributeChanged(AccessibleAttribute.FOCUS_ITEM);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override public void select(Tab tab) {
|
||||||
|
final int itemCount = getItemCount();
|
||||||
|
|
||||||
|
for (int i = 0; i < itemCount; i++) {
|
||||||
|
final Tab value = getModelItem(i);
|
||||||
|
if (value != null && value.equals(tab)) {
|
||||||
|
select(i);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (tab != null) {
|
||||||
|
setSelectedItem(tab);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override protected Tab getModelItem(int index) {
|
||||||
|
final ObservableList<Tab> items = tabHeader.getTabs();
|
||||||
|
if (items == null) return null;
|
||||||
|
if (index < 0 || index >= items.size()) return null;
|
||||||
|
return items.get(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override protected int getItemCount() {
|
||||||
|
final ObservableList<Tab> items = tabHeader.getTabs();
|
||||||
|
return items == null ? 0 : items.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Tab findNearestAvailableTab(int tabIndex, boolean doSelect) {
|
||||||
|
// we always try to select the nearest, non-disabled
|
||||||
|
// tab from the position of the closed tab.
|
||||||
|
final int tabCount = getItemCount();
|
||||||
|
int i = 1;
|
||||||
|
Tab bestTab = null;
|
||||||
|
while (true) {
|
||||||
|
// look leftwards
|
||||||
|
int downPos = tabIndex - i;
|
||||||
|
if (downPos >= 0) {
|
||||||
|
Tab _tab = getModelItem(downPos);
|
||||||
|
if (_tab != null) {
|
||||||
|
bestTab = _tab;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// look rightwards. We subtract one as we need
|
||||||
|
// to take into account that a tab has been removed
|
||||||
|
// and if we don't do this we'll miss the tab
|
||||||
|
// to the right of the tab (as it has moved into
|
||||||
|
// the removed tabs position).
|
||||||
|
int upPos = tabIndex + i - 1;
|
||||||
|
if (upPos < tabCount) {
|
||||||
|
Tab _tab = getModelItem(upPos);
|
||||||
|
if (_tab != null) {
|
||||||
|
bestTab = _tab;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (downPos < 0 && upPos >= tabCount) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (doSelect && bestTab != null) {
|
||||||
|
select(bestTab);
|
||||||
|
}
|
||||||
|
|
||||||
|
return bestTab;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Tab {
|
||||||
|
private final StringProperty id = new SimpleStringProperty(this, "id");
|
||||||
|
private final StringProperty text = new SimpleStringProperty(this, "text");
|
||||||
|
private final ReadOnlyBooleanWrapper selected = new ReadOnlyBooleanWrapper(this, "selected");
|
||||||
|
private final ObjectProperty<Node> node = new SimpleObjectProperty<>(this, "node");
|
||||||
|
private Supplier<Node> nodeSupplier;
|
||||||
|
|
||||||
|
public Tab(String id) {
|
||||||
|
setId(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Tab(String id, String text) {
|
||||||
|
setId(id);
|
||||||
|
setText(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Supplier<Node> getNodeSupplier() {
|
||||||
|
return nodeSupplier;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setNodeSupplier(Supplier<Node> nodeSupplier) {
|
||||||
|
this.nodeSupplier = nodeSupplier;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getId() {
|
||||||
|
return id.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
public StringProperty idProperty() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(String id) {
|
||||||
|
this.id.set(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getText() {
|
||||||
|
return text.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
public StringProperty textProperty() {
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setText(String text) {
|
||||||
|
this.text.set(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isSelected() {
|
||||||
|
return selected.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ReadOnlyBooleanProperty selectedProperty() {
|
||||||
|
return selected.getReadOnlyProperty();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setSelected(boolean selected) {
|
||||||
|
this.selected.set(selected);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Node getNode() {
|
||||||
|
return node.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ObjectProperty<Node> nodeProperty() {
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setNode(Node node) {
|
||||||
|
this.node.set(node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -21,14 +21,13 @@ import com.jfoenix.controls.JFXRippler;
|
|||||||
import javafx.animation.*;
|
import javafx.animation.*;
|
||||||
import javafx.application.Platform;
|
import javafx.application.Platform;
|
||||||
import javafx.beans.binding.Bindings;
|
import javafx.beans.binding.Bindings;
|
||||||
import javafx.beans.property.*;
|
import javafx.beans.property.ObjectProperty;
|
||||||
|
import javafx.beans.property.SimpleObjectProperty;
|
||||||
import javafx.collections.FXCollections;
|
import javafx.collections.FXCollections;
|
||||||
import javafx.collections.ListChangeListener;
|
|
||||||
import javafx.collections.ObservableList;
|
import javafx.collections.ObservableList;
|
||||||
import javafx.css.PseudoClass;
|
import javafx.css.PseudoClass;
|
||||||
import javafx.geometry.Insets;
|
import javafx.geometry.Insets;
|
||||||
import javafx.geometry.Side;
|
import javafx.geometry.Side;
|
||||||
import javafx.scene.AccessibleAttribute;
|
|
||||||
import javafx.scene.Node;
|
import javafx.scene.Node;
|
||||||
import javafx.scene.control.*;
|
import javafx.scene.control.*;
|
||||||
import javafx.scene.input.MouseButton;
|
import javafx.scene.input.MouseButton;
|
||||||
@@ -40,7 +39,7 @@ import javafx.util.Duration;
|
|||||||
import org.jackhuang.hmcl.ui.FXUtils;
|
import org.jackhuang.hmcl.ui.FXUtils;
|
||||||
import org.jackhuang.hmcl.util.javafx.MappedObservableList;
|
import org.jackhuang.hmcl.util.javafx.MappedObservableList;
|
||||||
|
|
||||||
public class TabHeader extends Control {
|
public class TabHeader extends Control implements TabControl {
|
||||||
|
|
||||||
public TabHeader(Tab... tabs) {
|
public TabHeader(Tab... tabs) {
|
||||||
getStyleClass().setAll("tab-header");
|
getStyleClass().setAll("tab-header");
|
||||||
@@ -51,11 +50,12 @@ public class TabHeader extends Control {
|
|||||||
|
|
||||||
private ObservableList<Tab> tabs = FXCollections.observableArrayList();
|
private ObservableList<Tab> tabs = FXCollections.observableArrayList();
|
||||||
|
|
||||||
|
@Override
|
||||||
public ObservableList<Tab> getTabs() {
|
public ObservableList<Tab> getTabs() {
|
||||||
return tabs;
|
return tabs;
|
||||||
}
|
}
|
||||||
|
|
||||||
private final ObjectProperty<SingleSelectionModel<Tab>> selectionModel = new SimpleObjectProperty<>(this, "selectionModel", new TabHeaderSelectionModel(this));
|
private final ObjectProperty<SingleSelectionModel<Tab>> selectionModel = new SimpleObjectProperty<>(this, "selectionModel", new TabControlSelectionModel(this));
|
||||||
|
|
||||||
public SingleSelectionModel<Tab> getSelectionModel() {
|
public SingleSelectionModel<Tab> getSelectionModel() {
|
||||||
return selectionModel.get();
|
return selectionModel.get();
|
||||||
@@ -69,151 +69,6 @@ public class TabHeader extends Control {
|
|||||||
this.selectionModel.set(selectionModel);
|
this.selectionModel.set(selectionModel);
|
||||||
}
|
}
|
||||||
|
|
||||||
static class TabHeaderSelectionModel extends SingleSelectionModel<Tab> {
|
|
||||||
private final TabHeader tabHeader;
|
|
||||||
|
|
||||||
public TabHeaderSelectionModel(final TabHeader t) {
|
|
||||||
if (t == null) {
|
|
||||||
throw new NullPointerException("TabPane can not be null");
|
|
||||||
}
|
|
||||||
this.tabHeader = t;
|
|
||||||
|
|
||||||
// watching for changes to the items list content
|
|
||||||
final ListChangeListener<Tab> itemsContentObserver = c -> {
|
|
||||||
while (c.next()) {
|
|
||||||
for (Tab tab : c.getRemoved()) {
|
|
||||||
if (tab != null && !tabHeader.getTabs().contains(tab)) {
|
|
||||||
if (tab.isSelected()) {
|
|
||||||
tab.setSelected(false);
|
|
||||||
final int tabIndex = c.getFrom();
|
|
||||||
|
|
||||||
// we always try to select the nearest, non-disabled
|
|
||||||
// tab from the position of the closed tab.
|
|
||||||
findNearestAvailableTab(tabIndex, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (c.wasAdded() || c.wasRemoved()) {
|
|
||||||
// The selected tab index can be out of sync with the list of tab if
|
|
||||||
// we add or remove tabs before the selected tab.
|
|
||||||
if (getSelectedIndex() != tabHeader.getTabs().indexOf(getSelectedItem())) {
|
|
||||||
clearAndSelect(tabHeader.getTabs().indexOf(getSelectedItem()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (getSelectedIndex() == -1 && getSelectedItem() == null && tabHeader.getTabs().size() > 0) {
|
|
||||||
// we go looking for the first non-disabled tab, as opposed to
|
|
||||||
// just selecting the first tab (fix for RT-36908)
|
|
||||||
findNearestAvailableTab(0, true);
|
|
||||||
} else if (tabHeader.getTabs().isEmpty()) {
|
|
||||||
clearSelection();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
if (this.tabHeader.getTabs() != null) {
|
|
||||||
this.tabHeader.getTabs().addListener(itemsContentObserver);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// API Implementation
|
|
||||||
@Override public void select(int index) {
|
|
||||||
if (index < 0 || (getItemCount() > 0 && index >= getItemCount()) ||
|
|
||||||
(index == getSelectedIndex() && getModelItem(index).isSelected())) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unselect the old tab
|
|
||||||
if (getSelectedIndex() >= 0 && getSelectedIndex() < tabHeader.getTabs().size()) {
|
|
||||||
tabHeader.getTabs().get(getSelectedIndex()).setSelected(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
setSelectedIndex(index);
|
|
||||||
|
|
||||||
Tab tab = getModelItem(index);
|
|
||||||
if (tab != null) {
|
|
||||||
setSelectedItem(tab);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Select the new tab
|
|
||||||
if (getSelectedIndex() >= 0 && getSelectedIndex() < tabHeader.getTabs().size()) {
|
|
||||||
tabHeader.getTabs().get(getSelectedIndex()).setSelected(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Does this get all the change events */
|
|
||||||
tabHeader.notifyAccessibleAttributeChanged(AccessibleAttribute.FOCUS_ITEM);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override public void select(Tab tab) {
|
|
||||||
final int itemCount = getItemCount();
|
|
||||||
|
|
||||||
for (int i = 0; i < itemCount; i++) {
|
|
||||||
final Tab value = getModelItem(i);
|
|
||||||
if (value != null && value.equals(tab)) {
|
|
||||||
select(i);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (tab != null) {
|
|
||||||
setSelectedItem(tab);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override protected Tab getModelItem(int index) {
|
|
||||||
final ObservableList<Tab> items = tabHeader.getTabs();
|
|
||||||
if (items == null) return null;
|
|
||||||
if (index < 0 || index >= items.size()) return null;
|
|
||||||
return items.get(index);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override protected int getItemCount() {
|
|
||||||
final ObservableList<Tab> items = tabHeader.getTabs();
|
|
||||||
return items == null ? 0 : items.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
private Tab findNearestAvailableTab(int tabIndex, boolean doSelect) {
|
|
||||||
// we always try to select the nearest, non-disabled
|
|
||||||
// tab from the position of the closed tab.
|
|
||||||
final int tabCount = getItemCount();
|
|
||||||
int i = 1;
|
|
||||||
Tab bestTab = null;
|
|
||||||
while (true) {
|
|
||||||
// look leftwards
|
|
||||||
int downPos = tabIndex - i;
|
|
||||||
if (downPos >= 0) {
|
|
||||||
Tab _tab = getModelItem(downPos);
|
|
||||||
if (_tab != null) {
|
|
||||||
bestTab = _tab;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// look rightwards. We subtract one as we need
|
|
||||||
// to take into account that a tab has been removed
|
|
||||||
// and if we don't do this we'll miss the tab
|
|
||||||
// to the right of the tab (as it has moved into
|
|
||||||
// the removed tabs position).
|
|
||||||
int upPos = tabIndex + i - 1;
|
|
||||||
if (upPos < tabCount) {
|
|
||||||
Tab _tab = getModelItem(upPos);
|
|
||||||
if (_tab != null) {
|
|
||||||
bestTab = _tab;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (downPos < 0 && upPos >= tabCount) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (doSelect && bestTab != null) {
|
|
||||||
select(bestTab);
|
|
||||||
}
|
|
||||||
|
|
||||||
return bestTab;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Skin<?> createDefaultSkin() {
|
protected Skin<?> createDefaultSkin() {
|
||||||
return new TabHeaderSkin(this);
|
return new TabHeaderSkin(this);
|
||||||
@@ -450,55 +305,4 @@ public class TabHeader extends Control {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class Tab {
|
|
||||||
private final StringProperty id = new SimpleStringProperty(this, "id");
|
|
||||||
private final StringProperty text = new SimpleStringProperty(this, "text");
|
|
||||||
private final ReadOnlyBooleanWrapper selected = new ReadOnlyBooleanWrapper(this, "selected");
|
|
||||||
|
|
||||||
public Tab(String id) {
|
|
||||||
setId(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Tab(String id, String text) {
|
|
||||||
setId(id);
|
|
||||||
setText(text);
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getId() {
|
|
||||||
return id.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
public StringProperty idProperty() {
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setId(String id) {
|
|
||||||
this.id.set(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getText() {
|
|
||||||
return text.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
public StringProperty textProperty() {
|
|
||||||
return text;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setText(String text) {
|
|
||||||
this.text.set(text);
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isSelected() {
|
|
||||||
return selected.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
public ReadOnlyBooleanProperty selectedProperty() {
|
|
||||||
return selected.getReadOnlyProperty();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setSelected(boolean selected) {
|
|
||||||
this.selected.set(selected);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,13 +17,9 @@
|
|||||||
*/
|
*/
|
||||||
package org.jackhuang.hmcl.ui.decorator;
|
package org.jackhuang.hmcl.ui.decorator;
|
||||||
|
|
||||||
import javafx.beans.binding.Bindings;
|
|
||||||
import javafx.scene.Node;
|
import javafx.scene.Node;
|
||||||
import javafx.scene.layout.Region;
|
|
||||||
import javafx.scene.layout.StackPane;
|
|
||||||
import org.jackhuang.hmcl.ui.animation.AnimationProducer;
|
import org.jackhuang.hmcl.ui.animation.AnimationProducer;
|
||||||
import org.jackhuang.hmcl.ui.construct.Navigator;
|
import org.jackhuang.hmcl.ui.construct.Navigator;
|
||||||
import org.jackhuang.hmcl.ui.wizard.Refreshable;
|
|
||||||
|
|
||||||
public abstract class DecoratorNavigatorPage extends DecoratorTransitionPage {
|
public abstract class DecoratorNavigatorPage extends DecoratorTransitionPage {
|
||||||
protected final Navigator navigator = new Navigator();
|
protected final Navigator navigator = new Navigator();
|
||||||
@@ -31,6 +27,7 @@ public abstract class DecoratorNavigatorPage extends DecoratorTransitionPage {
|
|||||||
{
|
{
|
||||||
this.navigator.setOnNavigating(this::onNavigating);
|
this.navigator.setOnNavigating(this::onNavigating);
|
||||||
this.navigator.setOnNavigated(this::onNavigated);
|
this.navigator.setOnNavigated(this::onNavigated);
|
||||||
|
backableProperty().bind(navigator.backableProperty());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -50,39 +47,11 @@ public abstract class DecoratorNavigatorPage extends DecoratorTransitionPage {
|
|||||||
|
|
||||||
private void onNavigating(Navigator.NavigationEvent event) {
|
private void onNavigating(Navigator.NavigationEvent event) {
|
||||||
if (event.getSource() != this.navigator) return;
|
if (event.getSource() != this.navigator) return;
|
||||||
Node from = event.getNode();
|
onNavigating(event.getNode());
|
||||||
|
|
||||||
if (from instanceof DecoratorPage)
|
|
||||||
((DecoratorPage) from).back();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onNavigated(Navigator.NavigationEvent event) {
|
private void onNavigated(Navigator.NavigationEvent event) {
|
||||||
if (event.getSource() != this.navigator) return;
|
if (event.getSource() != this.navigator) return;
|
||||||
Node to = event.getNode();
|
onNavigated(event.getNode());
|
||||||
|
|
||||||
if (to instanceof Refreshable) {
|
|
||||||
refreshableProperty().bind(((Refreshable) to).refreshableProperty());
|
|
||||||
} else {
|
|
||||||
refreshableProperty().unbind();
|
|
||||||
refreshableProperty().set(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (to instanceof DecoratorPage) {
|
|
||||||
state.bind(Bindings.createObjectBinding(() -> {
|
|
||||||
State state = ((DecoratorPage) to).stateProperty().get();
|
|
||||||
return new State(state.getTitle(), state.getTitleNode(), navigator.canGoBack(), state.isRefreshable(), true);
|
|
||||||
}, ((DecoratorPage) to).stateProperty()));
|
|
||||||
} else {
|
|
||||||
state.unbind();
|
|
||||||
state.set(new State("", null, navigator.canGoBack(), false, true));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (to instanceof Region) {
|
|
||||||
Region region = (Region) to;
|
|
||||||
// Let root pane fix window size.
|
|
||||||
StackPane parent = (StackPane) region.getParent();
|
|
||||||
region.prefWidthProperty().bind(parent.widthProperty());
|
|
||||||
region.prefHeightProperty().bind(parent.heightProperty());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,74 @@
|
|||||||
|
/*
|
||||||
|
* Hello Minecraft! Launcher
|
||||||
|
* Copyright (C) 2020 huangyuhui <huanghongxun2008@126.com> and contributors
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package org.jackhuang.hmcl.ui.decorator;
|
||||||
|
|
||||||
|
import javafx.beans.property.ObjectProperty;
|
||||||
|
import javafx.beans.property.SimpleObjectProperty;
|
||||||
|
import javafx.collections.FXCollections;
|
||||||
|
import javafx.collections.ObservableList;
|
||||||
|
import javafx.scene.control.SingleSelectionModel;
|
||||||
|
import org.jackhuang.hmcl.ui.animation.ContainerAnimations;
|
||||||
|
import org.jackhuang.hmcl.ui.construct.Navigator;
|
||||||
|
import org.jackhuang.hmcl.ui.construct.TabControl;
|
||||||
|
import org.jackhuang.hmcl.ui.construct.TabHeader;
|
||||||
|
|
||||||
|
public abstract class DecoratorTabPage extends DecoratorTransitionPage implements TabControl {
|
||||||
|
|
||||||
|
public DecoratorTabPage() {
|
||||||
|
getSelectionModel().selectedItemProperty().addListener((a, b, newValue) -> {
|
||||||
|
if (newValue.getNode() == null && newValue.getNodeSupplier() != null) {
|
||||||
|
newValue.setNode(newValue.getNodeSupplier().get());
|
||||||
|
}
|
||||||
|
if (newValue.getNode() != null) {
|
||||||
|
onNavigating(getCurrentPage());
|
||||||
|
if (getCurrentPage() != null) getCurrentPage().fireEvent(new Navigator.NavigationEvent(null, getCurrentPage(), Navigator.NavigationEvent.NAVIGATING));
|
||||||
|
navigate(newValue.getNode(), ContainerAnimations.FADE.getAnimationProducer());
|
||||||
|
onNavigated(getCurrentPage());
|
||||||
|
if (getCurrentPage() != null) getCurrentPage().fireEvent(new Navigator.NavigationEvent(null, getCurrentPage(), Navigator.NavigationEvent.NAVIGATED));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public DecoratorTabPage(TabHeader.Tab... tabs) {
|
||||||
|
this();
|
||||||
|
if (tabs != null) {
|
||||||
|
getTabs().addAll(tabs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private ObservableList<TabHeader.Tab> tabs = FXCollections.observableArrayList();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ObservableList<TabHeader.Tab> getTabs() {
|
||||||
|
return tabs;
|
||||||
|
}
|
||||||
|
|
||||||
|
private final ObjectProperty<SingleSelectionModel<TabHeader.Tab>> selectionModel = new SimpleObjectProperty<>(this, "selectionModel", new TabControl.TabControlSelectionModel(this));
|
||||||
|
|
||||||
|
public SingleSelectionModel<TabHeader.Tab> getSelectionModel() {
|
||||||
|
return selectionModel.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ObjectProperty<SingleSelectionModel<TabHeader.Tab>> selectionModelProperty() {
|
||||||
|
return selectionModel;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSelectionModel(SingleSelectionModel<TabHeader.Tab> selectionModel) {
|
||||||
|
this.selectionModel.set(selectionModel);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -17,6 +17,7 @@
|
|||||||
*/
|
*/
|
||||||
package org.jackhuang.hmcl.ui.decorator;
|
package org.jackhuang.hmcl.ui.decorator;
|
||||||
|
|
||||||
|
import javafx.beans.binding.Bindings;
|
||||||
import javafx.beans.property.BooleanProperty;
|
import javafx.beans.property.BooleanProperty;
|
||||||
import javafx.beans.property.ReadOnlyObjectProperty;
|
import javafx.beans.property.ReadOnlyObjectProperty;
|
||||||
import javafx.beans.property.ReadOnlyObjectWrapper;
|
import javafx.beans.property.ReadOnlyObjectWrapper;
|
||||||
@@ -24,6 +25,8 @@ import javafx.beans.property.SimpleBooleanProperty;
|
|||||||
import javafx.scene.Node;
|
import javafx.scene.Node;
|
||||||
import javafx.scene.control.Control;
|
import javafx.scene.control.Control;
|
||||||
import javafx.scene.control.Skin;
|
import javafx.scene.control.Skin;
|
||||||
|
import javafx.scene.layout.Region;
|
||||||
|
import javafx.scene.layout.StackPane;
|
||||||
import org.jackhuang.hmcl.ui.animation.AnimationProducer;
|
import org.jackhuang.hmcl.ui.animation.AnimationProducer;
|
||||||
import org.jackhuang.hmcl.ui.animation.TransitionPane;
|
import org.jackhuang.hmcl.ui.animation.TransitionPane;
|
||||||
import org.jackhuang.hmcl.ui.wizard.Refreshable;
|
import org.jackhuang.hmcl.ui.wizard.Refreshable;
|
||||||
@@ -32,13 +35,45 @@ import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
|
|||||||
|
|
||||||
public abstract class DecoratorTransitionPage extends Control implements DecoratorPage {
|
public abstract class DecoratorTransitionPage extends Control implements DecoratorPage {
|
||||||
protected final ReadOnlyObjectWrapper<State> state = new ReadOnlyObjectWrapper<>(State.fromTitle(i18n("")));
|
protected final ReadOnlyObjectWrapper<State> state = new ReadOnlyObjectWrapper<>(State.fromTitle(i18n("")));
|
||||||
|
private final BooleanProperty backable = new SimpleBooleanProperty(false);
|
||||||
private final BooleanProperty refreshable = new SimpleBooleanProperty(false);
|
private final BooleanProperty refreshable = new SimpleBooleanProperty(false);
|
||||||
private Node currentPage;
|
private Node currentPage;
|
||||||
protected final TransitionPane transitionPane = new TransitionPane();
|
protected final TransitionPane transitionPane = new TransitionPane();
|
||||||
|
|
||||||
protected void navigate(Node page, AnimationProducer animation) {
|
protected void navigate(Node page, AnimationProducer animation) {
|
||||||
transitionPane.setContent(currentPage = page, animation);
|
transitionPane.setContent(currentPage = page, animation);
|
||||||
refreshable.setValue(page instanceof Refreshable);
|
}
|
||||||
|
|
||||||
|
protected void onNavigating(Node from) {
|
||||||
|
if (from instanceof DecoratorPage)
|
||||||
|
((DecoratorPage) from).back();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void onNavigated(Node to) {
|
||||||
|
if (to instanceof Refreshable) {
|
||||||
|
refreshableProperty().bind(((Refreshable) to).refreshableProperty());
|
||||||
|
} else {
|
||||||
|
refreshableProperty().unbind();
|
||||||
|
refreshableProperty().set(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (to instanceof DecoratorPage) {
|
||||||
|
state.bind(Bindings.createObjectBinding(() -> {
|
||||||
|
State state = ((DecoratorPage) to).stateProperty().get();
|
||||||
|
return new State(state.getTitle(), state.getTitleNode(), backable.get(), state.isRefreshable(), true);
|
||||||
|
}, ((DecoratorPage) to).stateProperty()));
|
||||||
|
} else {
|
||||||
|
state.unbind();
|
||||||
|
state.set(new State("", null, backable.get(), false, true));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (to instanceof Region) {
|
||||||
|
Region region = (Region) to;
|
||||||
|
// Let root pane fix window size.
|
||||||
|
StackPane parent = (StackPane) region.getParent();
|
||||||
|
region.prefWidthProperty().bind(parent.widthProperty());
|
||||||
|
region.prefHeightProperty().bind(parent.heightProperty());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -48,6 +83,18 @@ public abstract class DecoratorTransitionPage extends Control implements Decorat
|
|||||||
return currentPage;
|
return currentPage;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isBackable() {
|
||||||
|
return backable.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
public BooleanProperty backableProperty() {
|
||||||
|
return backable;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setBackable(boolean backable) {
|
||||||
|
this.backable.set(backable);
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isRefreshable() {
|
public boolean isRefreshable() {
|
||||||
return refreshable.get();
|
return refreshable.get();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ public class DecoratorWizardDisplayer extends DecoratorTransitionPage implements
|
|||||||
wizardController.setProvider(provider);
|
wizardController.setProvider(provider);
|
||||||
wizardController.onStart();
|
wizardController.onStart();
|
||||||
|
|
||||||
addEventHandler(Navigator.NavigationEvent.NAVIGATING, this::onDecoratorPageNavigating);
|
addEventHandler(Navigator.NavigationEvent.NAVIGATED, this::onDecoratorPageNavigating);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -77,6 +77,13 @@ public class DecoratorWizardDisplayer extends DecoratorTransitionPage implements
|
|||||||
else
|
else
|
||||||
title = "";
|
title = "";
|
||||||
state.set(new State(title, null, true, refreshableProperty().get(), true));
|
state.set(new State(title, null, true, refreshableProperty().get(), true));
|
||||||
|
|
||||||
|
if (page instanceof Refreshable) {
|
||||||
|
refreshableProperty().bind(((Refreshable) page).refreshableProperty());
|
||||||
|
} else {
|
||||||
|
refreshableProperty().unbind();
|
||||||
|
refreshableProperty().set(false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ package org.jackhuang.hmcl.ui.main;
|
|||||||
|
|
||||||
import javafx.application.Platform;
|
import javafx.application.Platform;
|
||||||
import javafx.geometry.Insets;
|
import javafx.geometry.Insets;
|
||||||
|
import javafx.scene.Node;
|
||||||
import javafx.scene.control.SkinBase;
|
import javafx.scene.control.SkinBase;
|
||||||
import javafx.scene.layout.BorderPane;
|
import javafx.scene.layout.BorderPane;
|
||||||
import javafx.scene.layout.StackPane;
|
import javafx.scene.layout.StackPane;
|
||||||
@@ -37,10 +38,10 @@ import org.jackhuang.hmcl.ui.FXUtils;
|
|||||||
import org.jackhuang.hmcl.ui.account.AccountAdvancedListItem;
|
import org.jackhuang.hmcl.ui.account.AccountAdvancedListItem;
|
||||||
import org.jackhuang.hmcl.ui.account.AccountList;
|
import org.jackhuang.hmcl.ui.account.AccountList;
|
||||||
import org.jackhuang.hmcl.ui.account.AddAccountPane;
|
import org.jackhuang.hmcl.ui.account.AddAccountPane;
|
||||||
import org.jackhuang.hmcl.ui.animation.ContainerAnimations;
|
|
||||||
import org.jackhuang.hmcl.ui.construct.AdvancedListBox;
|
import org.jackhuang.hmcl.ui.construct.AdvancedListBox;
|
||||||
import org.jackhuang.hmcl.ui.construct.AdvancedListItem;
|
import org.jackhuang.hmcl.ui.construct.AdvancedListItem;
|
||||||
import org.jackhuang.hmcl.ui.decorator.DecoratorNavigatorPage;
|
import org.jackhuang.hmcl.ui.construct.TabHeader;
|
||||||
|
import org.jackhuang.hmcl.ui.decorator.DecoratorTabPage;
|
||||||
import org.jackhuang.hmcl.ui.download.ModpackInstallWizardProvider;
|
import org.jackhuang.hmcl.ui.download.ModpackInstallWizardProvider;
|
||||||
import org.jackhuang.hmcl.ui.profile.ProfileAdvancedListItem;
|
import org.jackhuang.hmcl.ui.profile.ProfileAdvancedListItem;
|
||||||
import org.jackhuang.hmcl.ui.profile.ProfileList;
|
import org.jackhuang.hmcl.ui.profile.ProfileList;
|
||||||
@@ -63,19 +64,48 @@ import static org.jackhuang.hmcl.ui.FXUtils.newImage;
|
|||||||
import static org.jackhuang.hmcl.ui.FXUtils.runInFX;
|
import static org.jackhuang.hmcl.ui.FXUtils.runInFX;
|
||||||
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
|
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
|
||||||
|
|
||||||
public class RootPage extends DecoratorNavigatorPage {
|
public class RootPage extends DecoratorTabPage {
|
||||||
private MainPage mainPage = null;
|
private MainPage mainPage = null;
|
||||||
private SettingsPage settingsPage = null;
|
private SettingsPage settingsPage = null;
|
||||||
private GameList gameListPage = null;
|
private GameList gameListPage = null;
|
||||||
private AccountList accountListPage = null;
|
private AccountList accountListPage = null;
|
||||||
private ProfileList profileListPage = null;
|
private ProfileList profileListPage = null;
|
||||||
|
|
||||||
|
private final TabHeader.Tab mainTab = new TabHeader.Tab("main");
|
||||||
|
private final TabHeader.Tab settingsTab = new TabHeader.Tab("settings");
|
||||||
|
private final TabHeader.Tab gameTab = new TabHeader.Tab("game");
|
||||||
|
private final TabHeader.Tab accountTab = new TabHeader.Tab("account");
|
||||||
|
private final TabHeader.Tab profileTab = new TabHeader.Tab("profile");
|
||||||
|
|
||||||
public RootPage() {
|
public RootPage() {
|
||||||
EventBus.EVENT_BUS.channel(RefreshedVersionsEvent.class).register(event -> onRefreshedVersions((HMCLGameRepository) event.getSource()));
|
EventBus.EVENT_BUS.channel(RefreshedVersionsEvent.class).register(event -> onRefreshedVersions((HMCLGameRepository) event.getSource()));
|
||||||
|
|
||||||
Profile profile = Profiles.getSelectedProfile();
|
Profile profile = Profiles.getSelectedProfile();
|
||||||
if (profile != null && profile.getRepository().isLoaded())
|
if (profile != null && profile.getRepository().isLoaded())
|
||||||
onRefreshedVersions(Profiles.selectedProfileProperty().get().getRepository());
|
onRefreshedVersions(Profiles.selectedProfileProperty().get().getRepository());
|
||||||
|
|
||||||
|
mainTab.setNodeSupplier(this::getMainPage);
|
||||||
|
settingsTab.setNodeSupplier(this::getSettingsPage);
|
||||||
|
gameTab.setNodeSupplier(this::getGameListPage);
|
||||||
|
accountTab.setNodeSupplier(this::getAccountListPage);
|
||||||
|
profileTab.setNodeSupplier(this::getProfileListPage);
|
||||||
|
getTabs().setAll(mainTab, settingsTab, gameTab, accountTab, profileTab);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean back() {
|
||||||
|
if (mainTab.isSelected()) return true;
|
||||||
|
else {
|
||||||
|
getSelectionModel().select(mainTab);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onNavigated(Node to) {
|
||||||
|
backableProperty().set(!(to instanceof MainPage));
|
||||||
|
|
||||||
|
super.onNavigated(to);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -156,7 +186,8 @@ public class RootPage extends DecoratorNavigatorPage {
|
|||||||
|
|
||||||
// first item in left sidebar
|
// first item in left sidebar
|
||||||
AccountAdvancedListItem accountListItem = new AccountAdvancedListItem();
|
AccountAdvancedListItem accountListItem = new AccountAdvancedListItem();
|
||||||
accountListItem.setOnAction(e -> getSkinnable().navigate(getSkinnable().getAccountListPage(), ContainerAnimations.FADE.getAnimationProducer()));
|
accountListItem.activeProperty().bind(control.accountTab.selectedProperty());
|
||||||
|
accountListItem.setOnAction(e -> control.getSelectionModel().select(control.accountTab));
|
||||||
accountListItem.accountProperty().bind(Accounts.selectedAccountProperty());
|
accountListItem.accountProperty().bind(Accounts.selectedAccountProperty());
|
||||||
|
|
||||||
// second item in left sidebar
|
// second item in left sidebar
|
||||||
@@ -166,7 +197,7 @@ public class RootPage extends DecoratorNavigatorPage {
|
|||||||
Profile profile = Profiles.getSelectedProfile();
|
Profile profile = Profiles.getSelectedProfile();
|
||||||
String version = Profiles.getSelectedVersion();
|
String version = Profiles.getSelectedVersion();
|
||||||
if (version == null) {
|
if (version == null) {
|
||||||
getSkinnable().navigate(getSkinnable().getGameListPage(), ContainerAnimations.FADE.getAnimationProducer());
|
control.getSelectionModel().select(control.gameTab);
|
||||||
} else {
|
} else {
|
||||||
Versions.modifyGameSettings(profile, version);
|
Versions.modifyGameSettings(profile, version);
|
||||||
}
|
}
|
||||||
@@ -174,20 +205,23 @@ public class RootPage extends DecoratorNavigatorPage {
|
|||||||
|
|
||||||
// third item in left sidebar
|
// third item in left sidebar
|
||||||
AdvancedListItem gameItem = new AdvancedListItem();
|
AdvancedListItem gameItem = new AdvancedListItem();
|
||||||
|
gameItem.activeProperty().bind(control.gameTab.selectedProperty());
|
||||||
gameItem.setImage(newImage("/assets/img/bookshelf.png"));
|
gameItem.setImage(newImage("/assets/img/bookshelf.png"));
|
||||||
gameItem.setTitle(i18n("version.manage"));
|
gameItem.setTitle(i18n("version.manage"));
|
||||||
gameItem.setOnAction(e -> getSkinnable().navigate(getSkinnable().getGameListPage(), ContainerAnimations.FADE.getAnimationProducer()));
|
gameItem.setOnAction(e -> control.getSelectionModel().select(control.gameTab));
|
||||||
|
|
||||||
// forth item in left sidebar
|
// forth item in left sidebar
|
||||||
ProfileAdvancedListItem profileListItem = new ProfileAdvancedListItem();
|
ProfileAdvancedListItem profileListItem = new ProfileAdvancedListItem();
|
||||||
profileListItem.setOnAction(e -> getSkinnable().navigate(getSkinnable().getProfileListPage(), ContainerAnimations.FADE.getAnimationProducer()));
|
profileListItem.activeProperty().bind(control.profileTab.selectedProperty());
|
||||||
|
profileListItem.setOnAction(e -> control.getSelectionModel().select(control.profileTab));
|
||||||
profileListItem.profileProperty().bind(Profiles.selectedProfileProperty());
|
profileListItem.profileProperty().bind(Profiles.selectedProfileProperty());
|
||||||
|
|
||||||
// fifth item in left sidebar
|
// fifth item in left sidebar
|
||||||
AdvancedListItem launcherSettingsItem = new AdvancedListItem();
|
AdvancedListItem launcherSettingsItem = new AdvancedListItem();
|
||||||
|
launcherSettingsItem.activeProperty().bind(control.settingsTab.selectedProperty());
|
||||||
launcherSettingsItem.setImage(newImage("/assets/img/command.png"));
|
launcherSettingsItem.setImage(newImage("/assets/img/command.png"));
|
||||||
launcherSettingsItem.setTitle(i18n("settings.launcher"));
|
launcherSettingsItem.setTitle(i18n("settings.launcher"));
|
||||||
launcherSettingsItem.setOnAction(e -> getSkinnable().navigate(getSkinnable().getSettingsPage(), ContainerAnimations.FADE.getAnimationProducer()));
|
launcherSettingsItem.setOnAction(e -> control.getSelectionModel().select(control.settingsTab));
|
||||||
|
|
||||||
// the left sidebar
|
// the left sidebar
|
||||||
AdvancedListBox sideBar = new AdvancedListBox()
|
AdvancedListBox sideBar = new AdvancedListBox()
|
||||||
@@ -217,10 +251,10 @@ public class RootPage extends DecoratorNavigatorPage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
control.navigator.getStyleClass().add("jfx-decorator-content-container");
|
control.transitionPane.getStyleClass().add("jfx-decorator-content-container");
|
||||||
control.navigator.init(getSkinnable().getMainPage());
|
control.transitionPane.getChildren().setAll(getSkinnable().getMainPage());
|
||||||
FXUtils.setOverflowHidden(control.navigator, 8);
|
FXUtils.setOverflowHidden(control.transitionPane, 8);
|
||||||
StackPane wrapper = new StackPane(control.navigator);
|
StackPane wrapper = new StackPane(control.transitionPane);
|
||||||
wrapper.setPadding(new Insets(4));
|
wrapper.setPadding(new Insets(4));
|
||||||
root.setCenter(wrapper);
|
root.setCenter(wrapper);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ public final class SettingsPage extends SettingsView implements DecoratorPage {
|
|||||||
|
|
||||||
public SettingsPage() {
|
public SettingsPage() {
|
||||||
FXUtils.smoothScrolling(scroll);
|
FXUtils.smoothScrolling(scroll);
|
||||||
addEventHandler(Navigator.NavigationEvent.NAVIGATING, this::onDecoratorPageNavigating);
|
addEventHandler(Navigator.NavigationEvent.NAVIGATED, this::onDecoratorPageNavigating);
|
||||||
|
|
||||||
// ==== Download sources ====
|
// ==== Download sources ====
|
||||||
cboDownloadSource.getItems().setAll(DownloadProviders.providersById.keySet());
|
cboDownloadSource.getItems().setAll(DownloadProviders.providersById.keySet());
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ public class ProfileListItemSkin extends SkinBase<ProfileListItem> {
|
|||||||
right.getChildren().add(btnRemove);
|
right.getChildren().add(btnRemove);
|
||||||
root.setRight(right);
|
root.setRight(right);
|
||||||
|
|
||||||
root.setStyle("-fx-background-color: white; -fx-padding: 8 8 8 0;");
|
root.setStyle("-fx-background-color: white; -fx-background-radius: 4; -fx-padding: 8 8 8 0;");
|
||||||
JFXDepthManager.setDepth(root, 1);
|
JFXDepthManager.setDepth(root, 1);
|
||||||
item.titleProperty().bind(skinnable.titleProperty());
|
item.titleProperty().bind(skinnable.titleProperty());
|
||||||
item.subtitleProperty().bind(skinnable.subtitleProperty());
|
item.subtitleProperty().bind(skinnable.subtitleProperty());
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ public class GameList extends ListPageBase<GameListItem> implements DecoratorPag
|
|||||||
});
|
});
|
||||||
|
|
||||||
Profiles.registerVersionsListener(this::loadVersions);
|
Profiles.registerVersionsListener(this::loadVersions);
|
||||||
addEventHandler(Navigator.NavigationEvent.NAVIGATING, this::onDecoratorPageNavigating);
|
addEventHandler(Navigator.NavigationEvent.NAVIGATED, this::onDecoratorPageNavigating);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void loadVersions(Profile profile) {
|
private void loadVersions(Profile profile) {
|
||||||
|
|||||||
@@ -80,6 +80,11 @@ public class VersionPage extends Control implements DecoratorPage {
|
|||||||
loadVersion(newValue, profile);
|
loadVersion(newValue, profile);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
versionSettingsTab.setNode(versionSettingsPage);
|
||||||
|
modListTab.setNode(modListPage);
|
||||||
|
installerListTab.setNode(installerListPage);
|
||||||
|
worldListTab.setNode(worldListPage);
|
||||||
|
|
||||||
addEventHandler(Navigator.NavigationEvent.NAVIGATED, this::onNavigated);
|
addEventHandler(Navigator.NavigationEvent.NAVIGATED, this::onNavigated);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -211,15 +216,7 @@ public class VersionPage extends Control implements DecoratorPage {
|
|||||||
control.worldListTab);
|
control.worldListTab);
|
||||||
control.selectedTab.bind(tabPane.getSelectionModel().selectedItemProperty());
|
control.selectedTab.bind(tabPane.getSelectionModel().selectedItemProperty());
|
||||||
FXUtils.onChangeAndOperate(tabPane.getSelectionModel().selectedItemProperty(), newValue -> {
|
FXUtils.onChangeAndOperate(tabPane.getSelectionModel().selectedItemProperty(), newValue -> {
|
||||||
if (control.versionSettingsTab.equals(newValue)) {
|
control.transitionPane.setContent(newValue.getNode(), ContainerAnimations.FADE.getAnimationProducer());
|
||||||
control.transitionPane.setContent(control.versionSettingsPage, ContainerAnimations.FADE.getAnimationProducer());
|
|
||||||
} else if (control.modListTab.equals(newValue)) {
|
|
||||||
control.transitionPane.setContent(control.modListPage, ContainerAnimations.FADE.getAnimationProducer());
|
|
||||||
} else if (control.installerListTab.equals(newValue)) {
|
|
||||||
control.transitionPane.setContent(control.installerListPage, ContainerAnimations.FADE.getAnimationProducer());
|
|
||||||
} else if (control.worldListTab.equals(newValue)) {
|
|
||||||
control.transitionPane.setContent(control.worldListPage, ContainerAnimations.FADE.getAnimationProducer());
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
HBox toolBar = new HBox();
|
HBox toolBar = new HBox();
|
||||||
|
|||||||
@@ -105,7 +105,7 @@ public final class VersionSettingsPage extends StackPane implements DecoratorPag
|
|||||||
|
|
||||||
public VersionSettingsPage() {
|
public VersionSettingsPage() {
|
||||||
FXUtils.loadFXML(this, "/assets/fxml/version/version-settings.fxml");
|
FXUtils.loadFXML(this, "/assets/fxml/version/version-settings.fxml");
|
||||||
addEventHandler(Navigator.NavigationEvent.NAVIGATING, this::onDecoratorPageNavigating);
|
addEventHandler(Navigator.NavigationEvent.NAVIGATED, this::onDecoratorPageNavigating);
|
||||||
|
|
||||||
cboLauncherVisibility.getItems().setAll(LauncherVisibility.values());
|
cboLauncherVisibility.getItems().setAll(LauncherVisibility.values());
|
||||||
cboLauncherVisibility.setConverter(stringConverter(e -> i18n("settings.advanced.launcher_visibility." + e.name().toLowerCase())));
|
cboLauncherVisibility.setConverter(stringConverter(e -> i18n("settings.advanced.launcher_visibility." + e.name().toLowerCase())));
|
||||||
|
|||||||
@@ -19,5 +19,6 @@
|
|||||||
-fx-base-color: #5c6bc0;
|
-fx-base-color: #5c6bc0;
|
||||||
-fx-base-darker-color: derive(-fx-base-color, -10%);
|
-fx-base-darker-color: derive(-fx-base-color, -10%);
|
||||||
-fx-base-check-color: derive(-fx-base-color, 30%);
|
-fx-base-check-color: derive(-fx-base-color, 30%);
|
||||||
|
-fx-base-rippler-color: rgba(92, 107, 192, 0.3);
|
||||||
-fx-base-text-fill: white;
|
-fx-base-text-fill: white;
|
||||||
}
|
}
|
||||||
@@ -19,5 +19,6 @@
|
|||||||
-fx-base-color: %base-color%;
|
-fx-base-color: %base-color%;
|
||||||
-fx-base-darker-color: derive(-fx-base-color, -10%);
|
-fx-base-darker-color: derive(-fx-base-color, -10%);
|
||||||
-fx-base-check-color: derive(-fx-base-color, 30%);
|
-fx-base-check-color: derive(-fx-base-color, 30%);
|
||||||
|
-fx-base-rippler-color: %base-rippler-color%;
|
||||||
-fx-base-text-fill: %font-color%;
|
-fx-base-text-fill: %font-color%;
|
||||||
}
|
}
|
||||||
@@ -71,6 +71,10 @@
|
|||||||
-fx-text-fill: -fx-base-text-fill;
|
-fx-text-fill: -fx-base-text-fill;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.advanced-list-item:selected .container {
|
||||||
|
-fx-background-color: -fx-base-rippler-color;
|
||||||
|
}
|
||||||
|
|
||||||
.notice-pane > .label {
|
.notice-pane > .label {
|
||||||
-fx-text-fill: #0079FF;
|
-fx-text-fill: #0079FF;
|
||||||
-fx-font-size: 20;
|
-fx-font-size: 20;
|
||||||
@@ -692,6 +696,16 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.options-list-item:first {
|
.options-list-item:first {
|
||||||
|
-fx-background-radius: 4 4 0 0;
|
||||||
|
-fx-border-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.options-list-item:last {
|
||||||
|
-fx-background-radius: 0 0 4 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.options-list-item:first:last {
|
||||||
|
-fx-background-radius: 4 4 4 4;
|
||||||
-fx-border-width: 0;
|
-fx-border-width: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user