Compare commits
5 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
0a04912529 | ||
![]() |
aa22e3e03f | ||
![]() |
a7148073e3 | ||
![]() |
8ae31667ea | ||
![]() |
ef67779103 |
21
CHANGELOG.md
21
CHANGELOG.md
@ -14,7 +14,26 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
- **Removed** for now removed features.
|
||||
- **Fixed** for any bug fixes.
|
||||
|
||||
[//]: ##[Unreleased]
|
||||
## [Unreleased] - 21-03-2022
|
||||
|
||||
### Added
|
||||
|
||||
- New control: MFXTitledPane
|
||||
- Added some new resources
|
||||
|
||||
### Changes
|
||||
|
||||
- Added/Updated some font resources
|
||||
- Allow controls using MFXLabeledSkinBase to display only the graphic node
|
||||
- Improve width/height computation for MFXRectangleToggleNode
|
||||
- MFXScrollPane: added method to compute the full size of a scroll pane (including scroll bars)
|
||||
- Renamed ToggleButtonsUtil to ToggleUtils, and added a utility method to quickly add several toggles to a group
|
||||
|
||||
### Fixed
|
||||
|
||||
- MFXTitledPaneSkin: minSize of the content pane should be set according to the position
|
||||
- PositionUtils: fix compute position methods, as sometimes "getLayoutBounds().getHeight()" can return 0. Added a
|
||||
parameter to specify whether the sizes must be computed instead of using the layoutBounds
|
||||
|
||||
## [11.13.5] - 11-04-2022
|
||||
|
||||
|
@ -4,7 +4,7 @@ plugins {
|
||||
}
|
||||
|
||||
group 'io.github.palexdev'
|
||||
version '11.13.5'
|
||||
version '11.14.0-EA3'
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
|
@ -23,7 +23,7 @@ import io.github.palexdev.materialfx.controls.MFXRectangleToggleNode;
|
||||
import io.github.palexdev.materialfx.controls.MFXScrollPane;
|
||||
import io.github.palexdev.materialfx.font.MFXFontIcon;
|
||||
import io.github.palexdev.materialfx.utils.ScrollUtils;
|
||||
import io.github.palexdev.materialfx.utils.ToggleButtonsUtil;
|
||||
import io.github.palexdev.materialfx.utils.ToggleUtils;
|
||||
import io.github.palexdev.materialfx.utils.others.loader.MFXLoader;
|
||||
import io.github.palexdev.materialfx.utils.others.loader.MFXLoaderBean;
|
||||
import javafx.application.Platform;
|
||||
@ -79,7 +79,7 @@ public class DemoController implements Initializable {
|
||||
public DemoController(Stage stage) {
|
||||
this.stage = stage;
|
||||
this.toggleGroup = new ToggleGroup();
|
||||
ToggleButtonsUtil.addAlwaysOneSelectedSupport(toggleGroup);
|
||||
ToggleUtils.addAlwaysOneSelectedSupport(toggleGroup);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -71,7 +71,7 @@ public class DialogsController {
|
||||
|
||||
@FXML
|
||||
private void openInfo(ActionEvent event) {
|
||||
MFXFontIcon infoIcon = new MFXFontIcon("mfx-info-circle-filled", 18);
|
||||
MFXFontIcon infoIcon = new MFXFontIcon("mfx-info-circle", 18);
|
||||
dialogContent.setHeaderIcon(infoIcon);
|
||||
dialogContent.setHeaderText("This is a generic info dialog");
|
||||
convertDialogTo("mfx-info-dialog");
|
||||
|
@ -16,7 +16,7 @@
|
||||
* along with MaterialFX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
@import '../fonts/Fonts.css';
|
||||
@import 'Fonts.css';
|
||||
@import 'Common.css';
|
||||
@import 'MFXColors.css';
|
||||
|
||||
|
@ -16,7 +16,7 @@
|
||||
* along with MaterialFX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
@import '../fonts/Fonts.css';
|
||||
@import 'Fonts.css';
|
||||
@import 'Common.css';
|
||||
@import 'MFXColors.css';
|
||||
|
||||
|
@ -16,7 +16,7 @@
|
||||
* along with MaterialFX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
@import '../fonts/Fonts.css';
|
||||
@import 'Fonts.css';
|
||||
@import 'Common.css';
|
||||
@import 'MFXColors.css';
|
||||
|
||||
|
@ -16,7 +16,7 @@
|
||||
* along with MaterialFX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
@import '../fonts/Fonts.css';
|
||||
@import 'Fonts.css';
|
||||
@import 'MFXColors.css';
|
||||
|
||||
.header-label {
|
||||
|
@ -16,7 +16,7 @@
|
||||
* along with MaterialFX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
@import '../fonts/Fonts.css';
|
||||
@import 'Fonts.css';
|
||||
@import 'MFXColors.css';
|
||||
|
||||
/**************************************************
|
||||
|
@ -16,7 +16,7 @@
|
||||
* along with MaterialFX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
@import '../fonts/Fonts.css';
|
||||
@import 'Fonts.css';
|
||||
@import 'Common.css';
|
||||
@import 'MFXColors.css';
|
||||
|
||||
|
@ -16,7 +16,7 @@
|
||||
* along with MaterialFX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
@import '../fonts/Fonts.css';
|
||||
@import 'Fonts.css';
|
||||
@import 'Common.css';
|
||||
@import 'MFXColors.css';
|
||||
|
||||
|
@ -16,7 +16,7 @@
|
||||
* along with MaterialFX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
@import '../fonts/Fonts.css';
|
||||
@import 'Fonts.css';
|
||||
@import 'Common.css';
|
||||
@import 'MFXColors.css';
|
||||
|
||||
|
@ -16,7 +16,7 @@
|
||||
* along with MaterialFX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
@import '../fonts/Fonts.css';
|
||||
@import 'Fonts.css';
|
||||
@import 'Common.css';
|
||||
@import 'MFXColors.css';
|
||||
|
||||
|
@ -16,7 +16,7 @@
|
||||
* along with MaterialFX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
@import '../fonts/Fonts.css';
|
||||
@import 'Fonts.css';
|
||||
@import 'Common.css';
|
||||
@import 'MFXColors.css';
|
||||
|
||||
|
@ -16,7 +16,7 @@
|
||||
* along with MaterialFX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
@import '../fonts/Fonts.css';
|
||||
@import 'Fonts.css';
|
||||
@import 'Common.css';
|
||||
@import 'MFXColors.css';
|
||||
|
||||
|
@ -1,42 +1,25 @@
|
||||
import io.github.palexdev.materialfx.controls.MFXTextField;
|
||||
import io.github.palexdev.materialfx.controls.MFXTitledPane;
|
||||
import io.github.palexdev.materialfx.enums.HeaderPosition;
|
||||
import javafx.application.Application;
|
||||
import javafx.geometry.Pos;
|
||||
import javafx.scene.Scene;
|
||||
import javafx.scene.control.TextFormatter;
|
||||
import javafx.scene.layout.VBox;
|
||||
import javafx.scene.layout.BorderPane;
|
||||
import javafx.scene.shape.Rectangle;
|
||||
import javafx.stage.Stage;
|
||||
import org.scenicview.ScenicView;
|
||||
|
||||
import java.text.DecimalFormat;
|
||||
import java.text.ParsePosition;
|
||||
|
||||
public class Playground extends Application {
|
||||
private final double w = 445;
|
||||
private final double h = 270;
|
||||
|
||||
@Override
|
||||
public void start(Stage primaryStage) {
|
||||
VBox vBox = new VBox(10);
|
||||
vBox.setAlignment(Pos.CENTER);
|
||||
BorderPane bp = new BorderPane();
|
||||
|
||||
DecimalFormat format = new DecimalFormat("#.0");
|
||||
MFXTextField field = new MFXTextField("", "", "Numbers");
|
||||
field.delegateSetTextFormatter(new TextFormatter<>(c ->
|
||||
{
|
||||
if (c.getControlNewText().isEmpty()) {
|
||||
return c;
|
||||
}
|
||||
MFXTitledPane tp = new MFXTitledPane("SideBar", new Rectangle(200, 1200));
|
||||
tp.setHeaderPos(HeaderPosition.LEFT);
|
||||
bp.setRight(tp);
|
||||
|
||||
ParsePosition parsePosition = new ParsePosition(0);
|
||||
Object object = format.parse(c.getControlNewText(), parsePosition);
|
||||
|
||||
if (object == null || parsePosition.getIndex() < c.getControlNewText().length()) {
|
||||
return null;
|
||||
} else {
|
||||
return c;
|
||||
}
|
||||
}));
|
||||
|
||||
vBox.getChildren().addAll(field);
|
||||
Scene scene = new Scene(vBox, 800, 800);
|
||||
Scene scene = new Scene(bp, 1440, 900);
|
||||
primaryStage.setScene(scene);
|
||||
primaryStage.show();
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
GROUP=io.github.palexdev
|
||||
POM_ARTIFACT_ID=materialfx
|
||||
VERSION_NAME=11.13.5
|
||||
VERSION_NAME=11.14.0-EA3
|
||||
|
||||
POM_NAME=materialfx
|
||||
POM_DESCRIPTION=Material Desgin components for JavaFX
|
||||
@ -10,7 +10,7 @@ POM_URL=https://github.com/palexdev/MaterialFX
|
||||
POM_SCM_URL=https://github.com/palexdev/MaterialFX
|
||||
|
||||
POM_LICENCE_NAME=GNU LGPLv3
|
||||
POM_LICENCE_URL=https://www.gnu.org/licenses/gpl-3.0.html
|
||||
POM_LICENCE_URL=https://www.gnu.org/licenses/lgpl-3.0.html
|
||||
POM_LICENCE_DIST=repo
|
||||
|
||||
POM_DEVELOPER_ID=palexdev
|
||||
|
@ -0,0 +1,115 @@
|
||||
package io.github.palexdev.materialfx.controls;
|
||||
|
||||
import javafx.beans.property.ReadOnlyObjectProperty;
|
||||
import javafx.beans.property.ReadOnlyObjectWrapper;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ListChangeListener;
|
||||
import javafx.collections.ObservableList;
|
||||
import javafx.scene.Node;
|
||||
import javafx.scene.control.ToggleGroup;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* This class acts as a {@link ToggleGroup} but for {@link MFXTitledPane}s.
|
||||
*/
|
||||
public class ExpandGroup {
|
||||
private Set<Node> prevPanes = new HashSet<>();
|
||||
private final ReadOnlyObjectWrapper<MFXTitledPane> expandedPane = new ReadOnlyObjectWrapper<>() {
|
||||
@Override
|
||||
public void set(MFXTitledPane newValue) {
|
||||
if (isBound()) {
|
||||
throw new RuntimeException("A bound value cannot be set.");
|
||||
}
|
||||
MFXTitledPane old = get();
|
||||
if (old == newValue) return;
|
||||
|
||||
if (setExpanded(newValue, true) ||
|
||||
(newValue != null && newValue.getExpandGroup() == ExpandGroup.this) ||
|
||||
(newValue == null)) {
|
||||
if (old == null || old.getExpandGroup() == ExpandGroup.this || !old.isExpanded())
|
||||
setExpanded(old, false);
|
||||
super.set(newValue);
|
||||
}
|
||||
}
|
||||
};
|
||||
private final ObservableList<MFXTitledPane> panes = FXCollections.observableArrayList();
|
||||
|
||||
public ExpandGroup() {
|
||||
panes.addListener((ListChangeListener<? super MFXTitledPane>) c -> {
|
||||
while (c.next()) {
|
||||
List<? extends MFXTitledPane> addedList = c.getAddedSubList();
|
||||
|
||||
for (MFXTitledPane removed : c.getRemoved()) {
|
||||
if (removed.isExpanded()) {
|
||||
setExpandedPane(null);
|
||||
}
|
||||
|
||||
if (!addedList.contains(removed)) {
|
||||
removed.setExpandGroup(null);
|
||||
}
|
||||
}
|
||||
|
||||
for (MFXTitledPane added : addedList) {
|
||||
if (prevPanes.contains(added))
|
||||
throw new IllegalArgumentException("Duplicate panes are not allowed!");
|
||||
if (!this.equals(added.getExpandGroup())) {
|
||||
if (added.getExpandGroup() != null) added.getExpandGroup().getPanes().remove(added);
|
||||
added.setExpandGroup(this);
|
||||
}
|
||||
}
|
||||
|
||||
for (MFXTitledPane added : addedList) {
|
||||
if (added.isExpanded()) {
|
||||
setExpandedPane(added);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
prevPanes = new HashSet<>(c.getList());
|
||||
});
|
||||
}
|
||||
|
||||
private boolean setExpanded(MFXTitledPane pane, boolean expanded) {
|
||||
if (pane != null &&
|
||||
pane.getExpandGroup() == this &&
|
||||
!pane.expandedProperty().isBound()) {
|
||||
pane.setExpanded(expanded);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public final void clearExpandedPane() {
|
||||
if (!getExpandedPane().isExpanded()) {
|
||||
for (MFXTitledPane pane : getPanes()) {
|
||||
if (pane.isExpanded()) return;
|
||||
}
|
||||
}
|
||||
setExpandedPane(null);
|
||||
}
|
||||
|
||||
public MFXTitledPane getExpandedPane() {
|
||||
return expandedPane.get();
|
||||
}
|
||||
|
||||
public ReadOnlyObjectProperty<MFXTitledPane> expandedPaneProperty() {
|
||||
return expandedPane.getReadOnlyProperty();
|
||||
}
|
||||
|
||||
public final void setExpandedPane(MFXTitledPane expandedPane) {
|
||||
this.expandedPane.set(expandedPane);
|
||||
}
|
||||
|
||||
public ObservableList<MFXTitledPane> getPanes() {
|
||||
return FXCollections.unmodifiableObservableList(panes);
|
||||
}
|
||||
|
||||
public static void addToGroup(ExpandGroup group, MFXTitledPane... panes) {
|
||||
for (MFXTitledPane pane : panes) {
|
||||
pane.setExpandGroup(group);
|
||||
}
|
||||
}
|
||||
}
|
@ -19,11 +19,13 @@
|
||||
package io.github.palexdev.materialfx.controls;
|
||||
|
||||
import io.github.palexdev.materialfx.MFXResourcesLoader;
|
||||
import io.github.palexdev.materialfx.beans.SizeBean;
|
||||
import io.github.palexdev.materialfx.skins.MFXScrollPaneSkin;
|
||||
import io.github.palexdev.materialfx.utils.ColorUtils;
|
||||
import javafx.beans.property.ObjectProperty;
|
||||
import javafx.beans.property.SimpleObjectProperty;
|
||||
import javafx.scene.Node;
|
||||
import javafx.scene.control.ScrollBar;
|
||||
import javafx.scene.control.ScrollPane;
|
||||
import javafx.scene.control.Skin;
|
||||
import javafx.scene.paint.Color;
|
||||
@ -40,6 +42,8 @@ public class MFXScrollPane extends ScrollPane {
|
||||
//================================================================================
|
||||
private final String STYLE_CLASS = "mfx-scroll-pane";
|
||||
private final String STYLESHEET = MFXResourcesLoader.load("css/MFXScrollPane.css");
|
||||
private ScrollBar vBar;
|
||||
private ScrollBar hBar;
|
||||
|
||||
//================================================================================
|
||||
// Constructors
|
||||
@ -61,6 +65,28 @@ public class MFXScrollPane extends ScrollPane {
|
||||
addListeners();
|
||||
}
|
||||
|
||||
public SizeBean getFullSizes() {
|
||||
ScrollBar vBar = (this.vBar != null) ? this.vBar : (ScrollBar) lookup(".vBar");
|
||||
ScrollBar hBar = (this.hBar != null) ? this.hBar : (ScrollBar) lookup(".hBar");
|
||||
|
||||
this.vBar = vBar;
|
||||
this.hBar = hBar;
|
||||
|
||||
double vBarW = (vBar != null) ?
|
||||
vBar.snappedLeftInset() + vBar.prefWidth(-1) + vBar.snappedRightInset() :
|
||||
0.0;
|
||||
|
||||
double hBarH = (hBar != null) ?
|
||||
hBar.snappedTopInset() + hBar.prefHeight(-1) + hBar.snappedBottomInset() :
|
||||
0.0;
|
||||
|
||||
double contentW = (getContent() != null) ? getContent().prefWidth(-1) : 0.0;
|
||||
double contentH = (getContent() != null) ? getContent().prefHeight(-1) : 0.0;
|
||||
double fullW = snappedLeftInset() + contentW + snappedRightInset() + vBarW;
|
||||
double fullH = snappedTopInset() + contentH + snappedBottomInset() + hBarH;
|
||||
return SizeBean.of(fullW, fullH);
|
||||
}
|
||||
|
||||
//================================================================================
|
||||
// Style Properties
|
||||
//================================================================================
|
||||
|
@ -0,0 +1,482 @@
|
||||
/*
|
||||
* Copyright (C) 2022 Parisi Alessandro
|
||||
* This file is part of MaterialFX (https://github.com/palexdev/MaterialFX).
|
||||
*
|
||||
* MaterialFX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* MaterialFX 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 Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with MaterialFX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.github.palexdev.materialfx.controls;
|
||||
|
||||
import io.github.palexdev.materialfx.MFXResourcesLoader;
|
||||
import io.github.palexdev.materialfx.beans.properties.functional.SupplierProperty;
|
||||
import io.github.palexdev.materialfx.beans.properties.styleable.StyleableBooleanProperty;
|
||||
import io.github.palexdev.materialfx.beans.properties.styleable.StyleableObjectProperty;
|
||||
import io.github.palexdev.materialfx.effects.Interpolators;
|
||||
import io.github.palexdev.materialfx.enums.HeaderPosition;
|
||||
import io.github.palexdev.materialfx.font.MFXFontIcon;
|
||||
import io.github.palexdev.materialfx.skins.MFXTitledPaneSkin;
|
||||
import io.github.palexdev.materialfx.utils.AnimationUtils;
|
||||
import io.github.palexdev.materialfx.utils.NodeUtils;
|
||||
import io.github.palexdev.materialfx.utils.StyleablePropertiesUtils;
|
||||
import javafx.beans.binding.Bindings;
|
||||
import javafx.beans.property.*;
|
||||
import javafx.css.CssMetaData;
|
||||
import javafx.css.PseudoClass;
|
||||
import javafx.css.Styleable;
|
||||
import javafx.css.StyleablePropertyFactory;
|
||||
import javafx.geometry.Pos;
|
||||
import javafx.scene.Node;
|
||||
import javafx.scene.control.Control;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.control.Skin;
|
||||
import javafx.scene.control.TitledPane;
|
||||
import javafx.scene.input.MouseButton;
|
||||
import javafx.scene.input.MouseEvent;
|
||||
import javafx.scene.layout.HBox;
|
||||
import javafx.scene.layout.Priority;
|
||||
import javafx.scene.layout.StackPane;
|
||||
import javafx.scene.layout.VBox;
|
||||
import javafx.util.Duration;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* This is the implementation of a JavaFX's {@link TitledPane} remade from scratch to give it
|
||||
* more features, flexibility and of course a modern look.
|
||||
* <p></p>
|
||||
* Unlike the original pane, this one allows you to set whatever you want as header by setting
|
||||
* the {@link #headerSupplierProperty()}, just keep in mind that a {@code null} supplier or a {@code null} return value
|
||||
* won't be accepted. When using this constructor, {@link MFXTitledPane#MFXTitledPane(String, Node)}, the supplier
|
||||
* will be set to build a new {@link DefaultHeader} pane.
|
||||
* <p>
|
||||
* So, the {@link #titleProperty()}, is only relevant when using the default header supplier, or (of course) if
|
||||
* by making your own header you decide to use it in some way (a Label bound to the title for example).
|
||||
* <p></p>
|
||||
* There are also three other new features:
|
||||
* <p> - You can set the header position wherever you like, TOP/RIGHT/BOTTOM/LEFT
|
||||
* <p> - There's no need to use an accordion anymore, you can simply arrange multiple {@code MFXTitledPanes} in a
|
||||
* container, like a VBox or HBox, and use an {@link ExpandGroup} to achieve the same behavior
|
||||
* <p> - Unlike the original one, you can specify the duration of the expand/collapse animation
|
||||
*/
|
||||
public class MFXTitledPane extends Control {
|
||||
//================================================================================
|
||||
// Properties
|
||||
//================================================================================
|
||||
private final String STYLE_CLASS = "mfx-titled-pane";
|
||||
private final String STYLESHEET = MFXResourcesLoader.load("css/MFXTitledPane.css");
|
||||
|
||||
private final StringProperty title = new SimpleStringProperty();
|
||||
private final SupplierProperty<Node> headerSupplier = new SupplierProperty<>();
|
||||
private final ObjectProperty<Node> content = new SimpleObjectProperty<>();
|
||||
|
||||
private final BooleanProperty expanded = new SimpleBooleanProperty() {
|
||||
@Override
|
||||
protected void invalidated() {
|
||||
boolean state = get();
|
||||
pseudoClassStateChanged(EXPANDED_PSEUDO_CLASS, state);
|
||||
pseudoClassStateChanged(COLLAPSED_PSEUDO_CLASS, !state);
|
||||
}
|
||||
};
|
||||
private final ObjectProperty<ExpandGroup> expandGroup = new SimpleObjectProperty<>();
|
||||
|
||||
protected static final PseudoClass EXPANDED_PSEUDO_CLASS = PseudoClass.getPseudoClass("expanded");
|
||||
protected static final PseudoClass COLLAPSED_PSEUDO_CLASS = PseudoClass.getPseudoClass("collapsed");
|
||||
|
||||
//================================================================================
|
||||
// Constructors
|
||||
//================================================================================
|
||||
public MFXTitledPane() {
|
||||
this("Title", null);
|
||||
}
|
||||
|
||||
public MFXTitledPane(String title, Node content) {
|
||||
defaultHeaderSupplier();
|
||||
setTitle(title);
|
||||
setContent(content);
|
||||
initialize();
|
||||
}
|
||||
|
||||
public MFXTitledPane(Supplier<Node> headerSupplier, Node content) {
|
||||
setHeaderSupplier(headerSupplier);
|
||||
setContent(content);
|
||||
initialize();
|
||||
}
|
||||
|
||||
//================================================================================
|
||||
// Methods
|
||||
//================================================================================
|
||||
private void initialize() {
|
||||
getStyleClass().add(STYLE_CLASS);
|
||||
|
||||
expandGroup.addListener((observable, oldGroup, newGroup) -> {
|
||||
if (newGroup != null && newGroup.getPanes().contains(this)) {
|
||||
if (oldGroup != null) oldGroup.getPanes().remove(this);
|
||||
newGroup.getPanes().add(this);
|
||||
} else if (newGroup == null) {
|
||||
oldGroup.getPanes().remove(this);
|
||||
}
|
||||
});
|
||||
expanded.addListener(invalidated -> {
|
||||
ExpandGroup eg = getExpandGroup();
|
||||
if (eg != null) {
|
||||
if (isExpanded()) {
|
||||
eg.setExpandedPane(this);
|
||||
} else if (eg.getExpandedPane() == this) {
|
||||
eg.clearExpandedPane();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the {@link #headerSupplierProperty()} to the default supplier.
|
||||
*/
|
||||
public void defaultHeaderSupplier() {
|
||||
setHeaderSupplier(DefaultHeader::new);
|
||||
}
|
||||
|
||||
//================================================================================
|
||||
// Styleable Properties
|
||||
//================================================================================
|
||||
private final StyleableBooleanProperty animated = new StyleableBooleanProperty(
|
||||
StyleableProperties.ANIMATED,
|
||||
this,
|
||||
"animated",
|
||||
true
|
||||
);
|
||||
|
||||
private final StyleableObjectProperty<Duration> animationDuration = new StyleableObjectProperty<>(
|
||||
StyleableProperties.ANIMATION_DURATION,
|
||||
this,
|
||||
"animationDuration",
|
||||
Duration.millis(300)
|
||||
);
|
||||
|
||||
private final StyleableBooleanProperty collapsible = new StyleableBooleanProperty(
|
||||
StyleableProperties.COLLAPSIBLE,
|
||||
this,
|
||||
"collapsible",
|
||||
true
|
||||
);
|
||||
|
||||
private final StyleableObjectProperty<HeaderPosition> headerPos = new StyleableObjectProperty<>(
|
||||
StyleableProperties.HEADER_POS,
|
||||
this,
|
||||
"headerPos",
|
||||
HeaderPosition.TOP
|
||||
);
|
||||
|
||||
public boolean isAnimated() {
|
||||
return animated.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies whether to animate the expand/collapse transition.
|
||||
*/
|
||||
public StyleableBooleanProperty animatedProperty() {
|
||||
return animated;
|
||||
}
|
||||
|
||||
public void setAnimated(boolean animated) {
|
||||
this.animated.set(animated);
|
||||
}
|
||||
|
||||
public Duration getAnimationDuration() {
|
||||
return animationDuration.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies the duration of the expand/collapse animation.
|
||||
*/
|
||||
public StyleableObjectProperty<Duration> animationDurationProperty() {
|
||||
return animationDuration;
|
||||
}
|
||||
|
||||
public void setAnimationDuration(Duration animationDuration) {
|
||||
this.animationDuration.set(animationDuration);
|
||||
}
|
||||
|
||||
public boolean isCollapsible() {
|
||||
return collapsible.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies whether the pane can be collapsed.
|
||||
*/
|
||||
public StyleableBooleanProperty collapsibleProperty() {
|
||||
return collapsible;
|
||||
}
|
||||
|
||||
public void setCollapsible(boolean collapsible) {
|
||||
this.collapsible.set(collapsible);
|
||||
}
|
||||
|
||||
public HeaderPosition getHeaderPos() {
|
||||
return headerPos.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies the position of the header node.
|
||||
*/
|
||||
public StyleableObjectProperty<HeaderPosition> headerPosProperty() {
|
||||
return headerPos;
|
||||
}
|
||||
|
||||
public void setHeaderPos(HeaderPosition headerPos) {
|
||||
this.headerPos.set(headerPos);
|
||||
}
|
||||
|
||||
//================================================================================
|
||||
// CSSMetaData
|
||||
//================================================================================
|
||||
private static class StyleableProperties {
|
||||
private static final StyleablePropertyFactory<MFXTitledPane> FACTORY = new StyleablePropertyFactory<>(Control.getClassCssMetaData());
|
||||
private static final List<CssMetaData<? extends Styleable, ?>> cssMetaDataList;
|
||||
|
||||
private static final CssMetaData<MFXTitledPane, Boolean> ANIMATED =
|
||||
FACTORY.createBooleanCssMetaData(
|
||||
"-mfx-animated",
|
||||
MFXTitledPane::animatedProperty,
|
||||
true
|
||||
);
|
||||
|
||||
private static final CssMetaData<MFXTitledPane, Duration> ANIMATION_DURATION =
|
||||
FACTORY.createDurationCssMetaData(
|
||||
"-mfx-animation-duration",
|
||||
MFXTitledPane::animationDurationProperty,
|
||||
Duration.millis(300)
|
||||
);
|
||||
|
||||
private static final CssMetaData<MFXTitledPane, Boolean> COLLAPSIBLE =
|
||||
FACTORY.createBooleanCssMetaData(
|
||||
"-mfx-collapsible",
|
||||
MFXTitledPane::collapsibleProperty,
|
||||
true
|
||||
);
|
||||
|
||||
private static final CssMetaData<MFXTitledPane, HeaderPosition> HEADER_POS =
|
||||
FACTORY.createEnumCssMetaData(
|
||||
HeaderPosition.class,
|
||||
"-mfx-pos",
|
||||
MFXTitledPane::headerPosProperty,
|
||||
HeaderPosition.TOP
|
||||
);
|
||||
|
||||
static {
|
||||
cssMetaDataList = StyleablePropertiesUtils.cssMetaDataList(
|
||||
Control.getClassCssMetaData(),
|
||||
ANIMATED, ANIMATION_DURATION, COLLAPSIBLE, HEADER_POS
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public static List<CssMetaData<? extends Styleable, ?>> getClassCssMetaData() {
|
||||
return StyleableProperties.cssMetaDataList;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<CssMetaData<? extends Styleable, ?>> getControlCssMetaData() {
|
||||
return getClassCssMetaData();
|
||||
}
|
||||
|
||||
//================================================================================
|
||||
// Overridden Methods
|
||||
//================================================================================
|
||||
@Override
|
||||
protected Skin<?> createDefaultSkin() {
|
||||
return new MFXTitledPaneSkin(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUserAgentStylesheet() {
|
||||
return STYLESHEET;
|
||||
}
|
||||
|
||||
//================================================================================
|
||||
// Getters/Setters
|
||||
//================================================================================
|
||||
public String getTitle() {
|
||||
return title.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies the pane's title.
|
||||
*/
|
||||
public StringProperty titleProperty() {
|
||||
return title;
|
||||
}
|
||||
|
||||
public void setTitle(String title) {
|
||||
this.title.set(title);
|
||||
}
|
||||
|
||||
public Supplier<Node> getHeaderSupplier() {
|
||||
return headerSupplier.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies the {@link Supplier} used to build the header node.
|
||||
* <p></p>
|
||||
* The default one builds a new {@link DefaultHeader}.
|
||||
*/
|
||||
public SupplierProperty<Node> headerSupplierProperty() {
|
||||
return headerSupplier;
|
||||
}
|
||||
|
||||
public void setHeaderSupplier(Supplier<Node> headerSupplier) {
|
||||
this.headerSupplier.set(headerSupplier);
|
||||
}
|
||||
|
||||
public Node getContent() {
|
||||
return content.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies the pane's content, can be null and changed at runtime.
|
||||
*/
|
||||
public ObjectProperty<Node> contentProperty() {
|
||||
return content;
|
||||
}
|
||||
|
||||
public void setContent(Node content) {
|
||||
this.content.set(content);
|
||||
}
|
||||
|
||||
public boolean isExpanded() {
|
||||
return expanded.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies the expand state of the pane.
|
||||
*/
|
||||
public BooleanProperty expandedProperty() {
|
||||
return expanded;
|
||||
}
|
||||
|
||||
public void setExpanded(boolean expanded) {
|
||||
this.expanded.set(expanded);
|
||||
}
|
||||
|
||||
public ExpandGroup getExpandGroup() {
|
||||
return expandGroup.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies the {@link ExpandGroup} this pane belongs to.
|
||||
*/
|
||||
public ObjectProperty<ExpandGroup> expandGroupProperty() {
|
||||
return expandGroup;
|
||||
}
|
||||
|
||||
public void setExpandGroup(ExpandGroup expandGroup) {
|
||||
this.expandGroup.set(expandGroup);
|
||||
}
|
||||
|
||||
//================================================================================
|
||||
// Default Header Class
|
||||
//================================================================================
|
||||
|
||||
/**
|
||||
* Default header used by {@link MFXTitledPane}s.
|
||||
* <p></p>
|
||||
* It basically consists in a {@link Label} which has its text bound to the pane's {@link #titleProperty()},
|
||||
* and a {@link MFXFontIcon} (wrapped in a {@link MFXIconWrapper}) for the arrow, which is responsible
|
||||
* for expanding/collapsing the pane.
|
||||
* <p></p>
|
||||
* This header is capable of rearranging itself when the {@link #headerPosProperty()}, to make things easier
|
||||
* the layout is not manual but automatically managed, see {@link #initializeContainer()} for more info.
|
||||
* <p>
|
||||
* The icon also depends on the {@link #headerPosProperty()}, see {@link #iconForPosition()}.
|
||||
*/
|
||||
public class DefaultHeader extends StackPane {
|
||||
private final Label label;
|
||||
private final MFXIconWrapper wrapped;
|
||||
|
||||
public DefaultHeader() {
|
||||
label = new Label();
|
||||
label.textProperty().bind(titleProperty());
|
||||
label.getStyleClass().add("header-label");
|
||||
label.setMaxWidth(Double.MAX_VALUE);
|
||||
HBox.setHgrow(label, Priority.ALWAYS);
|
||||
|
||||
MFXFontIcon icon = new MFXFontIcon("mfx-chevron-left", 14);
|
||||
icon.descriptionProperty().bind(Bindings.createStringBinding(this::iconForPosition, headerPosProperty()));
|
||||
|
||||
wrapped = new MFXIconWrapper(icon, 20).defaultRippleGeneratorBehavior();
|
||||
wrapped.setRotate(isExpanded() ? -180 : 0);
|
||||
NodeUtils.makeRegionCircular(wrapped);
|
||||
|
||||
wrapped.addEventHandler(MouseEvent.MOUSE_CLICKED, event -> {
|
||||
if (event.getButton() != MouseButton.PRIMARY || (isExpanded() && !isCollapsible())) return;
|
||||
setExpanded(!isExpanded());
|
||||
});
|
||||
|
||||
expanded.addListener((observable, oldValue, newValue) -> {
|
||||
double rotate = newValue ? -180 : 0;
|
||||
AnimationUtils.TimelineBuilder.build()
|
||||
.add(AnimationUtils.KeyFrames.of(200, wrapped.rotateProperty(), rotate, Interpolators.INTERPOLATOR_V1))
|
||||
.getAnimation()
|
||||
.play();
|
||||
});
|
||||
|
||||
headerPos.addListener(invalidated -> initializeContainer());
|
||||
initializeContainer();
|
||||
|
||||
getStyleClass().add("header-pane");
|
||||
}
|
||||
|
||||
/**
|
||||
* Responsible for rearranging the header's content when the {@link #headerPosProperty()} changes.
|
||||
* <p></p>
|
||||
* When it is LEFT or RIGHT, both the label and icon will be contained by a VBox,
|
||||
* otherwise they will be contained by a HBox.
|
||||
*/
|
||||
private void initializeContainer() {
|
||||
HeaderPosition position = getHeaderPos();
|
||||
Node container;
|
||||
if (position == HeaderPosition.LEFT || position == HeaderPosition.RIGHT) {
|
||||
container = new VBox(label, wrapped);
|
||||
((VBox) container).setAlignment(Pos.CENTER);
|
||||
} else {
|
||||
container = new HBox(label, wrapped);
|
||||
((HBox) container).setAlignment(Pos.CENTER);
|
||||
}
|
||||
getChildren().setAll(container);
|
||||
}
|
||||
|
||||
/**
|
||||
* Responsible for changing the icon when {@link #headerPosProperty()} changes.
|
||||
* <p>
|
||||
* <p> - Case RIGHT: "mfx-chevron-left"
|
||||
* <p> - Case BOTTOM: "mfx-chevron-down"
|
||||
* <p> - Case LEFT: "mfx-chevron-right"
|
||||
* <p> - Case TOP: "mfx-chevron-up"
|
||||
*/
|
||||
protected String iconForPosition() {
|
||||
HeaderPosition position = getHeaderPos();
|
||||
switch (position) {
|
||||
case RIGHT:
|
||||
return "mfx-chevron-left";
|
||||
case BOTTOM:
|
||||
return "mfx-chevron-down";
|
||||
case LEFT:
|
||||
return "mfx-chevron-right";
|
||||
case TOP:
|
||||
default:
|
||||
return "mfx-chevron-up";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -24,7 +24,7 @@ import io.github.palexdev.materialfx.controls.base.MFXLabeled;
|
||||
import io.github.palexdev.materialfx.skins.MFXToggleButtonSkin;
|
||||
import io.github.palexdev.materialfx.utils.ColorUtils;
|
||||
import io.github.palexdev.materialfx.utils.StyleablePropertiesUtils;
|
||||
import io.github.palexdev.materialfx.utils.ToggleButtonsUtil;
|
||||
import io.github.palexdev.materialfx.utils.ToggleUtils;
|
||||
import javafx.beans.property.BooleanProperty;
|
||||
import javafx.beans.property.ObjectProperty;
|
||||
import javafx.beans.property.SimpleBooleanProperty;
|
||||
@ -114,7 +114,7 @@ public class MFXToggleButton extends Labeled implements Toggle, MFXLabeled {
|
||||
if (isSelected()) {
|
||||
tg.selectToggle(this);
|
||||
} else if (tg.getSelectedToggle() == this) {
|
||||
ToggleButtonsUtil.clearSelectedToggle(tg);
|
||||
ToggleUtils.clearSelectedToggle(tg);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -27,7 +27,7 @@ import javafx.util.StringConverter;
|
||||
* <p>
|
||||
* {@code SpinnerModel} is basically an helper class to allow the spinner to work on any object
|
||||
* type as long as a model exists for it. The model is responsible for changing the spinner's value by
|
||||
* going forward or backwards ({@link #next() or {@link #previous()}}.
|
||||
* going forward or backwards ({@link #next()}, or {@link #previous()}).
|
||||
* <p>
|
||||
* Along this core functionality the model also specifies a {@link StringConverter} which will be
|
||||
* used to convert the T value to a String, which will be the spinner's text.
|
||||
|
@ -30,7 +30,7 @@ public class MFXDialogs {
|
||||
}
|
||||
|
||||
public static MFXGenericDialogBuilder info() {
|
||||
MFXFontIcon infoIcon = new MFXFontIcon("mfx-info-circle-filled", 18);
|
||||
MFXFontIcon infoIcon = new MFXFontIcon("mfx-info-circle", 18);
|
||||
return MFXGenericDialogBuilder.build().addStyleClasses("mfx-info-dialog").setHeaderIcon(infoIcon);
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright (C) 2022 Parisi Alessandro
|
||||
* This file is part of MaterialFX (https://github.com/palexdev/MaterialFX).
|
||||
*
|
||||
* MaterialFX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* MaterialFX 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 Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with MaterialFX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.github.palexdev.materialfx.enums;
|
||||
|
||||
import io.github.palexdev.materialfx.controls.MFXTitledPane;
|
||||
|
||||
/**
|
||||
* Enumeration used by {@link MFXTitledPane} to specify the header's position.
|
||||
*/
|
||||
public enum HeaderPosition {
|
||||
TOP, RIGHT, BOTTOM, LEFT
|
||||
}
|
@ -19,7 +19,7 @@
|
||||
package io.github.palexdev.materialfx.font;
|
||||
|
||||
/**
|
||||
* Enumerator class for MaterialFX font resources. (Count: 132)
|
||||
* Enumerator class for MaterialFX font resources. (Count: 167)
|
||||
*/
|
||||
public enum FontResources {
|
||||
ANGLE_DOWN("mfx-angle-down", '\uE900'),
|
||||
@ -53,108 +53,142 @@ public enum FontResources {
|
||||
CHEVRON_RIGHT("mfx-chevron-right", '\uE91C'),
|
||||
CHEVRON_UP("mfx-chevron-up", '\uE91D'),
|
||||
CIRCLE("mfx-circle", '\uE91E'),
|
||||
CIRCLE_DOT("mfx-circle-dot", '\uE91F'),
|
||||
CIRCLE_EMPTY("mfx-circle-empty", '\uE920'),
|
||||
CONTENT_COPY("mfx-content-copy", '\uE921'),
|
||||
CONTENT_CUT("mfx-content-cut", '\uE922'),
|
||||
CONTENT_PASTE("mfx-content-paste", '\uE923'),
|
||||
DASHBOARD("mfx-dashboard", '\uE924'),
|
||||
DEBUG("mfx-debug", '\uE925'),
|
||||
DELETE("mfx-delete", '\uE926'),
|
||||
DELETE_ALT("mfx-delete-alt", '\uE927'),
|
||||
DO_NOT_ENTER_CIRCLE("mfx-do-not-enter-circle", '\uE928'),
|
||||
ELLIPSIS_VERTICAL("mfx-ellipsis-vertical", '\uE929'),
|
||||
EXCLAMATION_CIRCLE("mfx-exclamation-circle", '\uE92A'),
|
||||
EXCLAMATION_CIRCLE_FILLED("mfx-exclamation-circle-filled", '\uE92B'),
|
||||
EXCLAMATION_TRIANGLE("mfx-exclamation-triangle", '\uE92C'),
|
||||
EXPAND("mfx-expand", '\uE92D'),
|
||||
EYE("mfx-eye", '\uE92E'),
|
||||
EYE_SLASH("mfx-eye-slash", '\uE92F'),
|
||||
FILE("mfx-file", '\uE930'),
|
||||
FILTER("mfx-filter", '\uE931'),
|
||||
FILTER_ALT("mfx-filter-alt", '\uE932'),
|
||||
FILTER_ALT_CLEAR("mfx-filter-alt-clear", '\uE933'),
|
||||
FIRST_PAGE("mfx-first-page", '\uE934'),
|
||||
FIT("mfx-fit", '\uE935'),
|
||||
FOLDER("mfx-folder", '\uE936'),
|
||||
FONTICONS("mfx-fonticons", '\uE937'),
|
||||
GEAR("mfx-gear", '\uE938'),
|
||||
GOOGLE("mfx-google", '\uE939'),
|
||||
GOOGLE_DRAWING("mfx-google-drawing", '\uE93A'),
|
||||
GOOGLE_DRIVE("mfx-google-drive", '\uE93B'),
|
||||
GOOGLE_FORMS("mfx-google-forms", '\uE93C'),
|
||||
GOOGLE_FUSION_TABLES("mfx-google-fusion-tables", '\uE93D'),
|
||||
GOOGLE_PRESENTATION("mfx-google-presentation", '\uE93E'),
|
||||
GOOGLE_SCRIPT("mfx-google-script", '\uE93F'),
|
||||
GOOGLE_SITES("mfx-google-sites", '\uE940'),
|
||||
HOME("mfx-home", '\uE941'),
|
||||
HYPHEN("mfx-hyphen", '\uE942'),
|
||||
IMAGE("mfx-image", '\uE943'),
|
||||
INFO("mfx-info", '\uE944'),
|
||||
INFO_CIRCLE("mfx-info-circle", '\uE945'),
|
||||
INFO_CIRCLE_FILLED("mfx-info-circle-filled", '\uE946'),
|
||||
INPUT_PIPE("mfx-input-pipe", '\uE947'),
|
||||
INPUT_PIPE_ALT("mfx-input-pipe-alt", '\uE948'),
|
||||
LAST_PAGE("mfx-last-page", '\uE949'),
|
||||
LEVEL_UP("mfx-level-up", '\uE94A'),
|
||||
LIST_DROPDOWN("mfx-list-dropdown", '\uE94B'),
|
||||
LOCK("mfx-lock", '\uE94C'),
|
||||
LOCK_OPEN("mfx-lock-open", '\uE94D'),
|
||||
LOGO("mfx-logo", '\uE94E'),
|
||||
LOGO_ALT("mfx-logo-alt", '\uE94F'),
|
||||
MAP("mfx-map", '\uE950'),
|
||||
MESSAGE("mfx-message", '\uE951'),
|
||||
MESSAGES("mfx-messages", '\uE952'),
|
||||
MINUS("mfx-minus", '\uE953'),
|
||||
MINUS_CIRCLE("mfx-minus-circle", '\uE954'),
|
||||
MODENA_MARK("mfx-modena-mark", '\uE955'),
|
||||
MUSIC("mfx-music", '\uE956'),
|
||||
NEXT("mfx-next", '\uE957'),
|
||||
PLUS("mfx-plus", '\uE958'),
|
||||
PROGRESS_BARS("mfx-progress-bars", '\uE959'),
|
||||
PROGRESS_BARS_ALT("mfx-progress-bars-alt", '\uE95A'),
|
||||
REDO("mfx-redo", '\uE95B'),
|
||||
RESTORE("mfx-restore", '\uE95C'),
|
||||
SCROLL_BAR("mfx-scroll-bar", '\uE95D'),
|
||||
SEARCH("mfx-search", '\uE95E'),
|
||||
SEARCH_PLUS("mfx-search-plus", '\uE95F'),
|
||||
SELECT_ALL("mfx-select-all", '\uE960'),
|
||||
SHORTCUT("mfx-shortcut", '\uE961'),
|
||||
SIDEBAR_CLOSE("mfx-sidebar-close", '\uE962'),
|
||||
SIDEBAR_OPEN("mfx-sidebar-open", '\uE963'),
|
||||
SLIDERS("mfx-sliders", '\uE964'),
|
||||
SPREADSHEET("mfx-spreadsheet", '\uE965'),
|
||||
SQUARE_LIST("mfx-square-list", '\uE966'),
|
||||
STEP_BACKWARD("mfx-step-backward", '\uE967'),
|
||||
STEP_FORWARD("mfx-step-forward", '\uE968'),
|
||||
STEPPER("mfx-stepper", '\uE969'),
|
||||
SYNC("mfx-sync", '\uE96A'),
|
||||
SYNC_LIGHT("mfx-sync-light", '\uE96B'),
|
||||
TABLE("mfx-table", '\uE96C'),
|
||||
TABLE_ALT("mfx-table-alt", '\uE96D'),
|
||||
TOGGLE_OFF("mfx-toggle-off", '\uE96E'),
|
||||
TOGGLE_ON("mfx-toggle-on", '\uE96F'),
|
||||
UNDO("mfx-undo", '\uE970'),
|
||||
USER("mfx-user", '\uE971'),
|
||||
USERS("mfx-users", '\uE972'),
|
||||
VARIANT10_MARK("mfx-variant10-mark", '\uE973'),
|
||||
VARIANT11_MARK("mfx-variant11-mark", '\uE974'),
|
||||
VARIANT12_MARK("mfx-variant12-mark", '\uE975'),
|
||||
VARIANT13_MARK("mfx-variant13-mark", '\uE976'),
|
||||
VARIANT14_MARK("mfx-variant14-mark", '\uE977'),
|
||||
VARIANT3_MARK("mfx-variant3-mark", '\uE978'),
|
||||
VARIANT4_MARK("mfx-variant4-mark", '\uE979'),
|
||||
VARIANT5_MARK("mfx-variant5-mark", '\uE97A'),
|
||||
VARIANT6_MARK("mfx-variant6-mark", '\uE97B'),
|
||||
VARIANT7_MARK("mfx-variant7-mark", '\uE97C'),
|
||||
VARIANT8_MARK("mfx-variant8-mark", '\uE97D'),
|
||||
VARIANT9_MARK("mfx-variant9-mark", '\uE97E'),
|
||||
VIDEO("mfx-video", '\uE97F'),
|
||||
X("mfx-x", '\uE980'),
|
||||
X_ALT("mfx-x-alt", '\uE981'),
|
||||
X_CIRCLE("mfx-x-circle", '\uE982'),
|
||||
X_CIRCLE_LIGHT("mfx-x-circle-light", '\uE983'),
|
||||
X_LIGHT("mfx-x-light", '\uE984');
|
||||
CIRCLE_CHEVRON_DOWN("mfx-circle-chevron-down", '\uE91F'),
|
||||
CIRCLE_CHEVRON_LEFT("mfx-circle-chevron-left", '\uE920'),
|
||||
CIRCLE_CHEVRON_RIGHT("mfx-circle-chevron-right", '\uE921'),
|
||||
CIRCLE_CHEVRON_UP("mfx-circle-chevron-up", '\uE922'),
|
||||
CIRCLE_DOT("mfx-circle-dot", '\uE923'),
|
||||
CIRCLE_EMPTY("mfx-circle-empty", '\uE924'),
|
||||
CONTENT_COPY("mfx-content-copy", '\uE925'),
|
||||
CONTENT_CUT("mfx-content-cut", '\uE926'),
|
||||
CONTENT_PASTE("mfx-content-paste", '\uE927'),
|
||||
CSS("mfx-css", '\uE928'),
|
||||
CSS_ALT("mfx-css-alt", '\uE929'),
|
||||
DEBUG("mfx-debug", '\uE92A'),
|
||||
DELETE("mfx-delete", '\uE92B'),
|
||||
DELETE_ALT("mfx-delete-alt", '\uE92C'),
|
||||
DO_NOT_ENTER_CIRCLE("mfx-do-not-enter-circle", '\uE92D'),
|
||||
ELLIPSIS_VERTICAL("mfx-ellipsis-vertical", '\uE92E'),
|
||||
EXCLAMATION_CIRCLE("mfx-exclamation-circle", '\uE92F'),
|
||||
EXCLAMATION_CIRCLE_FILLED("mfx-exclamation-circle-filled", '\uE930'),
|
||||
EXCLAMATION_TRIANGLE("mfx-exclamation-triangle", '\uE931'),
|
||||
EXPAND("mfx-expand", '\uE932'),
|
||||
EYE("mfx-eye", '\uE933'),
|
||||
EYE_SLASH("mfx-eye-slash", '\uE934'),
|
||||
FILE("mfx-file", '\uE935'),
|
||||
FILES("mfx-files", '\uE936'),
|
||||
FILTER("mfx-filter", '\uE937'),
|
||||
FILTER_ALT("mfx-filter-alt", '\uE938'),
|
||||
FILTER_ALT_CLEAR("mfx-filter-alt-clear", '\uE939'),
|
||||
FIRST_PAGE("mfx-first-page", '\uE93A'),
|
||||
FIT("mfx-fit", '\uE93B'),
|
||||
FOLDER("mfx-folder", '\uE93C'),
|
||||
FONTICONS("mfx-fonticons", '\uE93D'),
|
||||
FUNCTION("mfx-function", '\uE93E'),
|
||||
FUNCTION_LIGHT("mfx-function-light", '\uE93F'),
|
||||
GEAR("mfx-gear", '\uE940'),
|
||||
GEAR_ALT("mfx-gear-alt", '\uE941'),
|
||||
GEARS("mfx-gears", '\uE942'),
|
||||
GOOGLE("mfx-google", '\uE943'),
|
||||
GOOGLE_DRAWING("mfx-google-drawing", '\uE944'),
|
||||
GOOGLE_DRIVE("mfx-google-drive", '\uE945'),
|
||||
GOOGLE_FORMS("mfx-google-forms", '\uE946'),
|
||||
GOOGLE_FUSION_TABLES("mfx-google-fusion-tables", '\uE947'),
|
||||
GOOGLE_PRESENTATION("mfx-google-presentation", '\uE948'),
|
||||
GOOGLE_SCRIPT("mfx-google-script", '\uE949'),
|
||||
GOOGLE_SITES("mfx-google-sites", '\uE94A'),
|
||||
HOME("mfx-home", '\uE94B'),
|
||||
HYPHEN("mfx-hyphen", '\uE94C'),
|
||||
IMAGE("mfx-image", '\uE94D'),
|
||||
INFO("mfx-info", '\uE94E'),
|
||||
INFO_CIRCLE("mfx-info-circle", '\uE94F'),
|
||||
INFO_CIRCLE_LIGHT("mfx-info-circle-light", '\uE950'),
|
||||
INFO_SQUARE("mfx-info-square", '\uE951'),
|
||||
INFO_SQUARE_LIGHT("mfx-info-square-light", '\uE952'),
|
||||
INPUT_PIPE("mfx-input-pipe", '\uE953'),
|
||||
INPUT_PIPE_ALT("mfx-input-pipe-alt", '\uE954'),
|
||||
LAST_PAGE("mfx-last-page", '\uE955'),
|
||||
LAYER_GROUP("mfx-layer-group", '\uE956'),
|
||||
LAYER_GROUP_LIGHT("mfx-layer-group-light", '\uE957'),
|
||||
LAYOUTS_ALT("mfx-layouts-alt", '\uE958'),
|
||||
LAYOUTS_ALT_LIGHT("mfx-layouts-alt-light", '\uE959'),
|
||||
LEVEL_UP("mfx-level-up", '\uE95A'),
|
||||
LIST_DROPDOWN("mfx-list-dropdown", '\uE95B'),
|
||||
LOCK("mfx-lock", '\uE95C'),
|
||||
LOCK_OPEN("mfx-lock-open", '\uE95D'),
|
||||
LOGO("mfx-logo", '\uE95E'),
|
||||
LOGO_ALT("mfx-logo-alt", '\uE95F'),
|
||||
MAGNIFYING_GLASS("mfx-magnifying-glass", '\uE960'),
|
||||
MAGNIFYING_GLASS_LIGHT("mfx-magnifying-glass-light", '\uE961'),
|
||||
MAGNIFYING_GLASS_MINUS("mfx-magnifying-glass-minus", '\uE962'),
|
||||
MAGNIFYING_GLASS_MINUS_LIGHT("mfx-magnifying-glass-minus-light", '\uE963'),
|
||||
MAGNIFYING_GLASS_PLUS("mfx-magnifying-glass-plus", '\uE964'),
|
||||
MAGNIFYING_GLASS_PLUS_LIGHT("mfx-magnifying-glass-plus-light", '\uE965'),
|
||||
MAP("mfx-map", '\uE966'),
|
||||
MENU_V1("mfx-menu-v1", '\uE967'),
|
||||
MENU_V2("mfx-menu-v2", '\uE968'),
|
||||
MENU_V3("mfx-menu-v3", '\uE969'),
|
||||
MENU_V3_LIGHT("mfx-menu-v3-light", '\uE96A'),
|
||||
MESSAGE("mfx-message", '\uE96B'),
|
||||
MESSAGES("mfx-messages", '\uE96C'),
|
||||
MINUS("mfx-minus", '\uE96D'),
|
||||
MINUS_CIRCLE("mfx-minus-circle", '\uE96E'),
|
||||
MODENA_MARK("mfx-modena-mark", '\uE96F'),
|
||||
MUSIC("mfx-music", '\uE970'),
|
||||
NEXT("mfx-next", '\uE971'),
|
||||
PEN_FIELD("mfx-pen-field", '\uE972'),
|
||||
PEN_FIELD_LIGHT("mfx-pen-field-light", '\uE973'),
|
||||
PLUS("mfx-plus", '\uE974'),
|
||||
PROGRESS_BARS("mfx-progress-bars", '\uE975'),
|
||||
PROGRESS_BARS_ALT("mfx-progress-bars-alt", '\uE976'),
|
||||
REDO("mfx-redo", '\uE977'),
|
||||
RESTORE("mfx-restore", '\uE978'),
|
||||
SCROLL_BAR("mfx-scroll-bar", '\uE979'),
|
||||
SELECT_ALL("mfx-select-all", '\uE97A'),
|
||||
SHORTCUT("mfx-shortcut", '\uE97B'),
|
||||
SIDEBAR_CLOSE("mfx-sidebar-close", '\uE97C'),
|
||||
SIDEBAR_OPEN("mfx-sidebar-open", '\uE97D'),
|
||||
SLIDERS("mfx-sliders", '\uE97E'),
|
||||
SPARKLES("mfx-sparkles", '\uE97F'),
|
||||
SPARKLES_LIGHT("mfx-sparkles-light", '\uE980'),
|
||||
SPREADSHEET("mfx-spreadsheet", '\uE981'),
|
||||
SQUARE_CHEVRON_DOWN("mfx-square-chevron-down", '\uE982'),
|
||||
SQUARE_CHEVRON_LEFT("mfx-square-chevron-left", '\uE983'),
|
||||
SQUARE_CHEVRON_RIGHT("mfx-square-chevron-right", '\uE984'),
|
||||
SQUARE_CHEVRON_UP("mfx-square-chevron-up", '\uE985'),
|
||||
SQUARE_LIST("mfx-square-list", '\uE986'),
|
||||
SQUARE_PEN("mfx-square-pen", '\uE987'),
|
||||
SQUARE_PLUS("mfx-square-plus", '\uE988'),
|
||||
STEP_BACKWARD("mfx-step-backward", '\uE989'),
|
||||
STEP_FORWARD("mfx-step-forward", '\uE98A'),
|
||||
STEPPER("mfx-stepper", '\uE98B'),
|
||||
SYNC("mfx-sync", '\uE98C'),
|
||||
SYNC_LIGHT("mfx-sync-light", '\uE98D'),
|
||||
TABLE("mfx-table", '\uE98E'),
|
||||
TABLE_ALT("mfx-table-alt", '\uE98F'),
|
||||
TOGGLE_OFF("mfx-toggle-off", '\uE990'),
|
||||
TOGGLE_ON("mfx-toggle-on", '\uE991'),
|
||||
UNDO("mfx-undo", '\uE992'),
|
||||
USER("mfx-user", '\uE993'),
|
||||
USERS("mfx-users", '\uE994'),
|
||||
VARIANT10_MARK("mfx-variant10-mark", '\uE995'),
|
||||
VARIANT11_MARK("mfx-variant11-mark", '\uE996'),
|
||||
VARIANT12_MARK("mfx-variant12-mark", '\uE997'),
|
||||
VARIANT13_MARK("mfx-variant13-mark", '\uE998'),
|
||||
VARIANT14_MARK("mfx-variant14-mark", '\uE999'),
|
||||
VARIANT3_MARK("mfx-variant3-mark", '\uE99A'),
|
||||
VARIANT4_MARK("mfx-variant4-mark", '\uE99B'),
|
||||
VARIANT5_MARK("mfx-variant5-mark", '\uE99C'),
|
||||
VARIANT6_MARK("mfx-variant6-mark", '\uE99D'),
|
||||
VARIANT7_MARK("mfx-variant7-mark", '\uE99E'),
|
||||
VARIANT8_MARK("mfx-variant8-mark", '\uE99F'),
|
||||
VARIANT9_MARK("mfx-variant9-mark", '\uE9A0'),
|
||||
VIDEO("mfx-video", '\uE9A1'),
|
||||
X("mfx-x", '\uE9A2'),
|
||||
X_ALT("mfx-x-alt", '\uE9A3'),
|
||||
X_CIRCLE("mfx-x-circle", '\uE9A4'),
|
||||
X_CIRCLE_LIGHT("mfx-x-circle-light", '\uE9A5'),
|
||||
X_LIGHT("mfx-x-light", '\uE9A6');
|
||||
|
||||
public static FontResources findByDescription(String description) {
|
||||
for (FontResources font : values()) {
|
||||
|
@ -173,7 +173,7 @@ public class MFXRectangleToggleNodeSkin extends SkinBase<MFXRectangleToggleNode>
|
||||
// Override Methods
|
||||
//================================================================================
|
||||
@Override
|
||||
protected double computeMinWidth(double height, double topInset, double rightInset, double bottomInset, double leftInset) {
|
||||
protected double computePrefWidth(double height, double topInset, double rightInset, double bottomInset, double leftInset) {
|
||||
MFXRectangleToggleNode toggleNode = getSkinnable();
|
||||
Node graphic = toggleNode.getGraphic();
|
||||
if (graphic != null) {
|
||||
@ -192,6 +192,16 @@ public class MFXRectangleToggleNodeSkin extends SkinBase<MFXRectangleToggleNode>
|
||||
rightInset;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected double computePrefHeight(double width, double topInset, double rightInset, double bottomInset, double leftInset) {
|
||||
MFXRectangleToggleNode toggleNode = getSkinnable();
|
||||
Node graphic = toggleNode.getGraphic();
|
||||
if (graphic != null) {
|
||||
return topInset + graphic.prefHeight(-1) + bottomInset;
|
||||
}
|
||||
return super.computePrefHeight(width, topInset, rightInset, bottomInset, leftInset);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected double computeMaxWidth(double height, double topInset, double rightInset, double bottomInset, double leftInset) {
|
||||
return getSkinnable().prefWidth(-1);
|
||||
|
@ -19,9 +19,14 @@
|
||||
package io.github.palexdev.materialfx.skins;
|
||||
|
||||
import io.github.palexdev.materialfx.controls.MFXScrollPane;
|
||||
import javafx.geometry.Orientation;
|
||||
import javafx.scene.Node;
|
||||
import javafx.scene.control.ScrollBar;
|
||||
import javafx.scene.control.skin.ScrollPaneSkin;
|
||||
import javafx.scene.layout.StackPane;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Skin used for {@link MFXScrollPane}, this class' purpose is to
|
||||
* fix a bug of ScrollPanes' viewport which makes the content blurry.
|
||||
@ -34,5 +39,13 @@ public class MFXScrollPaneSkin extends ScrollPaneSkin {
|
||||
super(scrollPane);
|
||||
StackPane viewPort = (StackPane) scrollPane.lookup(".viewport");
|
||||
viewPort.setCache(false);
|
||||
|
||||
Set<Node> nodes = scrollPane.lookupAll(".scroll-bar");
|
||||
nodes.forEach(node -> {
|
||||
if (node instanceof ScrollBar) {
|
||||
ScrollBar sb = ((ScrollBar) node);
|
||||
sb.getStyleClass().add(sb.getOrientation() == Orientation.VERTICAL ? "vBar" : "hBar");
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,282 @@
|
||||
/*
|
||||
* Copyright (C) 2022 Parisi Alessandro
|
||||
* This file is part of MaterialFX (https://github.com/palexdev/MaterialFX).
|
||||
*
|
||||
* MaterialFX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* MaterialFX 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 Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with MaterialFX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.github.palexdev.materialfx.skins;
|
||||
|
||||
import io.github.palexdev.materialfx.controls.MFXTitledPane;
|
||||
import io.github.palexdev.materialfx.effects.Interpolators;
|
||||
import io.github.palexdev.materialfx.enums.HeaderPosition;
|
||||
import io.github.palexdev.materialfx.utils.AnimationUtils.KeyFrames;
|
||||
import io.github.palexdev.materialfx.utils.AnimationUtils.TimelineBuilder;
|
||||
import io.github.palexdev.materialfx.utils.ExecutionUtils;
|
||||
import javafx.beans.property.DoubleProperty;
|
||||
import javafx.beans.value.ObservableValue;
|
||||
import javafx.scene.Node;
|
||||
import javafx.scene.control.SkinBase;
|
||||
import javafx.scene.input.MouseEvent;
|
||||
import javafx.scene.layout.BorderPane;
|
||||
import javafx.scene.layout.Region;
|
||||
import javafx.scene.layout.StackPane;
|
||||
import javafx.scene.shape.Rectangle;
|
||||
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* Default skin implementation used by {@link MFXTitledPane}.
|
||||
* <p></p>
|
||||
* It consists in three main nodes:
|
||||
* <p> - A {@link BorderPane} which is the top container, automatically manages the layout
|
||||
* for use making things way easier. It is also very convenient for the {@link MFXTitledPane#headerPosProperty()} feature,
|
||||
* see {@link #updatePane()}.
|
||||
* <p> - A {@link StackPane} which contains the {@link MFXTitledPane#contentProperty()}
|
||||
* <p> - A {@link Rectangle} to clip the above {@code StackPane}
|
||||
* <p></p>
|
||||
* Since the header position can change, the whole system needs to change as well. What I mean, is:
|
||||
* if the header is at the TOP or BOTTOM then we want to work with the content pane's prefHeight,
|
||||
* otherwise we want to work with the content pane's prefWidth.
|
||||
* This deeply influences both the clip and the expand/collapse code.
|
||||
* <p></p>
|
||||
* For this reason we use a smart system with three functions:
|
||||
* <p> - A {@link Supplier} which gives us the content pane's prefWidth/prefHeight property (sizeSupplier)
|
||||
* <p> - A {@link Supplier} which gives us the content's prefWidth/prefHeight (targetSizeSupplier)
|
||||
* <p> - A {@link Consumer} which accepts a target prefWidth/prefHeight and sets it on the content pane (setter)
|
||||
*/
|
||||
public class MFXTitledPaneSkin extends SkinBase<MFXTitledPane> {
|
||||
//================================================================================
|
||||
// Properties
|
||||
//================================================================================
|
||||
private final BorderPane bp;
|
||||
private final StackPane contentPane;
|
||||
private final Rectangle clip;
|
||||
|
||||
private Node header;
|
||||
private Node content;
|
||||
private Supplier<DoubleProperty> sizeSupplier;
|
||||
private Supplier<Double> targetSizeSupplier;
|
||||
private Consumer<Double> setter;
|
||||
|
||||
//================================================================================
|
||||
// Constructors
|
||||
//================================================================================
|
||||
public MFXTitledPaneSkin(MFXTitledPane pane) {
|
||||
super(pane);
|
||||
|
||||
header = pane.getHeaderSupplier().get();
|
||||
content = pane.getContent();
|
||||
|
||||
contentPane = new StackPane();
|
||||
contentPane.getStyleClass().add("content-pane");
|
||||
if (content != null) contentPane.getChildren().add(content);
|
||||
|
||||
clip = new Rectangle();
|
||||
clip();
|
||||
|
||||
bp = new BorderPane();
|
||||
bp.setCenter(contentPane);
|
||||
updateSuppliers();
|
||||
updatePane();
|
||||
getChildren().setAll(bp);
|
||||
|
||||
addListeners();
|
||||
}
|
||||
|
||||
//================================================================================
|
||||
// Methods
|
||||
//================================================================================
|
||||
|
||||
/**
|
||||
* Adds the following listeners/handlers:
|
||||
* <p> - A listener to the {@link MFXTitledPane#expandedProperty()} to call {@link #expandCollapse()}
|
||||
* <p> - A listener to the {@link MFXTitledPane#collapsibleProperty()} to call {@link #expandCollapse()}
|
||||
* <p> - A listener to the {@link MFXTitledPane#headerSupplierProperty()} to call {@link #updatePane()}
|
||||
* <p> - A listener to the {@link MFXTitledPane#contentProperty()} to update the content pane and call {@link #expandCollapse()}
|
||||
* <p> - A listener to the {@link MFXTitledPane#headerPosProperty()} to call {@link #updateSuppliers()}, {@link #clip()}, {@link #updatePane()} and {@link #expandCollapse()}
|
||||
* <p> - A MouseEvent.MOUSE_PRESSED event handler to acquire focus
|
||||
* <p></p>
|
||||
* There's also a call to {@link ExecutionUtils#executeWhen(ObservableValue, BiConsumer, boolean, BiFunction, boolean)},
|
||||
* which triggers when the content pane is laid out and calls {@link #expandCollapse()} to initialize the control.
|
||||
*/
|
||||
private void addListeners() {
|
||||
MFXTitledPane pane = getSkinnable();
|
||||
|
||||
pane.addEventHandler(MouseEvent.MOUSE_PRESSED, event -> pane.requestFocus());
|
||||
|
||||
pane.expandedProperty().addListener(invalidated -> expandCollapse());
|
||||
pane.collapsibleProperty().addListener(invalidated -> expandCollapse());
|
||||
pane.headerSupplierProperty().addListener((observable, oldValue, newValue) -> {
|
||||
bp.getChildren().remove(header);
|
||||
header = newValue.get();
|
||||
updatePane();
|
||||
});
|
||||
pane.contentProperty().addListener((observable, oldValue, newValue) -> {
|
||||
content = newValue;
|
||||
if (newValue != null) contentPane.getChildren().setAll(newValue);
|
||||
expandCollapse();
|
||||
});
|
||||
pane.headerPosProperty().addListener(invalidated -> {
|
||||
updateSuppliers();
|
||||
clip();
|
||||
updatePane();
|
||||
expandCollapse();
|
||||
});
|
||||
|
||||
ExecutionUtils.executeWhen(
|
||||
contentPane.layoutBoundsProperty(),
|
||||
(oldValue, newValue) -> expandCollapse(),
|
||||
false,
|
||||
(oldValue, newValue) -> newValue.getWidth() != 0 || newValue.getHeight() != 0,
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Responsible for updating the sizes' functions when {@link MFXTitledPane#headerPosProperty()} changes.
|
||||
* <p></p>
|
||||
* Case RIGHT, LEFT:
|
||||
* <p> - Pref Height set to {@link Region#USE_COMPUTED_SIZE}, sizeSupplier set to {@code contentPane::prefWidthProperty},
|
||||
* targetSizeSupplier set to {@code content.prefWidth(-1)} (plus insets), setter set to {@code contentPane::setPrefWidth}
|
||||
* Case TOP, BOTTOM:
|
||||
* <p> - Pref Width set to {@link Region#USE_COMPUTED_SIZE}, sizeSupplier set to {@code contentPane::prefHeightProperty},
|
||||
* targetSizeSupplier set to {@code content.prefHeight(-1)} (plus insets), setter set to {@code contentPane::setPrefHeight}
|
||||
*/
|
||||
private void updateSuppliers() {
|
||||
MFXTitledPane pane = getSkinnable();
|
||||
HeaderPosition position = pane.getHeaderPos();
|
||||
if (position == HeaderPosition.RIGHT || position == HeaderPosition.LEFT) {
|
||||
contentPane.setPrefHeight(Region.USE_COMPUTED_SIZE);
|
||||
sizeSupplier = contentPane::prefWidthProperty;
|
||||
targetSizeSupplier = () -> contentPane.snappedLeftInset() + content.prefWidth(-1) + contentPane.snappedRightInset();
|
||||
setter = contentPane::setPrefWidth;
|
||||
} else {
|
||||
contentPane.setPrefWidth(Region.USE_COMPUTED_SIZE);
|
||||
sizeSupplier = contentPane::prefHeightProperty;
|
||||
targetSizeSupplier = () -> contentPane.snappedTopInset() + content.prefHeight(-1) + contentPane.snappedBottomInset();
|
||||
setter = contentPane::setPrefHeight;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Responsible for updating the header position in the {@link BorderPane}
|
||||
* according to {@link MFXTitledPane#headerPosProperty()}.
|
||||
*/
|
||||
private void updatePane() {
|
||||
MFXTitledPane pane = getSkinnable();
|
||||
HeaderPosition position = pane.getHeaderPos();
|
||||
|
||||
switch (position) {
|
||||
case RIGHT:
|
||||
bp.setRight(header);
|
||||
contentPane.setMinSize(Region.USE_PREF_SIZE, Region.USE_COMPUTED_SIZE);
|
||||
break;
|
||||
case BOTTOM:
|
||||
bp.setBottom(header);
|
||||
contentPane.setMinSize(Region.USE_COMPUTED_SIZE, Region.USE_PREF_SIZE);
|
||||
break;
|
||||
case LEFT:
|
||||
bp.setLeft(header);
|
||||
contentPane.setMinSize(Region.USE_PREF_SIZE, Region.USE_COMPUTED_SIZE);
|
||||
break;
|
||||
case TOP:
|
||||
default:
|
||||
bp.setTop(header);
|
||||
contentPane.setMinSize(Region.USE_COMPUTED_SIZE, Region.USE_PREF_SIZE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Responsible for expanding/collapsing the content pane according to:
|
||||
* {@link MFXTitledPane#expandedProperty()}, {@link MFXTitledPane#collapsibleProperty()}, {@link MFXTitledPane#animatedProperty()}.
|
||||
* <p></p>
|
||||
* If the content is null the size is set to 0
|
||||
* <p>
|
||||
* If it's not expanded and it's not collapsible the size is set to {@code targetSizeSupplier.get()} and returns immediately.
|
||||
* <p></p>
|
||||
* Otherwise, the target size is computed according to the expand state, and the size is then set directly or by an animation,
|
||||
* the opacity is also animated.
|
||||
*/
|
||||
private void expandCollapse() {
|
||||
if (content == null) {
|
||||
setter.accept(0.0);
|
||||
return;
|
||||
}
|
||||
|
||||
MFXTitledPane pane = getSkinnable();
|
||||
boolean isExpanded = pane.isExpanded();
|
||||
if (!isExpanded && !pane.isCollapsible()) {
|
||||
setter.accept(targetSizeSupplier.get());
|
||||
return;
|
||||
}
|
||||
|
||||
DoubleProperty property = sizeSupplier.get();
|
||||
double targetSize = isExpanded ? targetSizeSupplier.get() : 0;
|
||||
double targetOp = isExpanded ? 1.0 : 0.0;
|
||||
if (pane.isAnimated()) {
|
||||
TimelineBuilder.build()
|
||||
.add(KeyFrames.of(pane.getAnimationDuration(), contentPane.opacityProperty(), targetOp, Interpolators.INTERPOLATOR_V1))
|
||||
.add(KeyFrames.of(pane.getAnimationDuration(), property, targetSize, Interpolators.INTERPOLATOR_V1))
|
||||
.getAnimation()
|
||||
.play();
|
||||
} else {
|
||||
contentPane.setOpacity(targetOp);
|
||||
setter.accept(targetSize);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Responsible for clipping the content pane according to the
|
||||
* {@link MFXTitledPane#headerPosProperty()}.
|
||||
*/
|
||||
private void clip() {
|
||||
MFXTitledPane pane = getSkinnable();
|
||||
HeaderPosition position = pane.getHeaderPos();
|
||||
|
||||
contentPane.setClip(null);
|
||||
if (position == HeaderPosition.RIGHT || position == HeaderPosition.LEFT) {
|
||||
clip.widthProperty().bind(contentPane.prefWidthProperty());
|
||||
clip.heightProperty().bind(pane.heightProperty());
|
||||
} else {
|
||||
clip.widthProperty().bind(pane.widthProperty());
|
||||
clip.heightProperty().bind(contentPane.prefHeightProperty());
|
||||
}
|
||||
contentPane.setClip(clip);
|
||||
}
|
||||
|
||||
//================================================================================
|
||||
// Overridden Methods
|
||||
//================================================================================
|
||||
@Override
|
||||
protected double computeMaxWidth(double height, double topInset, double rightInset, double bottomInset, double leftInset) {
|
||||
HeaderPosition position = getSkinnable().getHeaderPos();
|
||||
if (position == HeaderPosition.RIGHT || position == HeaderPosition.LEFT) {
|
||||
return super.computePrefWidth(height, topInset, rightInset, bottomInset, leftInset);
|
||||
}
|
||||
return super.computeMaxWidth(height, topInset, rightInset, bottomInset, leftInset);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected double computeMaxHeight(double width, double topInset, double rightInset, double bottomInset, double leftInset) {
|
||||
HeaderPosition position = getSkinnable().getHeaderPos();
|
||||
if (position == HeaderPosition.TOP || position == HeaderPosition.BOTTOM) {
|
||||
return super.computePrefHeight(width, topInset, rightInset, bottomInset, leftInset);
|
||||
}
|
||||
return super.computeMaxHeight(width, topInset, rightInset, bottomInset, leftInset);
|
||||
}
|
||||
}
|
@ -128,7 +128,9 @@ public abstract class MFXLabeledSkinBase<C extends Labeled & MFXLabeled> extends
|
||||
BorderPane.setMargin(text, InsetsFactory.left(gap));
|
||||
break;
|
||||
}
|
||||
case GRAPHIC_ONLY:
|
||||
case GRAPHIC_ONLY: {
|
||||
break;
|
||||
}
|
||||
case CENTER: {
|
||||
topContainer.setCenter(controlContainer);
|
||||
BorderPane.setMargin(text, InsetsFactory.none());
|
||||
@ -175,8 +177,12 @@ public abstract class MFXLabeledSkinBase<C extends Labeled & MFXLabeled> extends
|
||||
minW = Math.max(getControlContainer().prefWidth(-1), text.prefWidth(-1));
|
||||
break;
|
||||
}
|
||||
case CENTER:
|
||||
case GRAPHIC_ONLY: {
|
||||
Node graphic = labeled.getGraphic();
|
||||
minW = (graphic != null) ? graphic.prefWidth(-1) : 0.0;
|
||||
break;
|
||||
}
|
||||
case CENTER: {
|
||||
minW = getControlContainer().prefWidth(-1);
|
||||
}
|
||||
}
|
||||
|
@ -79,11 +79,11 @@ public class PositionUtils {
|
||||
//================================================================================
|
||||
public static PositionBean computePosition(Region parent, Node child, double areaX, double areaY, double areaWidth, double areaHeight,
|
||||
double areaBaselineOffset, Insets margin, HPos hAlignment, VPos vAlignment) {
|
||||
return computePosition(parent, child, areaX, areaY, areaWidth, areaHeight, areaBaselineOffset, margin, hAlignment, vAlignment, true);
|
||||
return computePosition(parent, child, areaX, areaY, areaWidth, areaHeight, areaBaselineOffset, margin, hAlignment, vAlignment, true, true);
|
||||
}
|
||||
|
||||
public static PositionBean computePosition(Region parent, Node child, double areaX, double areaY, double areaWidth, double areaHeight,
|
||||
double areaBaselineOffset, Insets margin, HPos hAlignment, VPos vAlignment, boolean snapToPixel) {
|
||||
double areaBaselineOffset, Insets margin, HPos hAlignment, VPos vAlignment, boolean snapToPixel, boolean computeSizes) {
|
||||
|
||||
Insets snappedMargin = margin == null ? Insets.EMPTY : margin;
|
||||
if (snapToPixel) {
|
||||
@ -95,12 +95,12 @@ public class PositionUtils {
|
||||
);
|
||||
}
|
||||
|
||||
double xPosition = computeXPosition(parent, child, areaX, areaWidth, snappedMargin, false, hAlignment, snapToPixel);
|
||||
double yPosition = computeYPosition(parent, child, areaY, areaHeight, areaBaselineOffset, snappedMargin, false, vAlignment, snapToPixel);
|
||||
double xPosition = computeXPosition(parent, child, areaX, areaWidth, snappedMargin, false, hAlignment, snapToPixel, computeSizes);
|
||||
double yPosition = computeYPosition(parent, child, areaY, areaHeight, areaBaselineOffset, snappedMargin, false, vAlignment, snapToPixel, computeSizes);
|
||||
return PositionBean.of(xPosition, yPosition);
|
||||
}
|
||||
|
||||
public static double computeXPosition(Region parent, Node child, double areaX, double areaWidth, Insets margin, boolean snapMargin, HPos hAlignment, boolean snapToPixel) {
|
||||
public static double computeXPosition(Region parent, Node child, double areaX, double areaWidth, Insets margin, boolean snapMargin, HPos hAlignment, boolean snapToPixel, boolean computeSizes) {
|
||||
Insets snappedMargin = margin == null ? Insets.EMPTY : margin;
|
||||
if (snapMargin) {
|
||||
snappedMargin = InsetsFactory.of(
|
||||
@ -113,12 +113,12 @@ public class PositionUtils {
|
||||
|
||||
final double leftMargin = snappedMargin.getLeft();
|
||||
final double rightMargin = snappedMargin.getRight();
|
||||
final double xOffset = leftMargin + computeXOffset(areaWidth - leftMargin - rightMargin, child.getLayoutBounds().getWidth(), hAlignment);
|
||||
final double xOffset = leftMargin + computeXOffset(areaWidth - leftMargin - rightMargin, computeSizes ? child.prefWidth(-1) : child.getLayoutBounds().getWidth(), hAlignment);
|
||||
final double xPosition = areaX + xOffset;
|
||||
return snapToPixel ? parent.snapPositionX(xPosition) : xPosition;
|
||||
}
|
||||
|
||||
public static double computeYPosition(Region parent, Node child, double areaY, double areaHeight, double areaBaselineOffset, Insets margin, boolean snapMargin, VPos vAlignment, boolean snapToPixel) {
|
||||
public static double computeYPosition(Region parent, Node child, double areaY, double areaHeight, double areaBaselineOffset, Insets margin, boolean snapMargin, VPos vAlignment, boolean snapToPixel, boolean computeSizes) {
|
||||
Insets snappedMargin = margin == null ? Insets.EMPTY : margin;
|
||||
if (snapMargin) {
|
||||
snappedMargin = InsetsFactory.of(
|
||||
@ -135,13 +135,12 @@ public class PositionUtils {
|
||||
if (vAlignment == VPos.BASELINE) {
|
||||
double bo = child.getBaselineOffset();
|
||||
if (bo == Node.BASELINE_OFFSET_SAME_AS_HEIGHT) {
|
||||
// We already know the layout bounds at this stage, so we can use them
|
||||
yOffset = areaBaselineOffset - child.getLayoutBounds().getHeight();
|
||||
yOffset = areaBaselineOffset - (computeSizes ? child.prefHeight(-1) : child.getLayoutBounds().getHeight());
|
||||
} else {
|
||||
yOffset = areaBaselineOffset - bo;
|
||||
}
|
||||
} else {
|
||||
yOffset = topMargin + computeYOffset(areaHeight - topMargin - bottomMargin, child.getLayoutBounds().getHeight(), vAlignment);
|
||||
yOffset = topMargin + computeYOffset(areaHeight - topMargin - bottomMargin, computeSizes ? child.prefHeight(-1) : child.getLayoutBounds().getHeight(), vAlignment);
|
||||
}
|
||||
final double yPosition = areaY + yOffset;
|
||||
return snapToPixel ? parent.snapPositionY(yPosition) : yPosition;
|
||||
|
@ -28,7 +28,7 @@ import javafx.scene.input.MouseEvent;
|
||||
/**
|
||||
* Utils class for {@code ToggleButtons}.
|
||||
*/
|
||||
public class ToggleButtonsUtil {
|
||||
public class ToggleUtils {
|
||||
|
||||
private static final EventHandler<MouseEvent> consumeMouseEventFilter = (MouseEvent mouseEvent) -> {
|
||||
if (((Toggle) mouseEvent.getSource()).isSelected()) {
|
||||
@ -56,7 +56,7 @@ public class ToggleButtonsUtil {
|
||||
}
|
||||
}
|
||||
});
|
||||
toggleGroup.getToggles().forEach(ToggleButtonsUtil::addConsumeMouseEventFilter);
|
||||
toggleGroup.getToggles().forEach(ToggleUtils::addConsumeMouseEventFilter);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -74,4 +74,10 @@ public class ToggleButtonsUtil {
|
||||
}
|
||||
toggleGroup.selectToggle(null);
|
||||
}
|
||||
|
||||
public static void addTogglesTo(ToggleGroup tg, Toggle... toggles) {
|
||||
for (Toggle toggle : toggles) {
|
||||
toggle.setToggleGroup(tg);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,41 @@
|
||||
@import "Fonts.css";
|
||||
@import "MFXColors.css";
|
||||
@import "MFXScrollPane.css";
|
||||
|
||||
.mfx-titled-pane {
|
||||
-fx-background-color: white;
|
||||
-fx-background-radius: 6;
|
||||
-fx-border-color: lightgray;
|
||||
-fx-border-radius: 6;
|
||||
}
|
||||
|
||||
.mfx-titled-pane:focused {
|
||||
-fx-border-color: -mfx-purple;
|
||||
}
|
||||
|
||||
.mfx-titled-pane .header-pane {
|
||||
-fx-padding: 5;
|
||||
}
|
||||
|
||||
.mfx-titled-pane .header-pane .header-label {
|
||||
-fx-font-family: "Open Sans SemiBold";
|
||||
-fx-text-fill: -mfx-text-he;
|
||||
}
|
||||
|
||||
.mfx-titled-pane:focused .header-pane .mfx-icon-wrapper .mfx-font-icon {
|
||||
-mfx-color: -mfx-purple;
|
||||
}
|
||||
|
||||
.mfx-titled-pane .header-pane .mfx-icon-wrapper .mfx-ripple-generator {
|
||||
-mfx-ripple-color: derive(-mfx-purple, 145%);
|
||||
}
|
||||
|
||||
.mfx-titled-pane .content-pane {
|
||||
-fx-background-color: transparent;
|
||||
-fx-border-color: lightgray transparent transparent transparent;
|
||||
-fx-padding: 10 5 10 5;
|
||||
}
|
||||
|
||||
.mfx-titled-pane:focused .content-pane {
|
||||
-fx-border-color: -mfx-purple transparent transparent transparent;
|
||||
}
|
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user