Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions HMCL/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@ val addOpens = listOf(
"javafx.controls/com.sun.javafx.scene.control",
"javafx.controls/com.sun.javafx.scene.control.behavior",
"javafx.graphics/com.sun.javafx.tk.quantum",
"javafx.controls/javafx.scene.control",
"javafx.controls/javafx.scene.control.skin",
"jdk.attach/sun.tools.attach",
)
Expand Down
713 changes: 713 additions & 0 deletions HMCL/src/main/java/com/jfoenix/skins/JFXDatePickerContent.java

Large diffs are not rendered by default.

177 changes: 177 additions & 0 deletions HMCL/src/main/java/com/jfoenix/skins/JFXDatePickerSkin.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package com.jfoenix.skins;

import com.jfoenix.adapters.ReflectionHelper;
import com.jfoenix.adapters.skins.DatePickerSkin;
import com.jfoenix.controls.JFXDatePicker;
import com.jfoenix.controls.JFXDialog;
import com.jfoenix.controls.JFXDialog.DialogTransition;
import javafx.beans.value.ChangeListener;
import javafx.event.ActionEvent;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Node;
import javafx.scene.control.TextField;
import javafx.scene.layout.*;
import javafx.scene.paint.Color;
import org.jackhuang.hmcl.ui.SVG;
import org.jackhuang.hmcl.ui.construct.DialogCloseEvent;
import org.jackhuang.hmcl.ui.construct.RipplerContainer;

import java.time.LocalDate;
import java.time.YearMonth;
import java.util.Stack;

import static org.jackhuang.hmcl.util.logging.Logger.LOG;

