我正在开发一个 JavaFX 项目,并希望将 decorator 设计模式 应用到我的控制器以增强其功能。具体来说,我想向现有控制器添加额外的行为,而不直接修改其代码。
在 JavaFX 控制器中实现装饰器模式的最佳方法是什么? 在 JavaFX 上下文中应用此设计模式时是否有任何具体注意事项或最佳实践? 您能否提供一个简单的示例或代码片段来演示如何为 JavaFX 控制器创建装饰器?
我有不同的 FXML 文件和带有通用部分的控制器。共同部分:
@FXML
public Text Text1;
@FXML
public Text Text2;
@FXML
public TextField ID_IN;//Common ID id input field
@FXML
public Button Save; //Edit and Add Save button
@FXML
public Button Close;
@Override
public void showOutsideElements() {
ID_IN.setVisible(true);
Close.setVisible(true);
Save.setVisible(true);
Text1.setVisible(true);
Text2.setVisible(true);
}
@Override
public void hideOutsideElements() {
ID_IN.setVisible(false);
Close.setVisible(false);
Save.setVisible(false);
Text1.setVisible(false);
Text2.setVisible(false);
}
我想用装饰版本隐藏和显示这些元素。
@FXML
public void showOutsideElements() {
super.showOutsideElements();
SearchFile.setVisible(true);
AppRe_IN.setVisible(true);
}
@FXML
public void hideOutsideElements() {
super.hideOutsideElements();
SearchFile.setVisible(false);
AppRe_IN.setVisible(false);
}
使用这种设计模式,我的代码会更加结构化,但似乎 FXML 元素只能由控制器访问。
这是实际的代码:
这是我的基础控制器:
package main.tooldatabase.controller;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.collections.transformation.FilteredList;
import javafx.fxml.FXML;
import javafx.scene.control.*;
import javafx.scene.text.Text;
import java.sql.SQLException;
import java.util.Optional;
public abstract class BaseController<T> {
@FXML
protected TextField filterField; //Search field
//Input field titles
@FXML
protected Text Text1;
@FXML
protected Text Text2;
@FXML
protected Text Text3;
@FXML
protected Text Text4;
@FXML
protected Text Text5;
@FXML
protected Text Text6;
@FXML
protected Text Text7;
//Save button
@FXML
protected Button Save; //Edit and Add Save button
@FXML
protected Button Close;
//Tabels
@FXML
protected TableView<T> output_table;
@FXML
protected TableColumn<T, String> Actions;
protected boolean statusCode;
protected ObservableList<T> listData;
protected FilteredList<T> filteredData;
public BaseController() {
listData = FXCollections.observableArrayList();
Actions = new TableColumn<>("Actions");
Actions.setSortable(false);
//crudData = data;
}
public boolean isStatusCode() {
return statusCode;
}
public void setStatusCode(boolean statusCode) {
this.statusCode = statusCode;
}
protected void showData1(){
output_table.setItems(filteredData);
}
protected void showSaveConfirmation() {
Alert alert = new Alert(Alert.AlertType.INFORMATION);
alert.setTitle("Save Confirmation");
alert.setHeaderText(null);
alert.setContentText("Save operation completed successfully!");
alert.showAndWait();
}
protected boolean confirmDelete(T selectedItem) throws SQLException {
Alert confirmation = new Alert(Alert.AlertType.CONFIRMATION);
confirmation.setTitle("Delete Confirmation");
confirmation.setHeaderText("Confirm Deletion");
confirmation.setContentText("Are you sure you want to delete?");
Optional<ButtonType> result = confirmation.showAndWait();
if (result.isPresent() && result.get() == ButtonType.OK) {
return true;
}else{
return false;
}
}
// Abstract method for deletion
protected abstract void deleteSelectedItem(T selectedItem, String successMessage) throws SQLException;
// Method to show a success message after an action (deletion, saving, etc.)
protected void showSuccessMessage(String message) {
Alert successAlert = new Alert(Alert.AlertType.INFORMATION);
successAlert.setTitle("Success");
successAlert.setHeaderText(null);
successAlert.setContentText(message);
successAlert.showAndWait();
}
}
这是会员控制器:
package main.tooldatabase.controller;
import javafx.beans.property.ReadOnlyObjectWrapper;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.collections.transformation.FilteredList;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.*;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.layout.HBox;
import main.tooldatabase.database.implementations.MainTables.implMemberDatab;
import main.tooldatabase.database.implementations.MainTables.implProjectDatab;
import main.tooldatabase.database.implementations.SwitchTables.impM_AssignDatab;
import main.tooldatabase.database.models.MainTables.MemberModel;
import main.tooldatabase.database.models.SwitchTables.M_AssignModel;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.sql.SQLException;
import java.util.ResourceBundle;
public class MemberController extends BaseController<MemberModel> implements Initializable {
@FXML
private TextField Name_IN;
@FXML
private TextField ID_IN;
@FXML
private TableColumn<MemberModel, String> ID_OUT;
@FXML
private TableColumn<MemberModel, String> Name_OUT;
@FXML
private ComboBox<String> Position_IN;
@FXML
private TableColumn<MemberModel, String> Position_OUT;
@FXML
private ComboBox<String> Project_IN;
private final implMemberDatab crudData;
private final implProjectDatab ProjectData;
private final impM_AssignDatab attach;
ObservableList<String> PositionList;
public MemberController() {
super();
crudData = new implMemberDatab();
ProjectData = new implProjectDatab();
attach = new impM_AssignDatab();
ID_OUT=new TableColumn<>();
Name_OUT=new TableColumn<>();
Position_OUT=new TableColumn<>();
PositionList= FXCollections.observableArrayList("Position1","Position2","Position3");
}
@Override
protected void deleteSelectedItem(MemberModel selectedItem, String successMessage) throws SQLException {
}
@Override
public void initialize(URL url, ResourceBundle resourceBundle) {
ID_OUT.setCellValueFactory((TableColumn.CellDataFeatures<MemberModel, String> cellData)
->cellData.getValue().member_idProperty());
Name_OUT.setCellValueFactory((TableColumn.CellDataFeatures<MemberModel, String> cellData)
-> cellData.getValue().nameProperty());
Position_OUT.setCellValueFactory((TableColumn.CellDataFeatures<MemberModel, String> cellData)
-> cellData.getValue().positionProperty());
try {
filteredData = new FilteredList<>(crudData.getAll(), b -> true);
} catch (SQLException e) {
throw new RuntimeException(e);
}
TableColumn<MemberModel, Object> newColumn = new TableColumn<>("Project");
newColumn.setCellValueFactory(new PropertyValueFactory<>("propertyName"));
output_table.getColumns().add(newColumn);
try {
ObservableList<String> teszt =ProjectData.getNames();
} catch (SQLException e) {
throw new RuntimeException(e);
}
Actions.setCellValueFactory(new PropertyValueFactory<>("member_id"));
Actions.setCellFactory(param -> new TableCell<MemberModel, String>() {
final Button deleteButton = new Button("Delete");
final Button editButton = new Button("Edit"); // Hozzáadott Edit gomb
@Override
protected void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (empty) {
setGraphic(null);
setText(null);
} else {
// Ha nem üres a sor, akkor adj hozzá a gombot
deleteButton.setOnAction(event -> {
MemberModel selectedSample = getTableView().getItems().get(getIndex());
try {
onDeleteButtonClick(selectedSample);
} catch (SQLException e) {
throw new RuntimeException(e);
}
});
editButton.setOnAction(event -> {
MemberModel selectedSample = getTableView().getItems().get(getIndex());
onEditButtonClick(selectedSample); // Kérdéses az onEditButtonClick neve
});
HBox buttonsContainer = new HBox(deleteButton, editButton);
buttonsContainer.setSpacing(10); // Gombok közötti tér
setGraphic(buttonsContainer);
setText(null);
}
}
});
// Set the filter Predicate whenever the filter changes.
filterField.textProperty().addListener((observable, oldValue, newValue) -> {
filteredData.setPredicate(sample -> {
if (newValue == null || newValue.isEmpty()) {
return true;
}
String lowerCaseFilter = newValue.toLowerCase();
String first = "";
String second = "";
String third = "";
if(sample.getMember_id()!=null) {
first = sample.getMember_id().toLowerCase();
}
if(sample.getName()!=null) {
second = sample.getName().toLowerCase();
}
if(sample.getPosition()!=null) {
third = sample.getPosition().toLowerCase();
}
return first.contains(lowerCaseFilter) || second.contains(lowerCaseFilter)|| third.toLowerCase().contains(lowerCaseFilter);
});
});
output_table.getColumns().add(Actions);
statusCode = false;
try {
Project_IN.setItems(ProjectData.getNames());
} catch (SQLException e) {
throw new RuntimeException(e);
}
showData1();
newColumn.setCellValueFactory(cellData -> {
try {
return new ReadOnlyObjectWrapper<>(
ProjectData.getProject(cellData.getValue().getMember_id())
);
} catch (SQLException e) {
throw new RuntimeException(e);
}
});
output_table.getSelectionModel().clearSelection();
Position_IN.setItems(PositionList);
hideOutsideElements();
}
private void autoId() throws SQLException {
MemberModel m = new MemberModel();
crudData.autoId(m);
ID_IN.setText(m.getMember_id());
}
@FXML
void AddItem(ActionEvent event) throws SQLException {
showOutsideElements();
statusCode = true;
autoId();
filterField.setText("");
}
@FXML
void OnClose(ActionEvent event) {
hideOutsideElements();
}
@FXML
void OnSave(ActionEvent event) throws SQLException, InvocationTargetException, IllegalAccessException {
MemberModel ToSave = new MemberModel();
M_AssignModel ToAssign = new M_AssignModel();
ToSave.setMember_id(ID_IN.getText());
ToSave.setName(Name_IN.getText());
ToSave.setPosition(Position_IN.getValue());
ToAssign.setMember_id(ID_IN.getText());
ToAssign.setProject_id(ProjectData.getProjectID(Project_IN.getValue()));
if(statusCode){
crudData.insert(ToSave);
attach.insert(ToAssign);
}else {
crudData.update(ToSave);
if(attach.IsThereRecord(ToAssign)){
attach.update(ToAssign);
}else {
attach.insert(ToAssign);
}
}
hideOutsideElements();
output_table.setItems(crudData.getAll());
filterField.setText("");
}
private void onEditButtonClick(MemberModel selectedSample) {
showOutsideElements();
//Project_IN.setItems(ProjData.getProjectNames());
ID_IN.setText(selectedSample.getMember_id());
Name_IN.setText(selectedSample.getName());
Position_IN.setItems(PositionList);
Position_IN.setValue(selectedSample.getPosition());
statusCode = false;
}
private void onDeleteButtonClick(MemberModel selectedSample) throws SQLException {
M_AssignModel connect = new M_AssignModel();
connect.setMember_id(selectedSample.getMember_id());
attach.delete(connect);
crudData.delete(selectedSample);
filterField.setText("");
hideOutsideElements();
output_table.setItems(crudData.getAll());
}
@FXML
void hideOutsideElements() {
Name_IN.setVisible(false);
Position_IN.setVisible(false);
ID_IN.setVisible(false);
Project_IN.setVisible(false);
Close.setVisible(false);
Save.setVisible(false);
Text1.setVisible(false);
Text2.setVisible(false);
Text3.setVisible(false);
Text6.setVisible(false);
}
@FXML
void showOutsideElements() {
Name_IN.setVisible(true);
Position_IN.setVisible(true);
ID_IN.setVisible(true);
ID_IN.setDisable(true);
Project_IN.setVisible(true);
Close.setVisible(true);
Save.setVisible(true);
Text1.setVisible(true);
Text2.setVisible(true);
Text3.setVisible(true);
Text6.setVisible(true);
}
}
装配控制器:
package main.tooldatabase.controller;
import javafx.collections.ObservableList;
import javafx.collections.transformation.FilteredList;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Button;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TextField;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.layout.HBox;
import javafx.stage.FileChooser;
import main.tooldatabase.database.implementations.MainTables.implAssemblyDatab;
import main.tooldatabase.database.models.MainTables.AssemblyModel;
import main.tooldatabase.interfaces.interCRUD;
import java.awt.*;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.sql.SQLException;
import java.util.ResourceBundle;
public class AssemblyController extends BaseController<AssemblyModel> implements Initializable {
@FXML
private TextField AppRe_IN;
@FXML
private TableColumn<AssemblyModel, String> AppRe_OUT;
@FXML
private Button Close;
@FXML
private Button SearchFile;
@FXML
private TextField ID_IN;
@FXML
private TableColumn<AssemblyModel, String> ID_OUT;
//@FXML
// private ProgressBar progressBar; // Make sure to link ProgressBar in your FXML file
ObservableList<String> PhaseList;
private final interCRUD crudData = new implAssemblyDatab();
public AssemblyController() {
super();
ID_OUT=new TableColumn<>();
AppRe_OUT=new TableColumn<>();
FileChooser fileChooser = new FileChooser();
}
@Override
public void initialize(URL url, ResourceBundle resourceBundle) {
ID_OUT.setCellValueFactory((TableColumn.CellDataFeatures<AssemblyModel, String> cellData)
->cellData.getValue().assembly_idProperty());
AppRe_OUT.setCellValueFactory((TableColumn.CellDataFeatures<AssemblyModel, String> cellData)
-> cellData.getValue().application_releaseProperty());
try {
filteredData = new FilteredList<>(crudData.getAll(), b -> true);
} catch (SQLException e) {
throw new RuntimeException(e);
}
Actions.setCellValueFactory(new PropertyValueFactory<>("assembly_id"));
Actions.setCellFactory(param -> new TableCell<AssemblyModel, String>() {
final Button deleteButton = new Button("Delete");
final Button editButton = new Button("Edit"); // Hozzáadott Edit gomb
final Button openButton = new Button("Open");
@Override
protected void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (empty) {
setGraphic(null);
setText(null);
} else {
// Ha nem üres a sor, akkor adj hozzá a gombot
deleteButton.setOnAction(event -> {
AssemblyModel selectedSample = getTableView().getItems().get(getIndex());
try {
deleteSelectedItem(selectedSample,"Delete succes!");
} catch (SQLException e) {
throw new RuntimeException(e);
}
});
editButton.setOnAction(event -> {
AssemblyModel selectedSample = getTableView().getItems().get(getIndex());
onEditButtonClick(selectedSample); // Kérdéses az onEditButtonClick neve
});
openButton.setOnAction(event -> {
AssemblyModel selectedSample = getTableView().getItems().get(getIndex());
File pdfFile = new File(selectedSample.getApplication_release());
if (pdfFile != null) {
try {
Desktop.getDesktop().open(pdfFile);
} catch (IOException e) {
e.printStackTrace();
// Handle the exception (e.g., show an alert to the user)
}
} else {
System.out.println("No PDF file selected.");
// Optionally, show an alert or message to the user
}
});
HBox buttonsContainer = new HBox(openButton,deleteButton, editButton);
buttonsContainer.setSpacing(10); // Gombok közötti tér
setGraphic(buttonsContainer);
setText(null);
}
}
});
// Set the filter Predicate whenever the filter changes.
filterField.textProperty().addListener((observable, oldValue, newValue) -> {
filteredData.setPredicate(sample -> {
if (newValue == null || newValue.isEmpty()) {
return true;
}
String lowerCaseFilter = newValue.toLowerCase();
String first = sample.getAssembly_id().toLowerCase();
String second = sample.getApplication_release().toLowerCase();
return first.contains(lowerCaseFilter) || second.contains(lowerCaseFilter);
});
});
output_table.getColumns().add(Actions);
statusCode = false;
showData1();
output_table.getSelectionModel().clearSelection();
hideOutsideElements();
}
private void autoId() throws SQLException {
AssemblyModel m = new AssemblyModel();
crudData.autoId(m);
ID_IN.setText(m.getAssembly_id());
}
@FXML
void AddItem(ActionEvent event) throws SQLException {
showOutsideElements();
//Phase_IN.setItems(PhaseList);
//Project_IN.setItems(ProjData.getProjectNames());
statusCode = true;
autoId();
filterField.setText("");
}
@FXML
void OnClose(ActionEvent event) {
hideOutsideElements();
}
@FXML
void OnSave(ActionEvent event) throws SQLException, InvocationTargetException, IllegalAccessException {
AssemblyModel ToSave = new AssemblyModel();
ToSave.setAssembly_id(ID_IN.getText());
ToSave.setApplication_release(AppRe_IN.getText());
if(statusCode){
crudData.insert(ToSave);
}else {
crudData.update(ToSave);
}
hideOutsideElements();
output_table.setItems(crudData.getAll());
filterField.setText("");
showSaveConfirmation();
}
@FXML
void ChooseFile(ActionEvent event) {
FileChooser fileChooser = new FileChooser();
fileChooser.setTitle("Choose a PDF File");
// Állítsd be a megengedett fájltípusokat, ha szükséges
fileChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter("PDF Files", "*.pdf"));
File selectedFile = fileChooser.showOpenDialog(null);
if (selectedFile != null) {
// Itt megteheted, amit szeretnél a kiválasztott fájllal
System.out.println("Selected File: " + selectedFile.getAbsolutePath());
AppRe_IN.setText(selectedFile.getAbsolutePath());
// További logika a kiválasztott fájllal való munkához
}
}
private void onEditButtonClick(AssemblyModel selectedSample) {
showOutsideElements();
//Project_IN.setItems(ProjData.getProjectNames());
ID_IN.setText(selectedSample.getAssembly_id());
AppRe_IN.setText(selectedSample.getApplication_release());
statusCode = false;
}
private void onDeleteButtonClick(AssemblyModel selectedSample) throws SQLException {
crudData.delete(selectedSample);
filterField.setText("");
hideOutsideElements();
output_table.setItems(crudData.getAll());
}
@Override
protected void deleteSelectedItem(AssemblyModel selectedItem, String successMessage) throws SQLException {
// Perform the deletion
if(confirmDelete(selectedItem)) {
crudData.delete(selectedItem);
// Show success message after deletion
showSuccessMessage(successMessage);
}
// Refresh the table or update data after deletion if needed
output_table.setItems(crudData.getAll());
}
@FXML
void hideOutsideElements() {
ID_IN.setVisible(false);
AppRe_IN.setVisible(false);
Close.setVisible(false);
Save.setVisible(false);
SearchFile.setVisible(false);
Text1.setVisible(false);
Text6.setVisible(false);
}
@FXML
void showOutsideElements() {
ID_IN.setVisible(true);
AppRe_IN.setVisible(true);
AppRe_IN.setDisable(true);
Close.setVisible(true);
Save.setVisible(true);
SearchFile.setVisible(true);
Text1.setVisible(true);
Text6.setVisible(true);
}
}
我想为不同的控制器功能创建不同的装饰器,例如选择文件,并使用控制器特定元素扩展显示和隐藏方法。
我已经研究了装饰器模式的总体情况,但我正在寻找有关如何在 JavaFX 上下文中有效应用它的指导。任何见解或代码示例将不胜感激!
FWIW,这里是应用 Decorator 模式来装饰 JavaFX 中文本的示例。
我什至不会尝试扩展它以与 FXML 和控制器一起使用(我不确定这在任何情况下都是一个好主意)或改编您问题中的示例代码。您可以查看该问题的评论,了解有关使用 FXML 和控制器应用装饰器模式的一些想法。
如果您查看示例,它可能会为您提供一些关于是否、何时以及如何将装饰器模式应用到未来项目的见解。
我将提供没有太多注释的代码。如果您想了解有关装饰器模式的更多信息,那么网络上有许多资源可供您学习。代码本身是有关该主题的链接维基百科文章中概述的结构和层次结构的直接实现。该代码还应用了一些 JavaFX 功能(例如 CSS、场景图渲染和属性)。
应用程序将各种装饰器应用于文本标签节点,以根据用户与应用程序中样式工具栏的交互来实现不同的样式组合。
消息.java
import javafx.beans.property.StringProperty;
import javafx.scene.Node;
public interface Message {
String getCSSRules();
void applyStyle(String cssRules);
StringProperty textProperty();
Node getNode();
}
SimpleMessage.java
import javafx.beans.property.StringProperty;
import javafx.scene.Node;
import javafx.scene.control.Label;
public class SimpleMessage implements Message {
private static final String DEFAULT_STYLE = """
-fx-font-family: "sans-serif";
-fx-font-size: 25px;
-fx-text-alignment: center;
-fx-wrap-text: true;
-fx-text-fill: crimson;
-fx-background-color: lavenderblush;
""";
private static final String CSS_DATA_URL = "data:text/css,";
private static final String CSS_TEMPLATE = CSS_DATA_URL + // language=CSS
"""
.message {
%s
}
""";
private final Label label = new Label();
public SimpleMessage(String text) {
label.setText(text);
label.getStyleClass().add("message");
}
@Override
public String getCSSRules() {
return DEFAULT_STYLE;
}
@Override
public void applyStyle(String cssRules) {
System.out.println("Applying\n" + cssRules);
label.getStylesheets().setAll(
CSS_TEMPLATE.formatted(
cssRules
)
);
}
@Override
public StringProperty textProperty() {
return label.textProperty();
}
@Override
public Node getNode() {
return label;
}
}
MessageDecorator.java
import javafx.beans.property.StringProperty;
import javafx.scene.Node;
abstract public class MessageDecorator implements Message {
private final Message messageToBeDecorated;
public MessageDecorator(Message messageToBeDecorated) {
this.messageToBeDecorated = messageToBeDecorated;
}
@Override
public void applyStyle(String cssRules) {
messageToBeDecorated.applyStyle(cssRules);
}
@Override
public StringProperty textProperty() {
return messageToBeDecorated.textProperty();
}
@Override
public String getCSSRules() {
return messageToBeDecorated.getCSSRules();
}
@Override
public Node getNode() {
return messageToBeDecorated.getNode();
}
}
BoldMessageDecorator.java
public class BoldMessageDecorator extends MessageDecorator {
private static final String BOLD_STYLE = "-fx-font-weight: bold;\n";
public BoldMessageDecorator(Message messageToBeDecorated) {
super(messageToBeDecorated);
}
@Override
public String getCSSRules() {
return super.getCSSRules() + BOLD_STYLE;
}
}
ItalicMessageDecorator.java
public class ItalicMessageDecorator extends MessageDecorator {
private static final String ITALIC_STYLE = "-fx-font-style: italic;\n";
public ItalicMessageDecorator(Message messageToBeDecorated) {
super(messageToBeDecorated);
}
@Override
public String getCSSRules() {
return super.getCSSRules() + ITALIC_STYLE;
}
}
装饰应用程序
import javafx.application.Application;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Toggle;
import javafx.scene.control.ToggleButton;
import javafx.scene.control.ToolBar;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
import java.util.List;
public class DecoratorApp extends Application {
private static final String SAMPLE_TEXT =
"Lasciate ogne speranza, voi ch'intrate";
private static final String CSS_DATA_URL = "data:text/css,";
private static final String CSS = CSS_DATA_URL + // language=CSS
"""
.root {
-fx-font-size: 20px;
}
.bold {
-fx-font-weight: bold;
-fx-font-family: "sans-serif";
}
.italic {
-fx-font-style: italic;
-fx-font-family: "sans-serif";
}
""";
private final Message sampleMessage = new SimpleMessage(
SAMPLE_TEXT
);
private final ObjectProperty<Message> message = new SimpleObjectProperty<>(
sampleMessage
);
private final ToggleButton bold = new ToggleButton("B");
private final ToggleButton italic = new ToggleButton("i");
private final List<Toggle> toggles = List.of(bold, italic);
@Override
public void start(Stage stage) {
bold.getStyleClass().add("bold");
italic.getStyleClass().add("italic");
bold.selectedProperty().addListener(o ->
applyStyles()
);
italic.selectedProperty().addListener(o ->
applyStyles()
);
ToolBar toolBar = new ToolBar(
bold,
italic
);
StackPane content = new StackPane(
message.get().getNode()
);
message.addListener(o ->
content.getChildren().setAll(
message.get().getNode()
)
);
content.setPadding(
new Insets(10)
);
content.setPrefSize(
300, 100
);
BorderPane layout = new BorderPane();
layout.setTop(toolBar);
layout.setCenter(content);
applyStyles();
Scene scene = new Scene(layout);
scene.getStylesheets().add(CSS);
stage.setScene(scene);
stage.show();
}
private void applyStyles() {
List<Toggle> selectedToggles = toggles.stream()
.filter(Toggle::isSelected)
.toList();
System.out.println(
selectedToggles.stream()
.map(toggle ->
((ToggleButton) toggle).getText()
).toList()
);
Message styledMessage = sampleMessage;
if (selectedToggles.contains(bold)) {
styledMessage = new BoldMessageDecorator(styledMessage);
}
if (selectedToggles.contains(italic)) {
styledMessage = new ItalicMessageDecorator(styledMessage);
}
styledMessage.applyStyle(styledMessage.getCSSRules());
message.set(styledMessage);
}
public static void main(String[] args) {
launch(args);
}
}