Skip to content

Commit

Permalink
💥 Huge Update Part 3/3 [components, resources]
Browse files Browse the repository at this point in the history
[Components Module]
- Behaviors Package
:recycle: MFXCheckboxBehavior: replace ifs with EnumUtils.next(...) utility. Also now the states order has been changed as shown by M3 guidelines: UNSELECTED -> SELECTED -> INDETERMINATE

- Controls Package
:sparkles: MFXIconButton: added support for animated icon switching
:sparkles: MFXCheckbox: added support for animated state change as shown by M3 guidelines. Now the icon of SELECTED and INDETERMINATE state needs to be changed through two new properties since the skin has been modified as well for this

- Skins Package
:sparkles: MFXCheckboxSkin: implemented animations as shown by M3 guidelines. Now the icon is wrapped in a MFXIconWrapper
:sparkles: MFXIconButtonSkin: the icon is now contained by a MFXIconWrapper which is also used to play the icon switch animation
:adhesive_bandage: MFXCheckboxSkin and MFXIconButtonSkin: re-enabled ripple generation, oversight of recent update to Skin and Behavior APIs

[Resources Module]
- Fonts Package
:adhesive_bandage: MFXIconWrapper: for how it handles icons add/remove it may happen to cause an IllegalStateException if the change is not done on the application thread, this was not necessary before. Simply ensure we are on FX thread otherwise runLater

- Assets/Themes
:lipstick: _checkbox.scss: M3 guidelines show a CLIP animation when switching states. Also remove '-mfx-description' properties for the changes mentioned above

[Misc]
:memo: Added some TODOs as remainders

Signed-off-by: palexdev <alessandro.parisi406@gmailÃ.com>
  • Loading branch information
palexdev authored and palexdev committed Sep 24, 2023
1 parent 5dd024c commit 32d5cee
Show file tree
Hide file tree
Showing 19 changed files with 359 additions and 163 deletions.
6 changes: 6 additions & 0 deletions .idea/inspectionProfiles/Project_Default.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 6 additions & 1 deletion TODO.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,4 +55,9 @@
- [ ] TimePicker (and combined with date)
- [ ] Check Combo-boxes
- [ ] Split Button
- [ ] Progress Button
- [ ] Progress Button

#### Misc/Remainders

- StyleableProperties should be split. The ones dependent on the skin from the ones dependent on the control
- Resolve icon provider from a string description (through prefix)
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import io.github.palexdev.mfxcomponents.controls.checkbox.MFXCheckbox;
import io.github.palexdev.mfxcomponents.controls.checkbox.TriState;
import io.github.palexdev.mfxcore.selection.SelectionProperty;
import io.github.palexdev.mfxcore.utils.EnumUtils;
import javafx.event.ActionEvent;

/**
Expand Down Expand Up @@ -53,7 +54,7 @@ public MFXCheckboxBehavior(MFXCheckbox button) {
* <p> 2) The checkbox is {@code indeterminate}, sets the state to {@code selected}
* <p> 3) The checkbox is not selected, sets the state to {@code indeterminate}
* <p>
* In short, the cycle is: UNSELECTED -> INDETERMINATE (if allowed) -> SELECTED
* In short, the cycle is: UNSELECTED -> SELECTED -> INDETERMINATE (if allowed)
* <p></p>
* <b>Note:</b> this method will not invoke {@link MFXCheckbox#fire()}, as it is handled by the checkbox' {@link SelectionProperty},
* this is done to make {@link ActionEvent}s work also when the property is bound. I've not yet decided if this will
Expand All @@ -65,17 +66,10 @@ protected void handleSelection() {
if (checkBox.stateProperty().isBound()) return;

TriState oldState = checkBox.getState();
if (checkBox.isAllowIndeterminate()) {
if (oldState == TriState.INDETERMINATE) {
checkBox.setState(TriState.SELECTED);
return;
}
if (oldState == TriState.UNSELECTED) {
checkBox.setState(TriState.INDETERMINATE);
return;
}
}
checkBox.setState(oldState == TriState.UNSELECTED ? TriState.SELECTED : TriState.UNSELECTED);
TriState newState = EnumUtils.next(TriState.class, oldState);
if (newState == TriState.INDETERMINATE && !checkBox.isAllowIndeterminate())
newState = EnumUtils.next(TriState.class, newState);
checkBox.setState(newState);
// fire() is handled by the state property, to make bindings work too
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import io.github.palexdev.mfxresources.base.properties.IconProperty;
import io.github.palexdev.mfxresources.fonts.IconProvider;
import io.github.palexdev.mfxresources.fonts.MFXFontIcon;
import io.github.palexdev.mfxresources.fonts.MFXIconWrapper;
import javafx.css.CssMetaData;
import javafx.css.Styleable;
import javafx.css.StyleablePropertyFactory;
Expand Down Expand Up @@ -136,6 +137,13 @@ public MFXIconButton removeVariants(IconButtonVariants... variants) {
//================================================================================
// Styleable Properties
//================================================================================
private final StyleableBooleanProperty animated = new StyleableBooleanProperty(
StyleableProperties.ANIMATED,
this,
"animated",
true
);

private final StyleableBooleanProperty selectable = new StyleableBooleanProperty(
StyleableProperties.SELECTABLE,
this,
Expand All @@ -155,6 +163,23 @@ protected void invalidated() {
40.0
);

public boolean isAnimated() {
return animated.get();
}

/**
* Specifies whether to play an animation when switching icons.
* <p>
* In the default skin the animation is built and played by the {@link MFXIconWrapper} that contains the icons.
*/
public StyleableBooleanProperty animatedProperty() {
return animated;
}

