mirror of
https://github.com/HMCL-dev/HMCL.git
synced 2025-09-18 16:26:05 -04:00
alt: some UI details
This commit is contained in:
parent
5b1c72ad5c
commit
1726cc7ec7
@ -41,7 +41,6 @@ public abstract class ToolbarListPageSkin<T extends ListPageBase<? extends Node>
|
|||||||
|
|
||||||
SpinnerPane spinnerPane = new SpinnerPane();
|
SpinnerPane spinnerPane = new SpinnerPane();
|
||||||
spinnerPane.getStyleClass().add("large-spinner-pane");
|
spinnerPane.getStyleClass().add("large-spinner-pane");
|
||||||
spinnerPane.getStyleClass().add("content-background");
|
|
||||||
|
|
||||||
BorderPane root = new BorderPane();
|
BorderPane root = new BorderPane();
|
||||||
|
|
||||||
|
@ -19,6 +19,7 @@ package org.jackhuang.hmcl.ui.construct;
|
|||||||
|
|
||||||
import com.jfoenix.effects.JFXDepthManager;
|
import com.jfoenix.effects.JFXDepthManager;
|
||||||
import javafx.geometry.Insets;
|
import javafx.geometry.Insets;
|
||||||
|
import javafx.scene.Cursor;
|
||||||
import javafx.scene.control.ListCell;
|
import javafx.scene.control.ListCell;
|
||||||
import javafx.scene.layout.StackPane;
|
import javafx.scene.layout.StackPane;
|
||||||
|
|
||||||
@ -29,7 +30,9 @@ public abstract class FloatListCell<T> extends ListCell<T> {
|
|||||||
setText(null);
|
setText(null);
|
||||||
setGraphic(null);
|
setGraphic(null);
|
||||||
|
|
||||||
pane.setStyle("-fx-background-color: white; -fx-padding: 8; -fx-cursor: HAND");
|
pane.setStyle("-fx-background-color: white");
|
||||||
|
pane.setCursor(Cursor.HAND);
|
||||||
|
pane.setPadding(new Insets(8));
|
||||||
setPadding(new Insets(5));
|
setPadding(new Insets(5));
|
||||||
JFXDepthManager.setDepth(pane, 1);
|
JFXDepthManager.setDepth(pane, 1);
|
||||||
}
|
}
|
||||||
@ -37,13 +40,13 @@ public abstract class FloatListCell<T> extends ListCell<T> {
|
|||||||
@Override
|
@Override
|
||||||
protected void updateItem(T item, boolean empty) {
|
protected void updateItem(T item, boolean empty) {
|
||||||
super.updateItem(item, empty);
|
super.updateItem(item, empty);
|
||||||
|
updateControl(item, empty);
|
||||||
if (empty) {
|
if (empty) {
|
||||||
setGraphic(null);
|
setGraphic(null);
|
||||||
} else {
|
} else {
|
||||||
updateControl(item);
|
|
||||||
setGraphic(pane);
|
setGraphic(pane);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract void updateControl(T dataItem);
|
protected abstract void updateControl(T dataItem, boolean empty);
|
||||||
}
|
}
|
||||||
|
@ -1,135 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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 com.jfoenix.controls.JFXCheckBox;
|
|
||||||
import javafx.beans.binding.Bindings;
|
|
||||||
import javafx.beans.property.BooleanProperty;
|
|
||||||
import javafx.beans.property.ObjectProperty;
|
|
||||||
import javafx.beans.property.SimpleObjectProperty;
|
|
||||||
import javafx.beans.value.ObservableValue;
|
|
||||||
import javafx.geometry.Pos;
|
|
||||||
import javafx.scene.control.CheckBox;
|
|
||||||
import javafx.scene.control.TreeTableCell;
|
|
||||||
import javafx.scene.layout.StackPane;
|
|
||||||
import javafx.util.Callback;
|
|
||||||
import javafx.util.StringConverter;
|
|
||||||
|
|
||||||
public class JFXCheckBoxTreeTableCell<S,T> extends TreeTableCell<S,T> {
|
|
||||||
private final StackPane pane;
|
|
||||||
private final CheckBox checkBox;
|
|
||||||
private boolean showLabel;
|
|
||||||
private ObservableValue<Boolean> booleanProperty;
|
|
||||||
|
|
||||||
public JFXCheckBoxTreeTableCell() {
|
|
||||||
this(null, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
public JFXCheckBoxTreeTableCell(
|
|
||||||
final Callback<Integer, ObservableValue<Boolean>> getSelectedProperty) {
|
|
||||||
this(getSelectedProperty, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
public JFXCheckBoxTreeTableCell(
|
|
||||||
final Callback<Integer, ObservableValue<Boolean>> getSelectedProperty,
|
|
||||||
final StringConverter<T> converter) {
|
|
||||||
this.getStyleClass().add("check-box-tree-table-cell");
|
|
||||||
this.checkBox = new JFXCheckBox();
|
|
||||||
this.pane = new StackPane(checkBox);
|
|
||||||
this.pane.setAlignment(Pos.CENTER);
|
|
||||||
setGraphic(null);
|
|
||||||
setSelectedStateCallback(getSelectedProperty);
|
|
||||||
setConverter(converter);
|
|
||||||
}
|
|
||||||
|
|
||||||
private ObjectProperty<StringConverter<T>> converter =
|
|
||||||
new SimpleObjectProperty<StringConverter<T>>(this, "converter") {
|
|
||||||
protected void invalidated() {
|
|
||||||
updateShowLabel();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
public final ObjectProperty<StringConverter<T>> converterProperty() {
|
|
||||||
return converter;
|
|
||||||
}
|
|
||||||
public final void setConverter(StringConverter<T> value) {
|
|
||||||
converterProperty().set(value);
|
|
||||||
}
|
|
||||||
public final StringConverter<T> getConverter() {
|
|
||||||
return converterProperty().get();
|
|
||||||
}
|
|
||||||
|
|
||||||
private ObjectProperty<Callback<Integer, ObservableValue<Boolean>>>
|
|
||||||
selectedStateCallback = new SimpleObjectProperty<>(this, "selectedStateCallback");
|
|
||||||
|
|
||||||
public final ObjectProperty<Callback<Integer, ObservableValue<Boolean>>> selectedStateCallbackProperty() {
|
|
||||||
return selectedStateCallback;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final void setSelectedStateCallback(Callback<Integer, ObservableValue<Boolean>> value) {
|
|
||||||
selectedStateCallbackProperty().set(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public final Callback<Integer, ObservableValue<Boolean>> getSelectedStateCallback() {
|
|
||||||
return selectedStateCallbackProperty().get();
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
@Override public void updateItem(T item, boolean empty) {
|
|
||||||
super.updateItem(item, empty);
|
|
||||||
|
|
||||||
if (empty) {
|
|
||||||
setText(null);
|
|
||||||
setGraphic(null);
|
|
||||||
} else {
|
|
||||||
StringConverter<T> c = getConverter();
|
|
||||||
|
|
||||||
if (showLabel) {
|
|
||||||
setText(c.toString(item));
|
|
||||||
}
|
|
||||||
|
|
||||||
setGraphic(pane);
|
|
||||||
|
|
||||||
if (booleanProperty instanceof BooleanProperty) {
|
|
||||||
checkBox.selectedProperty().unbindBidirectional((BooleanProperty)booleanProperty);
|
|
||||||
}
|
|
||||||
ObservableValue<?> obsValue = getSelectedProperty();
|
|
||||||
if (obsValue instanceof BooleanProperty) {
|
|
||||||
booleanProperty = (ObservableValue<Boolean>) obsValue;
|
|
||||||
checkBox.selectedProperty().bindBidirectional((BooleanProperty)booleanProperty);
|
|
||||||
}
|
|
||||||
|
|
||||||
checkBox.disableProperty().bind(Bindings.not(
|
|
||||||
getTreeTableView().editableProperty().and(
|
|
||||||
getTableColumn().editableProperty()).and(
|
|
||||||
editableProperty())
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void updateShowLabel() {
|
|
||||||
this.showLabel = converter != null;
|
|
||||||
this.checkBox.setAlignment(showLabel ? Pos.CENTER_LEFT : Pos.CENTER);
|
|
||||||
}
|
|
||||||
|
|
||||||
private ObservableValue<?> getSelectedProperty() {
|
|
||||||
return getSelectedStateCallback() != null ?
|
|
||||||
getSelectedStateCallback().call(getIndex()) :
|
|
||||||
getTableColumn().getCellObservableValue(getIndex());
|
|
||||||
}
|
|
||||||
}
|
|
@ -123,7 +123,8 @@ public final class VersionsPage extends BorderPane implements WizardPage, Refres
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void updateControl(RemoteVersion remoteVersion) {
|
protected void updateControl(RemoteVersion remoteVersion, boolean empty) {
|
||||||
|
if (empty) return;
|
||||||
content.setTitle(remoteVersion.getSelfVersion());
|
content.setTitle(remoteVersion.getSelfVersion());
|
||||||
content.setSubtitle(remoteVersion.getGameVersion());
|
content.setSubtitle(remoteVersion.getGameVersion());
|
||||||
|
|
||||||
|
@ -21,7 +21,6 @@ import javafx.beans.property.SimpleStringProperty;
|
|||||||
import javafx.beans.property.StringProperty;
|
import javafx.beans.property.StringProperty;
|
||||||
import javafx.collections.ObservableList;
|
import javafx.collections.ObservableList;
|
||||||
import javafx.scene.control.Skin;
|
import javafx.scene.control.Skin;
|
||||||
import javafx.scene.control.TreeItem;
|
|
||||||
import javafx.stage.FileChooser;
|
import javafx.stage.FileChooser;
|
||||||
import org.jackhuang.hmcl.mod.Datapack;
|
import org.jackhuang.hmcl.mod.Datapack;
|
||||||
import org.jackhuang.hmcl.task.Schedulers;
|
import org.jackhuang.hmcl.task.Schedulers;
|
||||||
@ -103,9 +102,8 @@ public class DatapackListPage extends ListPageBase<DatapackListPageSkin.Datapack
|
|||||||
datapack.loadFromDir();
|
datapack.loadFromDir();
|
||||||
}
|
}
|
||||||
|
|
||||||
void removeSelected(ObservableList<TreeItem<DatapackListPageSkin.DatapackInfoObject>> selectedItems) {
|
void removeSelected(ObservableList<DatapackListPageSkin.DatapackInfoObject> selectedItems) {
|
||||||
selectedItems.stream()
|
selectedItems.stream()
|
||||||
.map(TreeItem::getValue)
|
|
||||||
.map(DatapackListPageSkin.DatapackInfoObject::getPackInfo)
|
.map(DatapackListPageSkin.DatapackInfoObject::getPackInfo)
|
||||||
.forEach(pack -> {
|
.forEach(pack -> {
|
||||||
try {
|
try {
|
||||||
@ -117,16 +115,14 @@ public class DatapackListPage extends ListPageBase<DatapackListPageSkin.Datapack
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void enableSelected(ObservableList<TreeItem<DatapackListPageSkin.DatapackInfoObject>> selectedItems) {
|
void enableSelected(ObservableList<DatapackListPageSkin.DatapackInfoObject> selectedItems) {
|
||||||
selectedItems.stream()
|
selectedItems.stream()
|
||||||
.map(TreeItem::getValue)
|
|
||||||
.map(DatapackListPageSkin.DatapackInfoObject::getPackInfo)
|
.map(DatapackListPageSkin.DatapackInfoObject::getPackInfo)
|
||||||
.forEach(info -> info.setActive(true));
|
.forEach(info -> info.setActive(true));
|
||||||
}
|
}
|
||||||
|
|
||||||
void disableSelected(ObservableList<TreeItem<DatapackListPageSkin.DatapackInfoObject>> selectedItems) {
|
void disableSelected(ObservableList<DatapackListPageSkin.DatapackInfoObject> selectedItems) {
|
||||||
selectedItems.stream()
|
selectedItems.stream()
|
||||||
.map(TreeItem::getValue)
|
|
||||||
.map(DatapackListPageSkin.DatapackInfoObject::getPackInfo)
|
.map(DatapackListPageSkin.DatapackInfoObject::getPackInfo)
|
||||||
.forEach(info -> info.setActive(false));
|
.forEach(info -> info.setActive(false));
|
||||||
}
|
}
|
||||||
|
@ -17,30 +17,27 @@
|
|||||||
*/
|
*/
|
||||||
package org.jackhuang.hmcl.ui.versions;
|
package org.jackhuang.hmcl.ui.versions;
|
||||||
|
|
||||||
import com.jfoenix.controls.JFXTreeTableColumn;
|
import com.jfoenix.controls.JFXCheckBox;
|
||||||
import com.jfoenix.controls.JFXTreeTableView;
|
import com.jfoenix.controls.JFXListView;
|
||||||
import com.jfoenix.controls.RecursiveTreeItem;
|
|
||||||
import com.jfoenix.controls.datamodels.treetable.RecursiveTreeObject;
|
import com.jfoenix.controls.datamodels.treetable.RecursiveTreeObject;
|
||||||
import com.jfoenix.effects.JFXDepthManager;
|
import com.jfoenix.effects.JFXDepthManager;
|
||||||
|
import javafx.beans.binding.Bindings;
|
||||||
import javafx.beans.property.BooleanProperty;
|
import javafx.beans.property.BooleanProperty;
|
||||||
import javafx.beans.property.ObjectProperty;
|
|
||||||
import javafx.beans.property.SimpleObjectProperty;
|
|
||||||
import javafx.geometry.Insets;
|
import javafx.geometry.Insets;
|
||||||
import javafx.scene.Node;
|
import javafx.geometry.Pos;
|
||||||
import javafx.scene.control.SelectionMode;
|
import javafx.scene.control.SelectionMode;
|
||||||
import javafx.scene.control.SkinBase;
|
import javafx.scene.control.SkinBase;
|
||||||
import javafx.scene.layout.BorderPane;
|
import javafx.scene.layout.BorderPane;
|
||||||
import javafx.scene.layout.HBox;
|
import javafx.scene.layout.HBox;
|
||||||
|
import javafx.scene.layout.Region;
|
||||||
import org.jackhuang.hmcl.mod.Datapack;
|
import org.jackhuang.hmcl.mod.Datapack;
|
||||||
import org.jackhuang.hmcl.ui.Controllers;
|
import org.jackhuang.hmcl.ui.Controllers;
|
||||||
import org.jackhuang.hmcl.ui.SVG;
|
import org.jackhuang.hmcl.ui.SVG;
|
||||||
import org.jackhuang.hmcl.ui.construct.JFXCheckBoxTreeTableCell;
|
import org.jackhuang.hmcl.ui.construct.FloatListCell;
|
||||||
import org.jackhuang.hmcl.ui.construct.SpinnerPane;
|
import org.jackhuang.hmcl.ui.construct.SpinnerPane;
|
||||||
import org.jackhuang.hmcl.ui.construct.TwoLineListItem;
|
import org.jackhuang.hmcl.ui.construct.TwoLineListItem;
|
||||||
import org.jackhuang.hmcl.util.StringUtils;
|
import org.jackhuang.hmcl.util.StringUtils;
|
||||||
|
|
||||||
import static org.jackhuang.hmcl.ui.FXUtils.setupCellValueFactory;
|
|
||||||
import static org.jackhuang.hmcl.ui.FXUtils.wrapMargin;
|
|
||||||
import static org.jackhuang.hmcl.ui.ToolbarListPageSkin.createToolbarButton;
|
import static org.jackhuang.hmcl.ui.ToolbarListPageSkin.createToolbarButton;
|
||||||
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
|
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
|
||||||
|
|
||||||
@ -50,7 +47,7 @@ class DatapackListPageSkin extends SkinBase<DatapackListPage> {
|
|||||||
super(skinnable);
|
super(skinnable);
|
||||||
|
|
||||||
BorderPane root = new BorderPane();
|
BorderPane root = new BorderPane();
|
||||||
JFXTreeTableView<DatapackInfoObject> tableView = new JFXTreeTableView<>();
|
JFXListView<DatapackInfoObject> listView = new JFXListView<>();
|
||||||
|
|
||||||
{
|
{
|
||||||
HBox toolbar = new HBox();
|
HBox toolbar = new HBox();
|
||||||
@ -62,13 +59,13 @@ class DatapackListPageSkin extends SkinBase<DatapackListPage> {
|
|||||||
toolbar.getChildren().add(createToolbarButton(i18n("datapack.add"), SVG::plus, skinnable::add));
|
toolbar.getChildren().add(createToolbarButton(i18n("datapack.add"), SVG::plus, skinnable::add));
|
||||||
toolbar.getChildren().add(createToolbarButton(i18n("button.remove"), SVG::delete, () -> {
|
toolbar.getChildren().add(createToolbarButton(i18n("button.remove"), SVG::delete, () -> {
|
||||||
Controllers.confirmDialog(i18n("button.remove.confirm"), i18n("button.remove"), () -> {
|
Controllers.confirmDialog(i18n("button.remove.confirm"), i18n("button.remove"), () -> {
|
||||||
skinnable.removeSelected(tableView.getSelectionModel().getSelectedItems());
|
skinnable.removeSelected(listView.getSelectionModel().getSelectedItems());
|
||||||
}, null);
|
}, null);
|
||||||
}));
|
}));
|
||||||
toolbar.getChildren().add(createToolbarButton(i18n("mods.enable"), SVG::check, () ->
|
toolbar.getChildren().add(createToolbarButton(i18n("mods.enable"), SVG::check, () ->
|
||||||
skinnable.enableSelected(tableView.getSelectionModel().getSelectedItems())));
|
skinnable.enableSelected(listView.getSelectionModel().getSelectedItems())));
|
||||||
toolbar.getChildren().add(createToolbarButton(i18n("mods.disable"), SVG::close, () ->
|
toolbar.getChildren().add(createToolbarButton(i18n("mods.disable"), SVG::close, () ->
|
||||||
skinnable.disableSelected(tableView.getSelectionModel().getSelectedItems())));
|
skinnable.disableSelected(listView.getSelectionModel().getSelectedItems())));
|
||||||
root.setTop(toolbar);
|
root.setTop(toolbar);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -77,26 +74,43 @@ class DatapackListPageSkin extends SkinBase<DatapackListPage> {
|
|||||||
center.getStyleClass().add("large-spinner-pane");
|
center.getStyleClass().add("large-spinner-pane");
|
||||||
center.loadingProperty().bind(skinnable.loadingProperty());
|
center.loadingProperty().bind(skinnable.loadingProperty());
|
||||||
|
|
||||||
tableView.getStyleClass().addAll("no-header");
|
listView.setCellFactory(x -> new FloatListCell<DatapackInfoObject>() {
|
||||||
tableView.setShowRoot(false);
|
JFXCheckBox checkBox = new JFXCheckBox();
|
||||||
tableView.setEditable(true);
|
TwoLineListItem content = new TwoLineListItem();
|
||||||
tableView.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
|
BooleanProperty booleanProperty;
|
||||||
tableView.setRoot(new RecursiveTreeItem<>(skinnable.getItems(), RecursiveTreeObject::getChildren));
|
|
||||||
|
|
||||||
JFXTreeTableColumn<DatapackInfoObject, Boolean> activeColumn = new JFXTreeTableColumn<>();
|
{
|
||||||
setupCellValueFactory(activeColumn, DatapackInfoObject::activeProperty);
|
Region clippedContainer = (Region)listView.lookup(".clipped-container");
|
||||||
activeColumn.setCellFactory(c -> new JFXCheckBoxTreeTableCell<>());
|
setPrefWidth(0);
|
||||||
activeColumn.setEditable(true);
|
HBox container = new HBox(8);
|
||||||
activeColumn.setMaxWidth(40);
|
container.setPadding(new Insets(0, 0, 0, 6));
|
||||||
activeColumn.setMinWidth(40);
|
container.setAlignment(Pos.CENTER_LEFT);
|
||||||
|
pane.getChildren().add(container);
|
||||||
|
pane.setPadding(new Insets(8, 8, 8, 0));
|
||||||
|
if (clippedContainer != null) {
|
||||||
|
maxWidthProperty().bind(clippedContainer.widthProperty());
|
||||||
|
prefWidthProperty().bind(clippedContainer.widthProperty());
|
||||||
|
minWidthProperty().bind(clippedContainer.widthProperty());
|
||||||
|
}
|
||||||
|
|
||||||
JFXTreeTableColumn<DatapackInfoObject, Node> detailColumn = new JFXTreeTableColumn<>();
|
container.getChildren().setAll(checkBox, content);
|
||||||
setupCellValueFactory(detailColumn, DatapackInfoObject::nodeProperty);
|
}
|
||||||
|
|
||||||
tableView.getColumns().setAll(activeColumn, detailColumn);
|
@Override
|
||||||
|
protected void updateControl(DatapackInfoObject dataItem, boolean empty) {
|
||||||
|
if (empty) return;
|
||||||
|
content.setTitle(dataItem.getTitle());
|
||||||
|
content.setSubtitle(dataItem.getSubtitle());
|
||||||
|
if (booleanProperty != null) {
|
||||||
|
checkBox.selectedProperty().unbindBidirectional(booleanProperty);
|
||||||
|
}
|
||||||
|
checkBox.selectedProperty().bindBidirectional(booleanProperty = dataItem.active);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
listView.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
|
||||||
|
Bindings.bindContent(listView.getItems(), skinnable.getItems());
|
||||||
|
|
||||||
tableView.setColumnResizePolicy(JFXTreeTableView.CONSTRAINED_RESIZE_POLICY);
|
center.setContent(listView);
|
||||||
center.setContent(tableView);
|
|
||||||
root.setCenter(center);
|
root.setCenter(center);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -106,21 +120,18 @@ class DatapackListPageSkin extends SkinBase<DatapackListPage> {
|
|||||||
static class DatapackInfoObject extends RecursiveTreeObject<DatapackInfoObject> {
|
static class DatapackInfoObject extends RecursiveTreeObject<DatapackInfoObject> {
|
||||||
private final BooleanProperty active;
|
private final BooleanProperty active;
|
||||||
private final Datapack.Pack packInfo;
|
private final Datapack.Pack packInfo;
|
||||||
private final ObjectProperty<Node> node;
|
|
||||||
|
|
||||||
DatapackInfoObject(Datapack.Pack packInfo) {
|
DatapackInfoObject(Datapack.Pack packInfo) {
|
||||||
this.packInfo = packInfo;
|
this.packInfo = packInfo;
|
||||||
this.active = packInfo.activeProperty();
|
this.active = packInfo.activeProperty();
|
||||||
this.node = new SimpleObjectProperty<>(wrapMargin(new TwoLineListItem(packInfo.getId(), StringUtils.parseColorEscapes(packInfo.getDescription())),
|
|
||||||
new Insets(8, 0, 8, 0)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
BooleanProperty activeProperty() {
|
String getTitle() {
|
||||||
return active;
|
return packInfo.getId();
|
||||||
}
|
}
|
||||||
|
|
||||||
ObjectProperty<Node> nodeProperty() {
|
String getSubtitle() {
|
||||||
return node;
|
return StringUtils.parseColorEscapes(packInfo.getDescription());
|
||||||
}
|
}
|
||||||
|
|
||||||
Datapack.Pack getPackInfo() {
|
Datapack.Pack getPackInfo() {
|
||||||
|
@ -29,6 +29,7 @@ import org.jackhuang.hmcl.game.Version;
|
|||||||
import org.jackhuang.hmcl.setting.Profile;
|
import org.jackhuang.hmcl.setting.Profile;
|
||||||
import org.jackhuang.hmcl.setting.Profiles;
|
import org.jackhuang.hmcl.setting.Profiles;
|
||||||
import org.jackhuang.hmcl.ui.*;
|
import org.jackhuang.hmcl.ui.*;
|
||||||
|
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.ui.download.ModpackInstallWizardProvider;
|
import org.jackhuang.hmcl.ui.download.ModpackInstallWizardProvider;
|
||||||
import org.jackhuang.hmcl.ui.download.VanillaInstallWizardProvider;
|
import org.jackhuang.hmcl.ui.download.VanillaInstallWizardProvider;
|
||||||
@ -56,6 +57,7 @@ public class GameList extends ListPageBase<GameListItem> implements DecoratorPag
|
|||||||
});
|
});
|
||||||
|
|
||||||
Profiles.registerVersionsListener(this::loadVersions);
|
Profiles.registerVersionsListener(this::loadVersions);
|
||||||
|
addEventHandler(Navigator.NavigationEvent.NAVIGATING, this::onDecoratorPageNavigating);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void loadVersions(Profile profile) {
|
private void loadVersions(Profile profile) {
|
||||||
|
@ -22,7 +22,6 @@ import javafx.beans.property.BooleanProperty;
|
|||||||
import javafx.beans.property.SimpleBooleanProperty;
|
import javafx.beans.property.SimpleBooleanProperty;
|
||||||
import javafx.collections.ObservableList;
|
import javafx.collections.ObservableList;
|
||||||
import javafx.scene.control.Skin;
|
import javafx.scene.control.Skin;
|
||||||
import javafx.scene.control.TreeItem;
|
|
||||||
import javafx.stage.FileChooser;
|
import javafx.stage.FileChooser;
|
||||||
import org.jackhuang.hmcl.download.LibraryAnalyzer;
|
import org.jackhuang.hmcl.download.LibraryAnalyzer;
|
||||||
import org.jackhuang.hmcl.mod.ModInfo;
|
import org.jackhuang.hmcl.mod.ModInfo;
|
||||||
@ -139,11 +138,9 @@ public final class ModListPage extends ListPageBase<ModListPageSkin.ModInfoObjec
|
|||||||
this.parentTab = parentTab;
|
this.parentTab = parentTab;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void removeSelected(ObservableList<TreeItem<ModListPageSkin.ModInfoObject>> selectedItems) {
|
public void removeSelected(ObservableList<ModListPageSkin.ModInfoObject> selectedItems) {
|
||||||
try {
|
try {
|
||||||
modManager.removeMods(selectedItems.stream()
|
modManager.removeMods(selectedItems.stream()
|
||||||
.filter(Objects::nonNull)
|
|
||||||
.map(TreeItem::getValue)
|
|
||||||
.filter(Objects::nonNull)
|
.filter(Objects::nonNull)
|
||||||
.map(ModListPageSkin.ModInfoObject::getModInfo)
|
.map(ModListPageSkin.ModInfoObject::getModInfo)
|
||||||
.toArray(ModInfo[]::new));
|
.toArray(ModInfo[]::new));
|
||||||
@ -153,16 +150,16 @@ public final class ModListPage extends ListPageBase<ModListPageSkin.ModInfoObjec
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void enableSelected(ObservableList<TreeItem<ModListPageSkin.ModInfoObject>> selectedItems) {
|
public void enableSelected(ObservableList<ModListPageSkin.ModInfoObject> selectedItems) {
|
||||||
selectedItems.stream()
|
selectedItems.stream()
|
||||||
.map(TreeItem::getValue)
|
.filter(Objects::nonNull)
|
||||||
.map(ModListPageSkin.ModInfoObject::getModInfo)
|
.map(ModListPageSkin.ModInfoObject::getModInfo)
|
||||||
.forEach(info -> info.setActive(true));
|
.forEach(info -> info.setActive(true));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void disableSelected(ObservableList<TreeItem<ModListPageSkin.ModInfoObject>> selectedItems) {
|
public void disableSelected(ObservableList<ModListPageSkin.ModInfoObject> selectedItems) {
|
||||||
selectedItems.stream()
|
selectedItems.stream()
|
||||||
.map(TreeItem::getValue)
|
.filter(Objects::nonNull)
|
||||||
.map(ModListPageSkin.ModInfoObject::getModInfo)
|
.map(ModListPageSkin.ModInfoObject::getModInfo)
|
||||||
.forEach(info -> info.setActive(false));
|
.forEach(info -> info.setActive(false));
|
||||||
}
|
}
|
||||||
|
@ -17,32 +17,29 @@
|
|||||||
*/
|
*/
|
||||||
package org.jackhuang.hmcl.ui.versions;
|
package org.jackhuang.hmcl.ui.versions;
|
||||||
|
|
||||||
import com.jfoenix.controls.JFXTreeTableColumn;
|
import com.jfoenix.controls.JFXCheckBox;
|
||||||
import com.jfoenix.controls.JFXTreeTableView;
|
import com.jfoenix.controls.JFXListView;
|
||||||
import com.jfoenix.controls.RecursiveTreeItem;
|
|
||||||
import com.jfoenix.controls.datamodels.treetable.RecursiveTreeObject;
|
import com.jfoenix.controls.datamodels.treetable.RecursiveTreeObject;
|
||||||
import com.jfoenix.effects.JFXDepthManager;
|
import com.jfoenix.effects.JFXDepthManager;
|
||||||
|
import javafx.beans.binding.Bindings;
|
||||||
import javafx.beans.property.BooleanProperty;
|
import javafx.beans.property.BooleanProperty;
|
||||||
import javafx.beans.property.ObjectProperty;
|
|
||||||
import javafx.beans.property.SimpleObjectProperty;
|
|
||||||
import javafx.geometry.Insets;
|
import javafx.geometry.Insets;
|
||||||
import javafx.scene.Node;
|
import javafx.geometry.Pos;
|
||||||
import javafx.scene.control.Label;
|
import javafx.scene.control.Label;
|
||||||
import javafx.scene.control.SelectionMode;
|
import javafx.scene.control.SelectionMode;
|
||||||
import javafx.scene.control.SkinBase;
|
import javafx.scene.control.SkinBase;
|
||||||
import javafx.scene.layout.BorderPane;
|
import javafx.scene.layout.BorderPane;
|
||||||
import javafx.scene.layout.HBox;
|
import javafx.scene.layout.HBox;
|
||||||
|
import javafx.scene.layout.Region;
|
||||||
import javafx.scene.layout.StackPane;
|
import javafx.scene.layout.StackPane;
|
||||||
import org.jackhuang.hmcl.mod.ModInfo;
|
import org.jackhuang.hmcl.mod.ModInfo;
|
||||||
import org.jackhuang.hmcl.ui.Controllers;
|
import org.jackhuang.hmcl.ui.Controllers;
|
||||||
import org.jackhuang.hmcl.ui.FXUtils;
|
import org.jackhuang.hmcl.ui.FXUtils;
|
||||||
import org.jackhuang.hmcl.ui.SVG;
|
import org.jackhuang.hmcl.ui.SVG;
|
||||||
import org.jackhuang.hmcl.ui.construct.JFXCheckBoxTreeTableCell;
|
import org.jackhuang.hmcl.ui.construct.FloatListCell;
|
||||||
import org.jackhuang.hmcl.ui.construct.SpinnerPane;
|
import org.jackhuang.hmcl.ui.construct.SpinnerPane;
|
||||||
import org.jackhuang.hmcl.ui.construct.TwoLineListItem;
|
import org.jackhuang.hmcl.ui.construct.TwoLineListItem;
|
||||||
|
|
||||||
import static org.jackhuang.hmcl.ui.FXUtils.setupCellValueFactory;
|
|
||||||
import static org.jackhuang.hmcl.ui.FXUtils.wrapMargin;
|
|
||||||
import static org.jackhuang.hmcl.ui.ToolbarListPageSkin.createToolbarButton;
|
import static org.jackhuang.hmcl.ui.ToolbarListPageSkin.createToolbarButton;
|
||||||
import static org.jackhuang.hmcl.util.StringUtils.isNotBlank;
|
import static org.jackhuang.hmcl.util.StringUtils.isNotBlank;
|
||||||
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
|
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
|
||||||
@ -56,7 +53,7 @@ class ModListPageSkin extends SkinBase<ModListPage> {
|
|||||||
pane.getStyleClass().addAll("notice-pane");
|
pane.getStyleClass().addAll("notice-pane");
|
||||||
|
|
||||||
BorderPane root = new BorderPane();
|
BorderPane root = new BorderPane();
|
||||||
JFXTreeTableView<ModInfoObject> tableView = new JFXTreeTableView<>();
|
JFXListView<ModInfoObject> listView = new JFXListView<>();
|
||||||
|
|
||||||
{
|
{
|
||||||
HBox toolbar = new HBox();
|
HBox toolbar = new HBox();
|
||||||
@ -68,13 +65,13 @@ class ModListPageSkin extends SkinBase<ModListPage> {
|
|||||||
toolbar.getChildren().add(createToolbarButton(i18n("mods.add"), SVG::plus, skinnable::add));
|
toolbar.getChildren().add(createToolbarButton(i18n("mods.add"), SVG::plus, skinnable::add));
|
||||||
toolbar.getChildren().add(createToolbarButton(i18n("button.remove"), SVG::delete, () -> {
|
toolbar.getChildren().add(createToolbarButton(i18n("button.remove"), SVG::delete, () -> {
|
||||||
Controllers.confirmDialog(i18n("button.remove.confirm"), i18n("button.remove"), () -> {
|
Controllers.confirmDialog(i18n("button.remove.confirm"), i18n("button.remove"), () -> {
|
||||||
skinnable.removeSelected(tableView.getSelectionModel().getSelectedItems());
|
skinnable.removeSelected(listView.getSelectionModel().getSelectedItems());
|
||||||
}, null);
|
}, null);
|
||||||
}));
|
}));
|
||||||
toolbar.getChildren().add(createToolbarButton(i18n("mods.enable"), SVG::check, () ->
|
toolbar.getChildren().add(createToolbarButton(i18n("mods.enable"), SVG::check, () ->
|
||||||
skinnable.enableSelected(tableView.getSelectionModel().getSelectedItems())));
|
skinnable.enableSelected(listView.getSelectionModel().getSelectedItems())));
|
||||||
toolbar.getChildren().add(createToolbarButton(i18n("mods.disable"), SVG::close, () ->
|
toolbar.getChildren().add(createToolbarButton(i18n("mods.disable"), SVG::close, () ->
|
||||||
skinnable.disableSelected(tableView.getSelectionModel().getSelectedItems())));
|
skinnable.disableSelected(listView.getSelectionModel().getSelectedItems())));
|
||||||
root.setTop(toolbar);
|
root.setTop(toolbar);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -83,26 +80,43 @@ class ModListPageSkin extends SkinBase<ModListPage> {
|
|||||||
center.getStyleClass().add("large-spinner-pane");
|
center.getStyleClass().add("large-spinner-pane");
|
||||||
center.loadingProperty().bind(skinnable.loadingProperty());
|
center.loadingProperty().bind(skinnable.loadingProperty());
|
||||||
|
|
||||||
tableView.getStyleClass().add("no-header");
|
listView.setCellFactory(x -> new FloatListCell<ModInfoObject>() {
|
||||||
tableView.setShowRoot(false);
|
JFXCheckBox checkBox = new JFXCheckBox();
|
||||||
tableView.setEditable(true);
|
TwoLineListItem content = new TwoLineListItem();
|
||||||
tableView.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
|
BooleanProperty booleanProperty;
|
||||||
tableView.setRoot(new RecursiveTreeItem<>(skinnable.getItems(), RecursiveTreeObject::getChildren));
|
|
||||||
|
|
||||||
JFXTreeTableColumn<ModInfoObject, Boolean> activeColumn = new JFXTreeTableColumn<>();
|
{
|
||||||
setupCellValueFactory(activeColumn, ModInfoObject::activeProperty);
|
Region clippedContainer = (Region)listView.lookup(".clipped-container");
|
||||||
activeColumn.setCellFactory(c -> new JFXCheckBoxTreeTableCell<>());
|
setPrefWidth(0);
|
||||||
activeColumn.setEditable(true);
|
HBox container = new HBox(8);
|
||||||
activeColumn.setMaxWidth(40);
|
container.setPadding(new Insets(0, 0, 0, 6));
|
||||||
activeColumn.setMinWidth(40);
|
container.setAlignment(Pos.CENTER_LEFT);
|
||||||
|
pane.getChildren().add(container);
|
||||||
|
pane.setPadding(new Insets(8, 8, 8, 0));
|
||||||
|
if (clippedContainer != null) {
|
||||||
|
maxWidthProperty().bind(clippedContainer.widthProperty());
|
||||||
|
prefWidthProperty().bind(clippedContainer.widthProperty());
|
||||||
|
minWidthProperty().bind(clippedContainer.widthProperty());
|
||||||
|
}
|
||||||
|
|
||||||
JFXTreeTableColumn<ModInfoObject, Node> detailColumn = new JFXTreeTableColumn<>();
|
container.getChildren().setAll(checkBox, content);
|
||||||
setupCellValueFactory(detailColumn, ModInfoObject::nodeProperty);
|
}
|
||||||
|
|
||||||
tableView.getColumns().setAll(activeColumn, detailColumn);
|
@Override
|
||||||
|
protected void updateControl(ModInfoObject dataItem, boolean empty) {
|
||||||
|
if (empty) return;
|
||||||
|
content.setTitle(dataItem.getTitle());
|
||||||
|
content.setSubtitle(dataItem.getSubtitle());
|
||||||
|
if (booleanProperty != null) {
|
||||||
|
checkBox.selectedProperty().unbindBidirectional(booleanProperty);
|
||||||
|
}
|
||||||
|
checkBox.selectedProperty().bindBidirectional(booleanProperty = dataItem.active);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
listView.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
|
||||||
|
Bindings.bindContent(listView.getItems(), skinnable.getItems());
|
||||||
|
|
||||||
tableView.setColumnResizePolicy(JFXTreeTableView.CONSTRAINED_RESIZE_POLICY);
|
center.setContent(listView);
|
||||||
center.setContent(tableView);
|
|
||||||
root.setCenter(center);
|
root.setCenter(center);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -120,7 +134,7 @@ class ModListPageSkin extends SkinBase<ModListPage> {
|
|||||||
static class ModInfoObject extends RecursiveTreeObject<ModInfoObject> {
|
static class ModInfoObject extends RecursiveTreeObject<ModInfoObject> {
|
||||||
private final BooleanProperty active;
|
private final BooleanProperty active;
|
||||||
private final ModInfo modInfo;
|
private final ModInfo modInfo;
|
||||||
private final ObjectProperty<Node> node;
|
private final String message;
|
||||||
|
|
||||||
ModInfoObject(ModInfo modInfo) {
|
ModInfoObject(ModInfo modInfo) {
|
||||||
this.modInfo = modInfo;
|
this.modInfo = modInfo;
|
||||||
@ -132,15 +146,15 @@ class ModListPageSkin extends SkinBase<ModListPage> {
|
|||||||
message.append(", ").append(i18n("archive.game_version")).append(": ").append(modInfo.getGameVersion());
|
message.append(", ").append(i18n("archive.game_version")).append(": ").append(modInfo.getGameVersion());
|
||||||
if (isNotBlank(modInfo.getAuthors()))
|
if (isNotBlank(modInfo.getAuthors()))
|
||||||
message.append(", ").append(i18n("archive.author")).append(": ").append(modInfo.getAuthors());
|
message.append(", ").append(i18n("archive.author")).append(": ").append(modInfo.getAuthors());
|
||||||
this.node = new SimpleObjectProperty<>(wrapMargin(new TwoLineListItem(modInfo.getFileName(), message.toString()), new Insets(8, 0, 8, 0)));
|
this.message = message.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
BooleanProperty activeProperty() {
|
String getTitle() {
|
||||||
return active;
|
return modInfo.getFileName();
|
||||||
}
|
}
|
||||||
|
|
||||||
ObjectProperty<Node> nodeProperty() {
|
String getSubtitle() {
|
||||||
return node;
|
return message;
|
||||||
}
|
}
|
||||||
|
|
||||||
ModInfo getModInfo() {
|
ModInfo getModInfo() {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user