Support icon color and add icons page
This commit is contained in:
parent
c547b7231b
commit
0eee144d26
@ -5,6 +5,7 @@ import atlantafx.base.controls.Spacer;
|
||||
import atlantafx.base.theme.Styles;
|
||||
import atlantafx.sampler.page.Page;
|
||||
import atlantafx.sampler.page.components.*;
|
||||
import atlantafx.sampler.page.general.IconsPage;
|
||||
import atlantafx.sampler.page.general.ThemePage;
|
||||
import atlantafx.sampler.page.general.TypographyPage;
|
||||
import atlantafx.sampler.page.showcase.filemanager.FileManagerPage;
|
||||
@ -170,6 +171,7 @@ class Sidebar extends StackPane {
|
||||
caption("GENERAL"),
|
||||
navLink(ThemePage.NAME, ThemePage.class),
|
||||
navLink(TypographyPage.NAME, TypographyPage.class),
|
||||
navLink(IconsPage.NAME, IconsPage.class),
|
||||
caption("COMPONENTS"),
|
||||
navLink(OverviewPage.NAME, OverviewPage.class),
|
||||
navLink(InputGroupPage.NAME, InputGroupPage.class),
|
||||
|
@ -0,0 +1,128 @@
|
||||
package atlantafx.sampler.page.general;
|
||||
|
||||
import atlantafx.base.theme.Tweaks;
|
||||
import javafx.beans.property.SimpleObjectProperty;
|
||||
import javafx.beans.property.SimpleStringProperty;
|
||||
import javafx.scene.control.*;
|
||||
import org.kordamp.ikonli.Ikon;
|
||||
import org.kordamp.ikonli.javafx.FontIcon;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
import static atlantafx.base.theme.Styles.TEXT_SMALL;
|
||||
|
||||
public class IconBrowser extends TableView<List<Ikon>> {
|
||||
|
||||
static final int FILTER_LEN = 2;
|
||||
|
||||
private final int colNum;
|
||||
private final List<Ikon> icons;
|
||||
private final SimpleStringProperty filter = new SimpleStringProperty();
|
||||
|
||||
public IconBrowser(int colNum, List<Ikon> icons) {
|
||||
super();
|
||||
|
||||
if (colNum <= 0) {
|
||||
throw new IllegalArgumentException("Column count must be greater than 0.");
|
||||
}
|
||||
|
||||
if (icons == null || icons.isEmpty()) {
|
||||
throw new IllegalArgumentException("Icon list cannot be null or empty.");
|
||||
}
|
||||
|
||||
this.colNum = colNum;
|
||||
this.icons = icons;
|
||||
|
||||
filterProperty().addListener((obs, old, val) -> updateData(val));
|
||||
|
||||
initTable();
|
||||
updateData(null);
|
||||
}
|
||||
|
||||
public SimpleStringProperty filterProperty() {
|
||||
return filter;
|
||||
}
|
||||
|
||||
private void initTable() {
|
||||
for (int i = 0; i < colNum; i++) {
|
||||
var col = new TableColumn<List<Ikon>, Ikon>("col" + i);
|
||||
final int colIndex = i;
|
||||
col.setCellValueFactory(cb -> {
|
||||
var row = cb.getValue();
|
||||
var item = row.size() > colIndex ? row.get(colIndex) : null;
|
||||
return new SimpleObjectProperty<>(item);
|
||||
});
|
||||
col.setCellFactory(cb -> new FontIconCell());
|
||||
col.getStyleClass().add(Tweaks.ALIGN_CENTER);
|
||||
getColumns().add(col);
|
||||
}
|
||||
|
||||
setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
|
||||
getSelectionModel().setCellSelectionEnabled(true);
|
||||
getStyleClass().add("icon-browser");
|
||||
}
|
||||
|
||||
private void updateData(String filterString) {
|
||||
var displayedIcons = filterString == null || filterString.isBlank() || filterString.length() < FILTER_LEN
|
||||
? icons
|
||||
: icons.stream().filter(icon -> containsString(icon.getDescription(), filterString)).toList();
|
||||
|
||||
var data = partitionList(displayedIcons, colNum);
|
||||
getItems().setAll(data);
|
||||
}
|
||||
|
||||
private <T> Collection<List<T>> partitionList(List<T> list, int size) {
|
||||
List<List<T>> partitions = new ArrayList<>();
|
||||
if (list.size() == 0) {
|
||||
return partitions;
|
||||
}
|
||||
|
||||
int length = list.size();
|
||||
int numOfPartitions = length / size + ((length % size == 0) ? 0 : 1);
|
||||
|
||||
for (int i = 0; i < numOfPartitions; i++) {
|
||||
int from = i * size;
|
||||
int to = Math.min((i * size + size), length);
|
||||
partitions.add(list.subList(from, to));
|
||||
}
|
||||
return partitions;
|
||||
}
|
||||
|
||||
private boolean containsString(String s1, String s2) {
|
||||
return s1.toLowerCase(Locale.ROOT).contains(s2.toLowerCase(Locale.ROOT));
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public static class FontIconCell extends TableCell<List<Ikon>, Ikon> {
|
||||
|
||||
private final Label root = new Label();
|
||||
private final FontIcon fontIcon = new FontIcon();
|
||||
|
||||
public FontIconCell() {
|
||||
super();
|
||||
|
||||
root.setContentDisplay(ContentDisplay.TOP);
|
||||
root.setGraphic(fontIcon);
|
||||
root.setGraphicTextGap(10);
|
||||
root.getStyleClass().addAll("icon-label", TEXT_SMALL);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void updateItem(Ikon icon, boolean empty) {
|
||||
super.updateItem(icon, empty);
|
||||
|
||||
if (icon == null) {
|
||||
setGraphic(null);
|
||||
return;
|
||||
}
|
||||
|
||||
root.setText(icon.getDescription());
|
||||
fontIcon.setIconCode(icon);
|
||||
setGraphic(root);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,143 @@
|
||||
/* SPDX-License-Identifier: MIT */
|
||||
package atlantafx.sampler.page.general;
|
||||
|
||||
import atlantafx.base.controls.CustomTextField;
|
||||
import atlantafx.base.theme.Styles;
|
||||
import atlantafx.sampler.page.AbstractPage;
|
||||
import atlantafx.sampler.page.SampleBlock;
|
||||
import atlantafx.sampler.theme.CSSFragment;
|
||||
import javafx.geometry.Pos;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.layout.HBox;
|
||||
import javafx.scene.layout.Priority;
|
||||
import javafx.scene.layout.VBox;
|
||||
import javafx.scene.text.Text;
|
||||
import javafx.scene.text.TextFlow;
|
||||
import org.kordamp.ikonli.Ikon;
|
||||
import org.kordamp.ikonli.javafx.FontIcon;
|
||||
import org.kordamp.ikonli.javafx.StackedFontIcon;
|
||||
import org.kordamp.ikonli.material2.Material2AL;
|
||||
import org.kordamp.ikonli.material2.Material2MZ;
|
||||
import org.kordamp.ikonli.material2.Material2OutlinedAL;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
|
||||
import static atlantafx.sampler.page.SampleBlock.BLOCK_HGAP;
|
||||
import static atlantafx.sampler.page.SampleBlock.BLOCK_VGAP;
|
||||
import static atlantafx.sampler.util.Controls.hyperlink;
|
||||
|
||||
public class IconsPage extends AbstractPage {
|
||||
|
||||
public static final String NAME = "Icons";
|
||||
|
||||
@Override
|
||||
public String getName() { return NAME; }
|
||||
|
||||
public IconsPage() {
|
||||
super();
|
||||
createView();
|
||||
}
|
||||
|
||||
private void createView() {
|
||||
var headerText = new TextFlow(
|
||||
new Text("AtlantaFX supports "),
|
||||
hyperlink("Ikonli", URI.create("https://kordamp.org/ikonli")),
|
||||
new Text(" iconic fonts that can be used together with some JavaFX components.")
|
||||
);
|
||||
|
||||
var browserText = new TextFlow(
|
||||
new Text("There's a variety of icon packs. Sampler app uses "),
|
||||
hyperlink("Material Icons", URI.create("https://kordamp.org/ikonli/cheat-sheet-material2.html")),
|
||||
new Text(" you can preview below.")
|
||||
);
|
||||
|
||||
var filterText = new CustomTextField();
|
||||
filterText.setLeft(new FontIcon(Material2MZ.SEARCH));
|
||||
filterText.setPrefWidth(300);
|
||||
|
||||
var filterBox = new HBox(filterText);
|
||||
filterBox.setAlignment(Pos.CENTER);
|
||||
|
||||
var icons = new ArrayList<Ikon>();
|
||||
icons.addAll(Arrays.asList(Material2AL.values()));
|
||||
icons.addAll(Arrays.asList(Material2MZ.values()));
|
||||
|
||||
var iconBrowser = new IconBrowser(5, icons);
|
||||
VBox.setVgrow(iconBrowser, Priority.ALWAYS);
|
||||
iconBrowser.filterProperty().bind(filterText.textProperty());
|
||||
|
||||
setUserContent(new VBox(
|
||||
PAGE_VGAP,
|
||||
headerText,
|
||||
expandingHBox(colorSample(), stackingSample()),
|
||||
browserText,
|
||||
filterBox,
|
||||
iconBrowser
|
||||
));
|
||||
}
|
||||
|
||||
private SampleBlock colorSample() {
|
||||
var accentIcon = new FontIcon(Material2MZ.THUMB_UP);
|
||||
accentIcon.getStyleClass().add(Styles.ACCENT);
|
||||
|
||||
var successIcon = new FontIcon(Material2MZ.THUMB_UP);
|
||||
successIcon.getStyleClass().add(Styles.SUCCESS);
|
||||
|
||||
var warningIcon = new FontIcon(Material2MZ.THUMB_UP);
|
||||
warningIcon.getStyleClass().add(Styles.WARNING);
|
||||
|
||||
var dangerIcon = new FontIcon(Material2MZ.THUMB_UP);
|
||||
dangerIcon.getStyleClass().add(Styles.DANGER);
|
||||
|
||||
var content = new VBox(
|
||||
BLOCK_VGAP,
|
||||
new Label("You can also use pseudo-classes to set icon color."),
|
||||
new HBox(BLOCK_HGAP, accentIcon, successIcon, warningIcon, dangerIcon)
|
||||
);
|
||||
|
||||
return new SampleBlock("Colors", content);
|
||||
}
|
||||
|
||||
private SampleBlock stackingSample() {
|
||||
var outerIcon1 = new FontIcon(Material2OutlinedAL.BLOCK);
|
||||
outerIcon1.getStyleClass().add("outer-icon");
|
||||
|
||||
var innerIcon1 = new FontIcon(Material2MZ.PHOTO_CAMERA);
|
||||
innerIcon1.getStyleClass().add("inner-icon");
|
||||
|
||||
var stackIcon1 = new StackedFontIcon();
|
||||
stackIcon1.getChildren().addAll(innerIcon1, outerIcon1);
|
||||
new CSSFragment("""
|
||||
.stacked-ikonli-font-icon > .outer-icon {
|
||||
-fx-icon-size: 48px;
|
||||
-fx-icon-color: -color-danger-emphasis;
|
||||
}
|
||||
.stacked-ikonli-font-icon > .inner-icon {
|
||||
-fx-icon-size: 24px;
|
||||
}
|
||||
""").addTo(stackIcon1);
|
||||
|
||||
var outerIcon2 = new FontIcon(Material2OutlinedAL.CHECK_BOX_OUTLINE_BLANK);
|
||||
outerIcon2.getStyleClass().add("outer-icon");
|
||||
|
||||
var innerIcon2 = new FontIcon(Material2AL.LOCK);
|
||||
innerIcon2.getStyleClass().add("inner-icon");
|
||||
|
||||
var stackIcon2 = new StackedFontIcon();
|
||||
stackIcon2.getChildren().addAll(outerIcon2, innerIcon2);
|
||||
new CSSFragment("""
|
||||
.stacked-ikonli-font-icon > .outer-icon {
|
||||
-fx-icon-size: 48px;
|
||||
}
|
||||
.stacked-ikonli-font-icon > .inner-icon {
|
||||
-fx-icon-size: 24px;
|
||||
}
|
||||
""").addTo(stackIcon2);
|
||||
|
||||
var content = new HBox(BLOCK_HGAP, stackIcon1, stackIcon2);
|
||||
|
||||
return new SampleBlock("Stacking Icons", content);
|
||||
}
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
.icon-browser {
|
||||
|
||||
-fx-border-color: transparent;
|
||||
-color-cell-border: transparent;
|
||||
|
||||
>.column-header-background {
|
||||
-fx-max-height: 0;
|
||||
-fx-pref-height: 0;
|
||||
-fx-min-height: 0;
|
||||
}
|
||||
|
||||
.table-row-cell {
|
||||
-fx-cell-size: 80px;
|
||||
|
||||
.icon-label {
|
||||
-fx-padding: 10px;
|
||||
-fx-cursor: hand;
|
||||
}
|
||||
}
|
||||
|
||||
.ikonli-font-icon {
|
||||
-fx-icon-size: 36px;
|
||||
}
|
||||
}
|
@ -1,3 +1,4 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
@use "html-editor-fix"
|
||||
@use "html-editor-fix";
|
||||
@use "icon-browser";
|
@ -3,7 +3,8 @@
|
||||
@use "../settings/config" as cfg;
|
||||
@use "../settings/effects";
|
||||
|
||||
@each $level, $radius in cfg.$elevation {
|
||||
@each $level,
|
||||
$radius in cfg.$elevation {
|
||||
.elevated-#{$level} {
|
||||
@include effects.shadow(cfg.$elevation-color, $radius);
|
||||
}
|
||||
@ -12,3 +13,47 @@
|
||||
.interactive:hover {
|
||||
@include effects.shadow(cfg.$elevation-color, cfg.$elevation-interactive);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Ikonli //
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
.ikonli-font-icon.accent {
|
||||
-fx-fill: -color-accent-emphasis;
|
||||
-fx-icon-color: -color-accent-emphasis;
|
||||
}
|
||||
|
||||
.ikonli-font-icon.success {
|
||||
-fx-fill: -color-success-emphasis;
|
||||
-fx-icon-color: -color-success-emphasis;
|
||||
}
|
||||
|
||||
.ikonli-font-icon.warning {
|
||||
-fx-fill: -color-warning-emphasis;
|
||||
-fx-icon-color: -color-warning-emphasis;
|
||||
}
|
||||
|
||||
.ikonli-font-icon.danger {
|
||||
-fx-fill: -color-danger-emphasis;
|
||||
-fx-icon-color: -color-danger-emphasis;
|
||||
}
|
||||
|
||||
.ikonli-font-icon:accent {
|
||||
-fx-fill: -color-accent-emphasis;
|
||||
-fx-icon-color: -color-accent-emphasis;
|
||||
}
|
||||
|
||||
.ikonli-font-icon:success {
|
||||
-fx-fill: -color-success-emphasis;
|
||||
-fx-icon-color: -color-success-emphasis;
|
||||
}
|
||||
|
||||
.ikonli-font-icon:warning {
|
||||
-fx-fill: -color-warning-emphasis;
|
||||
-fx-icon-color: -color-warning-emphasis;
|
||||
}
|
||||
|
||||
.ikonli-font-icon:danger {
|
||||
-fx-fill: -color-danger-emphasis;
|
||||
-fx-icon-color: -color-danger-emphasis;
|
||||
}
|
Loading…
Reference in New Issue
Block a user