public void setAnimated(boolean animated) {
this.animated.set(animated);
}

public boolean isSelectable() {
return selectable.get();
}
Expand Down Expand Up @@ -198,6 +223,13 @@ private static class StyleableProperties {
private static final StyleablePropertyFactory<MFXIconButton> FACTORY = new StyleablePropertyFactory<>(MFXSelectable.getClassCssMetaData());
private static final List<CssMetaData<? extends Styleable, ?>> cssMetaDataList;

private static final CssMetaData<MFXIconButton, Boolean> ANIMATED =
FACTORY.createBooleanCssMetaData(
"-mfx-animated",
MFXIconButton::animatedProperty,
true
);

private static final CssMetaData<MFXIconButton, Boolean> SELECTABLE =
FACTORY.createBooleanCssMetaData(
"-mfx-selectable",
Expand All @@ -215,7 +247,7 @@ private static class StyleableProperties {
static {
cssMetaDataList = StyleUtils.cssMetaDataList(
MFXSelectable.getClassCssMetaData(),
SELECTABLE, SIZE
ANIMATED, SELECTABLE, SIZE
);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,15 @@
import io.github.palexdev.mfxcomponents.skins.MFXCheckboxSkin;
import io.github.palexdev.mfxcomponents.theming.enums.PseudoClasses;
import io.github.palexdev.mfxcore.base.properties.styleable.StyleableBooleanProperty;
import io.github.palexdev.mfxcore.base.properties.styleable.StyleableStringProperty;
import io.github.palexdev.mfxcore.selection.Selectable;
import io.github.palexdev.mfxcore.selection.SelectionGroup;
import io.github.palexdev.mfxcore.utils.fx.SceneBuilderIntegration;
import io.github.palexdev.mfxeffects.utils.StyleUtils;
import io.github.palexdev.mfxresources.fonts.IconDescriptor;
import io.github.palexdev.mfxresources.fonts.MFXFontIcon;
import io.github.palexdev.mfxresources.fonts.MFXIconWrapper;
import io.github.palexdev.mfxresources.fonts.fontawesome.FontAwesomeSolid;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.css.CssMetaData;
Expand Down Expand Up @@ -73,6 +78,12 @@
* <p> 2) The {@link #selectedProperty()} is bound to the {@link #stateProperty()}. First of all, for this reason
* you won't be able to set it directly, {@link #setSelected(boolean)} is also overridden to change the {@link #stateProperty()}
* instead. Second, <b>don't try to unbind it</b>, it's just not meant to work like that.
* <p> 3) Previously the icon was changed according to the state by the theme. Now, since the checkbox is animated, the
* icon's description/identifier has been moved from the themes to code for one reason. Every time the state changes, a
* new icon is created and switched from the old one, this is because for the animation this uses the new API introduced
* by {@link MFXIconWrapper}. That being said, I didn't want to hard code it, so I added to properties to specify the icons
* in CSS: {@link #selectedIconProperty()} and {@link #indeterminateIconProperty()}. Also, keep in mind that you can even
* disable the animations via {@link #animatedProperty()} or change the animation in CSS, again see {@link MFXIconWrapper}.
*/
// TODO introduce validator
public class MFXCheckbox extends MFXSelectable<MFXCheckboxBehavior> {
Expand Down Expand Up @@ -158,6 +169,13 @@ protected void sceneBuilderIntegration() {
//================================================================================
// Styleable Properties
//================================================================================
private final StyleableBooleanProperty animated = new StyleableBooleanProperty(
StyleableProperties.ANIMATED,
this,
"animated",
true
);

private final StyleableBooleanProperty allowIndeterminate = new StyleableBooleanProperty(
StyleableProperties.ALLOW_INDETERMINATE,
this,
Expand All @@ -177,6 +195,35 @@ protected void invalidated() {
}
};

private final StyleableStringProperty selectedIcon = new StyleableStringProperty(
StyleableProperties.SELECTED_ICON,
this,
"selectedIcon",
"fas-check"
);

private final StyleableStringProperty indeterminateIcon = new StyleableStringProperty(
StyleableProperties.INDETERMINATE_ICON,
this,
"indeterminateBean",
"fas-minus"
);

public boolean isAnimated() {
return animated.get();
}

/**
* Specifies whether to play animations when the checkbox' state changes.
*/
public StyleableBooleanProperty animatedProperty() {
return animated;
}

public void setAnimated(boolean animated) {
this.animated.set(animated);
}

public boolean isAllowIndeterminate() {
return allowIndeterminate.get();
}
Expand All @@ -196,24 +243,81 @@ public void setAllowIndeterminate(boolean allowIndeterminate) {
this.allowIndeterminate.set(allowIndeterminate);
}

public String getSelectedIcon() {
return selectedIcon.get();
}

/**
* Specifies the {@link IconDescriptor} as a String, used to build a new {@link MFXFontIcon} when the checkbox is
* selected.
* <p>
* As of now, only {@link FontAwesomeSolid} are supported.
*/
public StyleableStringProperty selectedIconProperty() {
return selectedIcon;
}

public void setSelectedIcon(String selectedIcon) {
this.selectedIcon.set(selectedIcon);
}

public String getIndeterminateIcon() {
return indeterminateIcon.get();
}

/**
* Specifies the {@link IconDescriptor} as a String, used to build a new {@link MFXFontIcon} when the checkbox is
* indeterminate.
* <p>
* As of now, only {@link FontAwesomeSolid} are supported.
*/
public StyleableStringProperty indeterminateIconProperty() {
return indeterminateIcon;
}

public void setIndeterminateIcon(String indeterminateIcon) {
this.indeterminateIcon.set(indeterminateIcon);
}

//================================================================================
// CssMetaData
//================================================================================
private static class StyleableProperties {
private static final StyleablePropertyFactory<MFXCheckbox> FACTORY = new StyleablePropertyFactory<>(MFXSelectable.getClassCssMetaData());
private static final List<CssMetaData<? extends Styleable, ?>> cssMetaDataList;

private static final CssMetaData<MFXCheckbox, Boolean> ANIMATED =
FACTORY.createBooleanCssMetaData(
"-mfx-animated",
MFXCheckbox::animatedProperty,
true
);

private static final CssMetaData<MFXCheckbox, Boolean> ALLOW_INDETERMINATE =
FACTORY.createBooleanCssMetaData(
"-mfx-allow-indeterminate",
MFXCheckbox::allowIndeterminateProperty,
false
);

private static final CssMetaData<MFXCheckbox, String> SELECTED_ICON =
FACTORY.createStringCssMetaData(
"-mfx-selected-icon",
MFXCheckbox::selectedIconProperty,
"fas-check"
);

private static final CssMetaData<MFXCheckbox, String> INDETERMINATE_ICON =
FACTORY.createStringCssMetaData(
"-mfx-indeterminate-icon",
MFXCheckbox::indeterminateIconProperty,
"fas-minus"
);

static {
cssMetaDataList = StyleUtils.cssMetaDataList(
MFXSelectable.getClassCssMetaData(),
ALLOW_INDETERMINATE
ANIMATED, ALLOW_INDETERMINATE, SELECTED_ICON, INDETERMINATE_ICON
);
}
}
Expand Down
Loading

0 comments on commit 32d5cee

Please sign in to comment.