public class JFXDatePickerSkin extends DatePickerSkin {
public static final String PROPERTY_DATE_PICKER_CONTENTS = JFXDatePicker.class.getName() + ".dialog.contents";

private final JFXDatePicker jfxDatePicker;
private TextField displayNode;
private JFXDatePickerContent content;
private JFXDialog dialog;

public JFXDatePickerSkin(JFXDatePicker datePicker) {
super(datePicker);
this.jfxDatePicker = datePicker;
super.getPopupContent();

try {
Object expressionHelper = ReflectionHelper.getFieldContent(datePicker.focusedProperty().getClass().getSuperclass(), datePicker.focusedProperty(), "helper");
ChangeListener<Boolean>[] changeListeners = ReflectionHelper.getFieldContent(expressionHelper, "changeListeners");

int i = changeListeners.length - 1;
while (changeListeners[i] == null) {
--i;
}

datePicker.focusedProperty().removeListener(changeListeners[i]);
} catch (NullPointerException e) {
LOG.warning("Cannot remove focusedProperty listener", e);
}

datePicker.focusedProperty().addListener((obj, oldVal, newVal) -> {
if (this.getEditor() != null && !newVal) {
this.setTextFromTextFieldIntoComboBoxValue2();
}

});
var arrowBox = new HBox(new RipplerContainer(SVG.EDIT_CALENDAR.createIcon(30)));
arrowBox.setAlignment(Pos.CENTER);
this.setArrow(arrowBox);
this.getArrowButton().getChildren().setAll(arrowBox);
this.getArrowButton().setBackground(new Background(new BackgroundFill(Color.TRANSPARENT, CornerRadii.EMPTY, Insets.EMPTY)));
this.getArrowButton().setPadding(new Insets(1.0F, 8.0F, 1.0F, 8.0F));
this.registerChangeListener2(datePicker.converterProperty(), "CONVERTER", this::updateDisplayNode2);
this.registerChangeListener2(datePicker.dayCellFactoryProperty(), "DAY_CELL_FACTORY", () -> {
this.updateDisplayNode2();
this.content = null;
this.setPopup2(null);
});
this.registerChangeListener2(datePicker.editorProperty(), "EDITOR", this::getEditableInputNode2);
this.registerChangeListener2(datePicker.showingProperty(), "SHOWING", () -> {
if (this.jfxDatePicker.isShowing()) {
if (this.content != null) {
LocalDate date = this.jfxDatePicker.getValue();
this.content.displayedYearMonthProperty().set(date != null ? YearMonth.from(date) : YearMonth.now());
this.content.updateValues();
}

this.show();
} else {
this.hide();
}

});
this.registerChangeListener2(datePicker.showWeekNumbersProperty(), "SHOW_WEEK_NUMBERS", () -> {
if (this.content != null) {
this.content.updateContentGrid();
this.content.updateWeekNumberDateCells();
}

});
this.registerChangeListener2(datePicker.valueProperty(), "VALUE", () -> {
this.updateDisplayNode2();
this.jfxDatePicker.fireEvent(new ActionEvent());
});
}

public JFXDatePickerContent getPopupContent() {
if (this.content == null) {
this.content = new JFXDatePickerContent(this.jfxDatePicker);
}

return this.content;
}

public void show() {
if (!((JFXDatePicker) this.getSkinnable()).isOverLay()) {
super.show();
}

if (this.content != null) {
this.content.init();
this.content.clearFocus();
}

if (((JFXDatePicker) this.getSkinnable()).isOverLay() && this.dialog == null) {
StackPane dialogParent = this.jfxDatePicker.getDialogParent();
if (dialogParent == null) {
dialogParent = (StackPane) this.getSkinnable().getScene().getRoot();
}

this.dialog = new JFXDialog(dialogParent, this.getPopupContent(), DialogTransition.CENTER, true) {
@Override
public void close() {
super.close();
Object o = getDialogContainer().getProperties().get(PROPERTY_DATE_PICKER_CONTENTS);
if (o instanceof Stack<?> stack) {
stack.pop();
}
}
};
this.getPopupContent().addEventHandler(DialogCloseEvent.CLOSE, e -> this.dialog.close());
getArrowButton().setOnMouseClicked((click) -> {
if (((JFXDatePicker) this.getSkinnable()).isOverLay()) {
StackPane parent = this.jfxDatePicker.getDialogParent();
if (parent == null) {
parent = (StackPane) this.getSkinnable().getScene().getRoot();
}
Stack<JFXDatePickerContent> contentStack = (Stack<JFXDatePickerContent>) parent.getProperties().computeIfAbsent(PROPERTY_DATE_PICKER_CONTENTS, k -> new Stack<>());
contentStack.push(this.getPopupContent());

this.getPopupContent().valueProperty.set(this.getSkinnable().getValue());
this.dialog.show(parent);
}
});
}

}

JFXDialog getDialog() {
return this.dialog;
}

public Node getDisplayNode() {
if (this.displayNode == null) {
this.displayNode = this.getEditableInputNode2();
this.displayNode.getStyleClass().add("date-picker-display-node");
this.updateDisplayNode2();
}

this.displayNode.setEditable(this.jfxDatePicker.isEditable());
return this.displayNode;
}

public void syncWithAutoUpdate() {
if (!this.getPopup2().isShowing() && this.jfxDatePicker.isShowing()) {
this.jfxDatePicker.hide();
}

}
}
85 changes: 82 additions & 3 deletions HMCL/src/main/java/org/jackhuang/hmcl/ui/FXUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
import javafx.event.EventHandler;
import javafx.event.EventType;
import javafx.geometry.Bounds;
import javafx.geometry.Pos;
import javafx.geometry.Rectangle2D;
import javafx.scene.Cursor;
import javafx.scene.Node;
Expand All @@ -48,9 +49,7 @@
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.input.*;
import javafx.scene.layout.ColumnConstraints;
import javafx.scene.layout.Priority;
import javafx.scene.layout.Region;
import javafx.scene.layout.*;
import javafx.scene.paint.Color;
import javafx.scene.paint.Paint;
import javafx.scene.shape.Rectangle;
Expand All @@ -68,12 +67,14 @@
import org.jackhuang.hmcl.task.Task;
import org.jackhuang.hmcl.ui.animation.AnimationUtils;
import org.jackhuang.hmcl.ui.animation.Motion;
import org.jackhuang.hmcl.ui.construct.DialogCloseEvent;
import org.jackhuang.hmcl.ui.construct.IconedMenuItem;
import org.jackhuang.hmcl.ui.construct.MenuSeparator;
import org.jackhuang.hmcl.ui.construct.PopupMenu;
import org.jackhuang.hmcl.ui.image.ImageLoader;
import org.jackhuang.hmcl.ui.image.ImageUtils;
import org.jackhuang.hmcl.util.Lang;
import org.jackhuang.hmcl.util.Pair;
import org.jackhuang.hmcl.util.ResourceNotFoundError;
import org.jackhuang.hmcl.util.io.FileUtils;
import org.jackhuang.hmcl.util.io.NetworkUtils;
Expand Down Expand Up @@ -107,6 +108,8 @@
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.PathMatcher;
import java.time.Clock;
import java.time.LocalDate;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.*;
Expand Down Expand Up @@ -1449,6 +1452,15 @@ public static void onSecondaryButtonClicked(Node node, Runnable action) {
});
}

