1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26 package it.tidalwave.ui.javafx.impl.dialog;
27
28 import jakarta.annotation.Nonnull;
29 import java.util.Optional;
30 import java.util.concurrent.Executor;
31 import java.io.IOException;
32 import java.net.URI;
33 import java.net.URISyntaxException;
34 import java.awt.Desktop;
35 import javafx.beans.value.ChangeListener;
36 import javafx.beans.value.ObservableValue;
37 import javafx.scene.Node;
38 import javafx.scene.control.Alert;
39 import javafx.scene.control.ButtonType;
40 import javafx.scene.control.Dialog;
41 import javafx.scene.control.Label;
42 import javafx.scene.paint.Color;
43 import javafx.scene.web.WebView;
44 import javafx.application.Platform;
45 import it.tidalwave.util.BundleUtilities;
46 import it.tidalwave.util.Callback;
47 import it.tidalwave.ui.core.UserNotificationWithFeedback;
48 import it.tidalwave.ui.javafx.impl.common.DelegateSupport;
49 import lombok.RequiredArgsConstructor;
50 import lombok.SneakyThrows;
51 import lombok.extern.slf4j.Slf4j;
52
53
54
55
56
57
58 @Slf4j
59 public class DialogBindings extends DelegateSupport
60 {
61 @RequiredArgsConstructor
62 static class UrlOpener implements ChangeListener<String>
63 {
64 @Nonnull
65 private final WebView webView;
66
67 @Nonnull
68 private final String text;
69
70 @Override
71 public void changed (final ObservableValue<? extends String> observableValue, final String oldValue, final String newValue)
72 {
73 if (newValue != null && !newValue.isEmpty())
74 {
75 Platform.runLater(() ->
76 {
77 try
78 {
79 log.debug("Opening {} ...", newValue);
80 Desktop.getDesktop().browse(new URI(newValue));
81 }
82 catch (IOException | URISyntaxException e)
83 {
84 log.warn("", e);
85 }
86 });
87
88 webView.getEngine().loadContent(text);
89 }
90 }
91 }
92
93
94
95
96 public DialogBindings (@Nonnull final Executor executor)
97 {
98 super(executor);
99 }
100
101
102
103
104 public void showInModalDialog (@Nonnull final UserNotificationWithFeedback notification,
105 @Nonnull final Optional<Node> node)
106 {
107
108 Platform.runLater(() ->
109 {
110 log.debug("modalDialog({}, {})", node, notification);
111
112
113 final Dialog<ButtonType> dialog = new Alert(Alert.AlertType.NONE);
114 dialog.initOwner(mainWindow);
115 dialog.setTitle(notification.getCaption());
116 dialog.setResizable(false);
117 final var dialogPane = dialog.getDialogPane();
118 final var text = notification.getText();
119
120 if (!text.startsWith("<html>"))
121 {
122 dialog.setContentText(text);
123 node.ifPresent(dialogPane::setContent);
124 }
125 else
126 {
127 final var webView = new WebView();
128 webView.setPrefSize(600, 300);
129 webView.setContextMenuEnabled(false);
130 final var label = new Label();
131 dialogPane.setContent(label);
132 final var fontFamily = label.getFont().getFamily();
133
134 final var textColor = "white";
135 var backgroundColor = "#252525";
136
137 try
138 {
139
140 final var setPageFill = webView.getClass().getDeclaredMethod("setPageFill", Color.class);
141 setPageFill.invoke(webView, Color.TRANSPARENT);
142 backgroundColor = "transparent";
143 }
144 catch (Exception e)
145 {
146 log.trace("WebView.setPageFill() not available", e);
147 }
148
149
150 final var text2 = BundleUtilities.getMessage(getClass(),"htmlTemplate", text, textColor, backgroundColor, fontFamily);
151
152 webView.getEngine().loadContent(text2);
153 webView.getEngine().locationProperty().addListener(new UrlOpener(webView, text2));
154 dialogPane.setContent(webView);
155 }
156
157 final var feedback = notification.getFeedback();
158 final var hasOnCancel = feedback.canCancel();
159
160 final var buttonTypes = dialogPane.getButtonTypes();
161 buttonTypes.clear();
162 buttonTypes.add(ButtonType.OK);
163
164 if (hasOnCancel)
165 {
166 buttonTypes.add(ButtonType.CANCEL);
167 }
168
169
170
171 final var result = dialog.showAndWait();
172
173 if (result.isEmpty())
174 {
175 if (hasOnCancel)
176 {
177 wrap(notification::cancel);
178 }
179 else
180 {
181 wrap(notification::confirm);
182 }
183 }
184 else
185 {
186 if (result.get() == ButtonType.OK)
187 {
188 wrap(notification::confirm);
189 }
190 else if (result.get() == ButtonType.CANCEL)
191 {
192 wrap(notification::cancel);
193 }
194 else
195 {
196 throw new IllegalStateException("Unexpected button pressed: " + result.get());
197 }
198 }
199 });
200 }
201
202
203
204
205 @SneakyThrows(Throwable.class)
206 private static void wrap (@Nonnull final Callback callback)
207 {
208 callback.call();
209 }
210 }