mirror of
https://github.com/HMCL-dev/HMCL.git
synced 2025-02-11 16:59:54 +08:00
alt: root page tab
This commit is contained in:
parent
e11a5389c6
commit
1c3b3bf050
@ -23,7 +23,6 @@ import com.google.gson.stream.JsonWriter;
|
||||
import javafx.beans.binding.Bindings;
|
||||
import javafx.beans.binding.ObjectBinding;
|
||||
import javafx.scene.paint.Color;
|
||||
|
||||
import org.jackhuang.hmcl.util.Logging;
|
||||
import org.jackhuang.hmcl.util.ResourceNotFoundError;
|
||||
import org.jackhuang.hmcl.util.io.FileUtils;
|
||||
@ -50,12 +49,14 @@ public class Theme {
|
||||
Color.web("#B71C1C") // red
|
||||
};
|
||||
|
||||
private final Color paint;
|
||||
private final String color;
|
||||
private final String name;
|
||||
|
||||
Theme(String name, String color) {
|
||||
this.name = name;
|
||||
this.color = color;
|
||||
this.paint = Color.web(color);
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
@ -84,6 +85,7 @@ public class Theme {
|
||||
File temp = File.createTempFile("hmcl", ".css");
|
||||
FileUtils.writeText(temp, IOUtils.readFullyAsString(ResourceNotFoundError.getResourceAsStream("/assets/css/custom.css"))
|
||||
.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())));
|
||||
css = temp.toURI().toString();
|
||||
} catch (IOException | NullPointerException e) {
|
||||
|
@ -100,7 +100,7 @@ public class AccountListItemSkin extends SkinBase<AccountListItem> {
|
||||
right.getChildren().add(btnRemove);
|
||||
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);
|
||||
|
||||
getChildren().setAll(root);
|
||||
|
@ -23,6 +23,7 @@ import javafx.collections.ObservableList;
|
||||
import org.jackhuang.hmcl.auth.authlibinjector.AuthlibInjectorServer;
|
||||
import org.jackhuang.hmcl.ui.Controllers;
|
||||
import org.jackhuang.hmcl.ui.ListPage;
|
||||
import org.jackhuang.hmcl.ui.construct.Navigator;
|
||||
import org.jackhuang.hmcl.ui.decorator.DecoratorPage;
|
||||
import org.jackhuang.hmcl.util.javafx.MappedObservableList;
|
||||
|
||||
@ -37,6 +38,7 @@ public class AuthlibInjectorServersPage extends ListPage<AuthlibInjectorServerIt
|
||||
public AuthlibInjectorServersPage() {
|
||||
serverItems = MappedObservableList.create(config().getAuthlibInjectorServers(), this::createServerItem);
|
||||
Bindings.bindContent(itemsProperty(), serverItems);
|
||||
addEventHandler(Navigator.NavigationEvent.NAVIGATED, this::onDecoratorPageNavigating);
|
||||
}
|
||||
|
||||
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<Node> rightGraphic = new SimpleObjectProperty<>(this, "rightGraphic");
|
||||
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 BooleanProperty actionButtonVisible = new SimpleBooleanProperty(this, "actionButtonVisible", true);
|
||||
|
||||
public AdvancedListItem() {
|
||||
getStyleClass().add("advanced-list-item");
|
||||
addEventHandler(MouseEvent.MOUSE_CLICKED, e -> fireEvent(new ActionEvent()));
|
||||
}
|
||||
|
||||
@ -73,6 +75,18 @@ public class AdvancedListItem extends Control {
|
||||
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() {
|
||||
return subtitle.get();
|
||||
}
|
||||
|
@ -17,6 +17,7 @@
|
||||
*/
|
||||
package org.jackhuang.hmcl.ui.construct;
|
||||
|
||||
import javafx.css.PseudoClass;
|
||||
import javafx.geometry.Insets;
|
||||
import javafx.geometry.Pos;
|
||||
import javafx.scene.control.Label;
|
||||
@ -30,13 +31,19 @@ import javafx.scene.text.TextAlignment;
|
||||
import org.jackhuang.hmcl.ui.FXUtils;
|
||||
|
||||
public class AdvancedListItemSkin extends SkinBase<AdvancedListItem> {
|
||||
private final PseudoClass SELECTED = PseudoClass.getPseudoClass("selected");
|
||||
|
||||
public AdvancedListItemSkin(AdvancedListItem skinnable) {
|
||||
super(skinnable);
|
||||
|
||||
StackPane stackPane = new StackPane();
|
||||
stackPane.getStyleClass().add("container");
|
||||
RipplerContainer container = new RipplerContainer(stackPane);
|
||||
|
||||
FXUtils.onChangeAndOperate(skinnable.activeProperty(), active -> {
|
||||
skinnable.pseudoClassStateChanged(SELECTED, active);
|
||||
});
|
||||
|
||||
BorderPane root = new BorderPane();
|
||||
root.setPickOnBounds(false);
|
||||
|
||||
|
@ -117,9 +117,11 @@ public class ComponentList extends Control {
|
||||
|
||||
protected static class Skin extends SkinBase<ComponentList> {
|
||||
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 ObjectBinding<Node> firstItem;
|
||||
private final ObjectBinding<Node> lastItem;
|
||||
|
||||
protected Skin(ComponentList control) {
|
||||
super(control);
|
||||
@ -140,6 +142,16 @@ public class ComponentList extends Control {
|
||||
if (!list.isEmpty())
|
||||
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();
|
||||
Bindings.bindContent(vbox.getChildren(), list);
|
||||
getChildren().setAll(vbox);
|
||||
|
@ -1,4 +1,4 @@
|
||||
/**
|
||||
/*
|
||||
* Hello Minecraft! Launcher
|
||||
* Copyright (C) 2020 huangyuhui <huanghongxun2008@126.com> and contributors
|
||||
*
|
||||
|
@ -17,7 +17,9 @@
|
||||
*/
|
||||
package org.jackhuang.hmcl.ui.construct;
|
||||
|
||||
import javafx.beans.property.BooleanProperty;
|
||||
import javafx.beans.property.ObjectProperty;
|
||||
import javafx.beans.property.SimpleBooleanProperty;
|
||||
import javafx.beans.property.SimpleObjectProperty;
|
||||
import javafx.event.Event;
|
||||
import javafx.event.EventHandler;
|
||||
@ -37,11 +39,13 @@ import java.util.logging.Level;
|
||||
public class Navigator extends TransitionPane {
|
||||
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 boolean initialized = false;
|
||||
|
||||
public void init(Node init) {
|
||||
stack.push(init);
|
||||
backable.set(canGoBack());
|
||||
getChildren().setAll(init);
|
||||
|
||||
fireEvent(new NavigationEvent(this, init, NavigationEvent.NAVIGATED));
|
||||
@ -62,6 +66,7 @@ public class Navigator extends TransitionPane {
|
||||
Logging.LOG.info("Navigate to " + node);
|
||||
|
||||
stack.push(node);
|
||||
backable.set(canGoBack());
|
||||
|
||||
NavigationEvent navigating = new NavigationEvent(this, from, NavigationEvent.NAVIGATING);
|
||||
fireEvent(navigating);
|
||||
@ -103,6 +108,7 @@ public class Navigator extends TransitionPane {
|
||||
Logging.LOG.info("Closed page " + from);
|
||||
|
||||
stack.pop();
|
||||
backable.set(canGoBack());
|
||||
Node node = stack.peek();
|
||||
|
||||
NavigationEvent navigating = new NavigationEvent(this, from, NavigationEvent.NAVIGATING);
|
||||
@ -131,6 +137,18 @@ public class Navigator extends TransitionPane {
|
||||
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() {
|
||||
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.application.Platform;
|
||||
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.ListChangeListener;
|
||||
import javafx.collections.ObservableList;
|
||||
import javafx.css.PseudoClass;
|
||||
import javafx.geometry.Insets;
|
||||
import javafx.geometry.Side;
|
||||
import javafx.scene.AccessibleAttribute;
|
||||
import javafx.scene.Node;
|
||||
import javafx.scene.control.*;
|
||||
import javafx.scene.input.MouseButton;
|
||||
@ -40,7 +39,7 @@ import javafx.util.Duration;
|
||||
import org.jackhuang.hmcl.ui.FXUtils;
|
||||
import org.jackhuang.hmcl.util.javafx.MappedObservableList;
|
||||
|
||||
public class TabHeader extends Control {
|
||||
public class TabHeader extends Control implements TabControl {
|
||||
|
||||
public TabHeader(Tab... tabs) {
|
||||
getStyleClass().setAll("tab-header");
|
||||
@ -51,11 +50,12 @@ public class TabHeader extends Control {
|
||||
|
||||
private ObservableList<Tab> tabs = FXCollections.observableArrayList();
|
||||
|
||||
@Override
|
||||
public ObservableList<Tab> getTabs() {
|
||||
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() {
|
||||
return selectionModel.get();
|
||||
@ -69,151 +69,6 @@ public class TabHeader extends Control {
|
||||
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
|
||||
protected Skin<?> createDefaultSkin() {
|
||||
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;
|
||||
|
||||
import javafx.beans.binding.Bindings;
|
||||
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.construct.Navigator;
|
||||
import org.jackhuang.hmcl.ui.wizard.Refreshable;
|
||||
|
||||
public abstract class DecoratorNavigatorPage extends DecoratorTransitionPage {
|
||||
protected final Navigator navigator = new Navigator();
|
||||
@ -31,6 +27,7 @@ public abstract class DecoratorNavigatorPage extends DecoratorTransitionPage {
|
||||
{
|
||||
this.navigator.setOnNavigating(this::onNavigating);
|
||||
this.navigator.setOnNavigated(this::onNavigated);
|
||||
backableProperty().bind(navigator.backableProperty());
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -50,39 +47,11 @@ public abstract class DecoratorNavigatorPage extends DecoratorTransitionPage {
|
||||
|
||||
private void onNavigating(Navigator.NavigationEvent event) {
|
||||
if (event.getSource() != this.navigator) return;
|
||||
Node from = event.getNode();
|
||||
|
||||
if (from instanceof DecoratorPage)
|
||||
((DecoratorPage) from).back();
|
||||
onNavigating(event.getNode());
|
||||
}
|
||||
|
||||
private void onNavigated(Navigator.NavigationEvent event) {
|
||||
if (event.getSource() != this.navigator) return;
|
||||
Node to = 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());
|
||||
}
|
||||
onNavigated(event.getNode());
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
import javafx.beans.binding.Bindings;
|
||||
import javafx.beans.property.BooleanProperty;
|
||||
import javafx.beans.property.ReadOnlyObjectProperty;
|
||||
import javafx.beans.property.ReadOnlyObjectWrapper;
|
||||
@ -24,6 +25,8 @@ import javafx.beans.property.SimpleBooleanProperty;
|
||||
import javafx.scene.Node;
|
||||
import javafx.scene.control.Control;
|
||||
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.TransitionPane;
|
||||
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 {
|
||||
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 Node currentPage;
|
||||
protected final TransitionPane transitionPane = new TransitionPane();
|
||||
|
||||
protected void navigate(Node page, AnimationProducer 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
|
||||
@ -48,6 +83,18 @@ public abstract class DecoratorTransitionPage extends Control implements Decorat
|
||||
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() {
|
||||
return refreshable.get();
|
||||
}
|
||||
|
@ -42,7 +42,7 @@ public class DecoratorWizardDisplayer extends DecoratorTransitionPage implements
|
||||
wizardController.setProvider(provider);
|
||||
wizardController.onStart();
|
||||
|
||||
addEventHandler(Navigator.NavigationEvent.NAVIGATING, this::onDecoratorPageNavigating);
|
||||
addEventHandler(Navigator.NavigationEvent.NAVIGATED, this::onDecoratorPageNavigating);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -77,6 +77,13 @@ public class DecoratorWizardDisplayer extends DecoratorTransitionPage implements
|
||||
else
|
||||
title = "";
|
||||
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
|
||||
|
@ -19,6 +19,7 @@ package org.jackhuang.hmcl.ui.main;
|
||||
|
||||
import javafx.application.Platform;
|
||||
import javafx.geometry.Insets;
|
||||
import javafx.scene.Node;
|
||||
import javafx.scene.control.SkinBase;
|
||||
import javafx.scene.layout.BorderPane;
|
||||
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.AccountList;
|
||||
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.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.profile.ProfileAdvancedListItem;
|
||||
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.util.i18n.I18n.i18n;
|
||||
|
||||
public class RootPage extends DecoratorNavigatorPage {
|
||||
public class RootPage extends DecoratorTabPage {
|
||||
private MainPage mainPage = null;
|
||||
private SettingsPage settingsPage = null;
|
||||
private GameList gameListPage = null;
|
||||
private AccountList accountListPage = 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() {
|
||||
EventBus.EVENT_BUS.channel(RefreshedVersionsEvent.class).register(event -> onRefreshedVersions((HMCLGameRepository) event.getSource()));
|
||||
|
||||
Profile profile = Profiles.getSelectedProfile();
|
||||
if (profile != null && profile.getRepository().isLoaded())
|
||||
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
|
||||
@ -156,7 +186,8 @@ public class RootPage extends DecoratorNavigatorPage {
|
||||
|
||||
// first item in left sidebar
|
||||
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());
|
||||
|
||||
// second item in left sidebar
|
||||
@ -166,7 +197,7 @@ public class RootPage extends DecoratorNavigatorPage {
|
||||
Profile profile = Profiles.getSelectedProfile();
|
||||
String version = Profiles.getSelectedVersion();
|
||||
if (version == null) {
|
||||
getSkinnable().navigate(getSkinnable().getGameListPage(), ContainerAnimations.FADE.getAnimationProducer());
|
||||
control.getSelectionModel().select(control.gameTab);
|
||||
} else {
|
||||
Versions.modifyGameSettings(profile, version);
|
||||
}
|
||||
@ -174,20 +205,23 @@ public class RootPage extends DecoratorNavigatorPage {
|
||||
|
||||
// third item in left sidebar
|
||||
AdvancedListItem gameItem = new AdvancedListItem();
|
||||
gameItem.activeProperty().bind(control.gameTab.selectedProperty());
|
||||
gameItem.setImage(newImage("/assets/img/bookshelf.png"));
|
||||
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
|
||||
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());
|
||||
|
||||
// fifth item in left sidebar
|
||||
AdvancedListItem launcherSettingsItem = new AdvancedListItem();
|
||||
launcherSettingsItem.activeProperty().bind(control.settingsTab.selectedProperty());
|
||||
launcherSettingsItem.setImage(newImage("/assets/img/command.png"));
|
||||
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
|
||||
AdvancedListBox sideBar = new AdvancedListBox()
|
||||
@ -217,10 +251,10 @@ public class RootPage extends DecoratorNavigatorPage {
|
||||
}
|
||||
|
||||
{
|
||||
control.navigator.getStyleClass().add("jfx-decorator-content-container");
|
||||
control.navigator.init(getSkinnable().getMainPage());
|
||||
FXUtils.setOverflowHidden(control.navigator, 8);
|
||||
StackPane wrapper = new StackPane(control.navigator);
|
||||
control.transitionPane.getStyleClass().add("jfx-decorator-content-container");
|
||||
control.transitionPane.getChildren().setAll(getSkinnable().getMainPage());
|
||||
FXUtils.setOverflowHidden(control.transitionPane, 8);
|
||||
StackPane wrapper = new StackPane(control.transitionPane);
|
||||
wrapper.setPadding(new Insets(4));
|
||||
root.setCenter(wrapper);
|
||||
}
|
||||
|
@ -73,7 +73,7 @@ public final class SettingsPage extends SettingsView implements DecoratorPage {
|
||||
|
||||
public SettingsPage() {
|
||||
FXUtils.smoothScrolling(scroll);
|
||||
addEventHandler(Navigator.NavigationEvent.NAVIGATING, this::onDecoratorPageNavigating);
|
||||
addEventHandler(Navigator.NavigationEvent.NAVIGATED, this::onDecoratorPageNavigating);
|
||||
|
||||
// ==== Download sources ====
|
||||
cboDownloadSource.getItems().setAll(DownloadProviders.providersById.keySet());
|
||||
|
@ -73,7 +73,7 @@ public class ProfileListItemSkin extends SkinBase<ProfileListItem> {
|
||||
right.getChildren().add(btnRemove);
|
||||
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);
|
||||
item.titleProperty().bind(skinnable.titleProperty());
|
||||
item.subtitleProperty().bind(skinnable.subtitleProperty());
|
||||
|
@ -57,7 +57,7 @@ public class GameList extends ListPageBase<GameListItem> implements DecoratorPag
|
||||
});
|
||||
|
||||
Profiles.registerVersionsListener(this::loadVersions);
|
||||
addEventHandler(Navigator.NavigationEvent.NAVIGATING, this::onDecoratorPageNavigating);
|
||||
addEventHandler(Navigator.NavigationEvent.NAVIGATED, this::onDecoratorPageNavigating);
|
||||
}
|
||||
|
||||
private void loadVersions(Profile profile) {
|
||||
|
@ -80,6 +80,11 @@ public class VersionPage extends Control implements DecoratorPage {
|
||||
loadVersion(newValue, profile);
|
||||
});
|
||||
|
||||
versionSettingsTab.setNode(versionSettingsPage);
|
||||
modListTab.setNode(modListPage);
|
||||
installerListTab.setNode(installerListPage);
|
||||
worldListTab.setNode(worldListPage);
|
||||
|
||||
addEventHandler(Navigator.NavigationEvent.NAVIGATED, this::onNavigated);
|
||||
}
|
||||
|
||||
@ -211,15 +216,7 @@ public class VersionPage extends Control implements DecoratorPage {
|
||||
control.worldListTab);
|
||||
control.selectedTab.bind(tabPane.getSelectionModel().selectedItemProperty());
|
||||
FXUtils.onChangeAndOperate(tabPane.getSelectionModel().selectedItemProperty(), newValue -> {
|
||||
if (control.versionSettingsTab.equals(newValue)) {
|
||||
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());
|
||||
}
|
||||
control.transitionPane.setContent(newValue.getNode(), ContainerAnimations.FADE.getAnimationProducer());
|
||||
});
|
||||
|
||||
HBox toolBar = new HBox();
|
||||
|
@ -105,7 +105,7 @@ public final class VersionSettingsPage extends StackPane implements DecoratorPag
|
||||
|
||||
public VersionSettingsPage() {
|
||||
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.setConverter(stringConverter(e -> i18n("settings.advanced.launcher_visibility." + e.name().toLowerCase())));
|
||||
|
@ -19,5 +19,6 @@
|
||||
-fx-base-color: #5c6bc0;
|
||||
-fx-base-darker-color: derive(-fx-base-color, -10%);
|
||||
-fx-base-check-color: derive(-fx-base-color, 30%);
|
||||
-fx-base-rippler-color: rgba(92, 107, 192, 0.3);
|
||||
-fx-base-text-fill: white;
|
||||
}
|
@ -19,5 +19,6 @@
|
||||
-fx-base-color: %base-color%;
|
||||
-fx-base-darker-color: derive(-fx-base-color, -10%);
|
||||
-fx-base-check-color: derive(-fx-base-color, 30%);
|
||||
-fx-base-rippler-color: %base-rippler-color%;
|
||||
-fx-base-text-fill: %font-color%;
|
||||
}
|
@ -71,6 +71,10 @@
|
||||
-fx-text-fill: -fx-base-text-fill;
|
||||
}
|
||||
|
||||
.advanced-list-item:selected .container {
|
||||
-fx-background-color: -fx-base-rippler-color;
|
||||
}
|
||||
|
||||
.notice-pane > .label {
|
||||
-fx-text-fill: #0079FF;
|
||||
-fx-font-size: 20;
|
||||
@ -692,6 +696,16 @@
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user