public static void onDoubleClicked(Node node, Runnable action) {
node.addEventHandler(MouseEvent.MOUSE_CLICKED, e -> {
if (e.getButton() == MouseButton.PRIMARY && e.getClickCount() == 2) {
action.run();
e.consume();
}
});
}

public static <N extends Parent> N prepareNode(N node) {
Scene dummyScene = new Scene(node);
StyleSheets.init(dummyScene);
Expand Down Expand Up @@ -1514,6 +1526,20 @@ public static void copyText(String text, @Nullable String toastMessage) {
}
}

public static void copyFiles(List<Path> paths) {
copyFiles(paths.stream().map(Path::toFile).toList(), i18n("message.copied"));
}

public static void copyFiles(List<File> files, @Nullable String toastMessage) {
ClipboardContent content = new ClipboardContent();
content.putFiles(files);
Clipboard.getSystemClipboard().setContent(content);

if (toastMessage != null && !Controllers.isStopped()) {
Controllers.showToast(toastMessage);
}
}

public static List<Node> parseSegment(String segment, Consumer<String> hyperlinkAction) {
if (segment.indexOf('<') < 0)
return Collections.singletonList(new Text(segment));
Expand Down Expand Up @@ -1679,4 +1705,57 @@ public static void useJFXContextMenu(TextInputControl control) {
e.consume();
});
}

public static void chooseDateRange(Consumer<Pair<LocalDate, LocalDate>> onConfirm) {
Controllers.dialog(new DateRangeDialog(onConfirm));
}

private static class DateRangeDialog extends JFXDialogLayout {

private final JFXDatePicker fromPicker, toPicker;

public DateRangeDialog(Consumer<Pair<LocalDate, LocalDate>> onConfirm) {
super();

setHeading(new Label(i18n("button.select_date")));

VBox body = new VBox(8);
{
HBox fromBox = new HBox(4);
fromBox.setAlignment(Pos.CENTER_LEFT);
Label fromLabel = new Label(i18n("button.select_date.from"));
this.fromPicker = new JFXDatePicker(LocalDate.now(Clock.systemDefaultZone()));
fromPicker.setOverLay(true);
fromPicker.setDialogParent(Controllers.getDecorator().getDecorator().getDrawerWrapper());
fromBox.getChildren().setAll(fromLabel, fromPicker);
body.getChildren().add(fromBox);
}
{
HBox toBox = new HBox(4);
toBox.setAlignment(Pos.CENTER_LEFT);
Label toLabel = new Label(i18n("button.select_date.to"));
this.toPicker = new JFXDatePicker(LocalDate.now(Clock.systemDefaultZone()));
toPicker.setOverLay(true);
toPicker.setDialogParent(Controllers.getDecorator().getDecorator().getDrawerWrapper());
toBox.getChildren().setAll(toLabel, toPicker);
body.getChildren().add(toBox);
}
setBody(body);

JFXButton cancelButton = new JFXButton(i18n("button.cancel"));
cancelButton.getStyleClass().add("dialog-cancel");
cancelButton.setOnAction(e -> fireEvent(new DialogCloseEvent()));

JFXButton okButton = new JFXButton(i18n("button.ok"));
okButton.getStyleClass().add("dialog-accept");
okButton.setOnAction(e -> {
if (onConfirm != null) onConfirm.accept(Pair.pair(fromPicker.getValue(), toPicker.getValue()));
fireEvent(new DialogCloseEvent());
});

setActions(cancelButton, okButton);
onEscPressed(this, cancelButton::fire);
}

}
}
Loading