Revisão | 66 (tree) |
---|---|
Hora | 2023-01-08 17:37:44 |
Autor | ![]() |
JPKI PDF SIGNER 1.2.7
依存ライブラリ
pdfbox 2.0.26 -> 2.0.27
jackson-databind 2.13.3 -> 2.14.1
bouncycastle bcpkix jdk15on 1.70 -> jdk18on 1.72
fx-util 0.4.5 -> 0.5.0
jpki-wrapper 0.3.7 -> 0.3.8
gradle 7.5.1 -> 7.6
exewrap 1.6.5 -> 1.6.6
@@ -127,13 +127,6 @@ | ||
127 | 127 | return version; |
128 | 128 | } |
129 | 129 | |
130 | - public static boolean isRunningAsUWP() { | |
131 | - if(isRunningAsUWP == null) { | |
132 | - isRunningAsUWP = Files.exists(Datastore.getApplicationDirectory().resolve("AppxManifest.xml")); | |
133 | - } | |
134 | - return isRunningAsUWP; | |
135 | - } | |
136 | - | |
137 | 130 | public static String getLicense() throws IOException { |
138 | 131 | if(license == null) { |
139 | 132 | InputStream is = null; |
@@ -1,6 +1,6 @@ | ||
1 | 1 | .root { |
2 | 2 | -fx-font-family: "Meiryo"; |
3 | - -fx-font-size: 12px; | |
3 | + -fx-font-size: 12; | |
4 | 4 | } |
5 | 5 | |
6 | 6 | FontIcon { |
@@ -16,7 +16,7 @@ | ||
16 | 16 | -fx-background-insets: 0; |
17 | 17 | } |
18 | 18 | .list-cell { |
19 | - -fx-padding: 0px; | |
19 | + -fx-padding: 0; | |
20 | 20 | } |
21 | 21 | .list-cell:selected { |
22 | 22 | -fx-background-color: -fx-accent; |
@@ -30,15 +30,16 @@ | ||
30 | 30 | } |
31 | 31 | #buttonBox > .label { |
32 | 32 | -fx-alignment: center; |
33 | - -fx-min-width: 80px; | |
34 | - -fx-font-size: 14px; | |
33 | + -fx-min-width: 80; | |
34 | + -fx-font-size: 12; | |
35 | 35 | -fx-text-fill: #F2F2F2; |
36 | 36 | } |
37 | 37 | #buttonBox .button { |
38 | - -fx-padding: 0 10px 0 7px; | |
39 | - -fx-min-height: 25px; | |
40 | - -fx-pref-height: 25px; | |
41 | - -fx-min-width: 40px; | |
38 | + -fx-font-size: 12; | |
39 | + -fx-padding: 0 16 0 12; | |
40 | + -fx-min-height: 25; | |
41 | + -fx-pref-height: 25; | |
42 | + -fx-min-width: 40; | |
42 | 43 | -fx-background-insets: 0; |
43 | 44 | -fx-background-radius: 0; |
44 | 45 | -fx-background-color: transparent; |
@@ -33,9 +33,11 @@ | ||
33 | 33 | import net.osdn.jpki.pdf_signer.control.Toast; |
34 | 34 | import net.osdn.jpki.wrapper.JpkiException; |
35 | 35 | import net.osdn.jpki.wrapper.JpkiWrapper; |
36 | +import net.osdn.util.javafx.Unchecked; | |
36 | 37 | import net.osdn.util.javafx.application.SingletonApplication; |
37 | -import net.osdn.util.javafx.concurrent.Async; | |
38 | +import net.osdn.util.javafx.concurrent.AsyncTask; | |
38 | 39 | import net.osdn.util.javafx.fxml.Fxml; |
40 | +import net.osdn.util.javafx.scene.SceneUtil; | |
39 | 41 | import net.osdn.util.javafx.scene.control.Dialogs; |
40 | 42 | import net.osdn.util.javafx.scene.control.pdf.Pager; |
41 | 43 | import net.osdn.util.javafx.scene.control.pdf.PdfView; |
@@ -65,6 +67,8 @@ | ||
65 | 67 | import java.nio.file.StandardCopyOption; |
66 | 68 | import java.util.List; |
67 | 69 | import java.util.ResourceBundle; |
70 | +import java.util.concurrent.ExecutorService; | |
71 | +import java.util.concurrent.Executors; | |
68 | 72 | import java.util.prefs.Preferences; |
69 | 73 | |
70 | 74 | public class MainApp extends SingletonApplication implements Initializable { |
@@ -72,6 +76,8 @@ | ||
72 | 76 | public static final String APPLICATION_NAME = "JPKI PDF SIGNER"; |
73 | 77 | public static final String APPLICATION_VERSION; |
74 | 78 | |
79 | + public static ExecutorService serialExecutor = Executors.newSingleThreadExecutor(); | |
80 | + | |
75 | 81 | static { |
76 | 82 | System.setProperty( |
77 | 83 | "org.apache.commons.logging.LogFactory", "net.osdn.jpki.pdf_signer.LogFilter"); |
@@ -117,12 +123,18 @@ | ||
117 | 123 | Parent root = Fxml.load(this); |
118 | 124 | |
119 | 125 | Scene scene = new Scene(root); |
120 | - scene.setOnDragOver(wrap(this::scene_onDragOver)); | |
121 | - scene.setOnDragDropped(wrap(this::scene_onDragDropped)); | |
126 | + scene.setOnDragOver(event -> Unchecked.execute(() -> { | |
127 | + scene_onDragOver(event); | |
128 | + })); | |
129 | + scene.setOnDragDropped(event -> Unchecked.execute(() -> { | |
130 | + scene_onDragDropped(event); | |
131 | + })); | |
122 | 132 | scene.getAccelerators().putAll(pager.createDefaultAccelerators()); |
123 | 133 | |
124 | 134 | StageUtil.setRestorable(primaryStage, Preferences.userNodeForPackage(getClass())); |
125 | - primaryStage.setOnShown(event -> { Platform.runLater(wrap(this::stage_onReady)); }); | |
135 | + primaryStage.setOnShown(event -> SceneUtil.invokeAfterLayout(root, Unchecked.runnable(() -> { | |
136 | + stage_onReady(); | |
137 | + }))); | |
126 | 138 | primaryStage.setMinWidth(448.0); |
127 | 139 | primaryStage.setMinHeight(396.0); |
128 | 140 | primaryStage.setOpacity(0.0); |
@@ -153,7 +165,7 @@ | ||
153 | 165 | if(message != null) { |
154 | 166 | message = message.trim(); |
155 | 167 | } |
156 | - toast.show(Toast.RED, title, message, null); | |
168 | + toast.show(Toast.COLOR_ERROR, title, message); | |
157 | 169 | }; |
158 | 170 | if(Platform.isFxApplicationThread()) { |
159 | 171 | r.run(); |
@@ -187,15 +199,33 @@ | ||
187 | 199 | // |
188 | 200 | // event handlers |
189 | 201 | // |
190 | - menuFileOpen.setOnAction(wrap(this::menuFileOpen_onAction)); | |
191 | - menuFileSave.setOnAction(wrap(this::menuFileSave_onAction)); | |
192 | - menuFileExit.setOnAction(wrap(this::menuFileExit_onAction)); | |
193 | - menuHelpAbout.setOnAction(wrap(this::menuHelpAbout_onAction)); | |
194 | - pdfView.setOnMouseMoved(wrap(this::pdfView_onMouseMoved)); | |
195 | - pdfView.setOnMouseClicked(wrap(this::pdfView_onMouseClicked)); | |
196 | - btnRemoveSignature.setOnAction(wrap(this::btnRemoveSignature_onAction)); | |
197 | - btnEditSignature.setOnAction(wrap(this::btnEditSignature_onAction)); | |
198 | - btnAddSignature.setOnAction(wrap(this::btnAddSignature_onAction)); | |
202 | + menuFileOpen.setOnAction(event -> Unchecked.execute(() -> { | |
203 | + menuFileOpen_onAction(event); | |
204 | + })); | |
205 | + menuFileSave.setOnAction(event -> Unchecked.execute(() -> { | |
206 | + menuFileSave_onAction(event); | |
207 | + })); | |
208 | + menuFileExit.setOnAction(event -> Unchecked.execute(() -> { | |
209 | + menuFileExit_onAction(event); | |
210 | + })); | |
211 | + menuHelpAbout.setOnAction(event -> Unchecked.execute(() -> { | |
212 | + menuHelpAbout_onAction(event); | |
213 | + })); | |
214 | + pdfView.setOnMouseMoved(event -> Unchecked.execute(() -> { | |
215 | + pdfView_onMouseMoved(event); | |
216 | + })); | |
217 | + pdfView.setOnMouseClicked(event -> Unchecked.execute(() -> { | |
218 | + pdfView_onMouseClicked(event); | |
219 | + })); | |
220 | + btnRemoveSignature.setOnAction(event -> Unchecked.execute(() -> { | |
221 | + btnRemoveSignature_onAction(event); | |
222 | + })); | |
223 | + btnEditSignature.setOnAction(event -> Unchecked.execute(() -> { | |
224 | + btnEditSignature_onAction(event); | |
225 | + })); | |
226 | + btnAddSignature.setOnAction(event -> Unchecked.execute(() -> { | |
227 | + btnAddSignature_onAction(event); | |
228 | + })); | |
199 | 229 | |
200 | 230 | // |
201 | 231 | // bindings |
@@ -242,18 +272,16 @@ | ||
242 | 272 | |
243 | 273 | lvSignature.getItems().clear(); |
244 | 274 | lvSignature.getItems().add(Signature.INVISIBLE); |
245 | - Async.execute(() -> { | |
275 | + MainApp.serialExecutor.execute(AsyncTask.create(() -> { | |
246 | 276 | return Datastore.loadSignatures(); |
247 | 277 | }).onSucceeded(signatures -> { |
248 | 278 | for (Signature signature : signatures) { |
249 | 279 | lvSignature.getItems().add(signature); |
250 | 280 | } |
251 | - Platform.runLater(() -> { | |
252 | - checkJpkiAvailability(); | |
253 | - }); | |
281 | + checkJpkiAvailability(); | |
254 | 282 | }).onFailed(exception -> { |
255 | 283 | showException(exception); |
256 | - }); | |
284 | + })); | |
257 | 285 | } |
258 | 286 | |
259 | 287 | void scene_onDragOver(DragEvent event) { |
@@ -321,7 +349,7 @@ | ||
321 | 349 | File file = fc.showSaveDialog(getPrimaryStage()); |
322 | 350 | if(file != null) { |
323 | 351 | Files.copy(signedTemporaryFileProperty.get().toPath(), file.toPath(), StandardCopyOption.REPLACE_EXISTING); |
324 | - toast.show(Toast.GREEN, "保存しました", file.getPath(), Toast.LONG); | |
352 | + toast.show(Toast.COLOR_SUCCESS, "保存しました", file.getPath()); | |
325 | 353 | } |
326 | 354 | } |
327 | 355 |
@@ -400,7 +428,7 @@ | ||
400 | 428 | if(!event.isPrimaryButtonDown()) { |
401 | 429 | return; |
402 | 430 | } else if(pdfView.getDocument() == null) { |
403 | - toast.show(Toast.GREEN, | |
431 | + toast.show(Toast.COLOR_SUCCESS, | |
404 | 432 | "はじめに", |
405 | 433 | "PDFファイルをこのウィンドウにドラッグ&ドロップして表示しましょう。"); |
406 | 434 | } else if(checkJpkiAvailability()) { |
@@ -413,21 +441,23 @@ | ||
413 | 441 | SignatureOptions options = null; |
414 | 442 | |
415 | 443 | busyProperty.set(true); |
416 | - Async.execute(() -> sign(document, null, APPLICATION_NAME, APPLICATION_VERSION)) | |
417 | - .onSucceeded(tmpFile -> { | |
418 | - if(tmpFile != null) { | |
419 | - signedTemporaryFileProperty.set(tmpFile); | |
420 | - pdfView.load(tmpFile, pageIndex); | |
421 | - busyProperty.set(false); | |
444 | + MainApp.serialExecutor.execute(AsyncTask.create(() -> { | |
445 | + return sign(document, null, APPLICATION_NAME, APPLICATION_VERSION); | |
446 | + }).onSucceeded(tmpFile -> { | |
447 | + if(tmpFile != null) { | |
448 | + signedTemporaryFileProperty.set(tmpFile); | |
449 | + pdfView.load(tmpFile, pageIndex); | |
450 | + busyProperty.set(false); | |
422 | 451 | |
423 | - if(ButtonType.YES == Dialogs.showConfirmation(getPrimaryStage(), | |
424 | - APPLICATION_NAME + " " + APPLICATION_VERSION, | |
425 | - "署名が完了しました。\nファイルに名前を付けて保存しますか?")) { | |
426 | - menuFileSave.fire(); | |
427 | - } | |
452 | + if(ButtonType.YES == Dialogs.showConfirmation(getPrimaryStage(), | |
453 | + APPLICATION_NAME + " " + APPLICATION_VERSION, | |
454 | + "署名が完了しました。\nファイルに名前を付けて保存しますか?")) { | |
455 | + menuFileSave.fire(); | |
428 | 456 | } |
429 | - }) | |
430 | - .onCompleted(state -> busyProperty.set(false)); | |
457 | + } | |
458 | + }).onFinished(state -> { | |
459 | + busyProperty.set(false); | |
460 | + })); | |
431 | 461 | } |
432 | 462 | } |
433 | 463 | } finally { |
@@ -568,21 +598,23 @@ | ||
568 | 598 | |
569 | 599 | lvSignature.getSelectionModel().clearSelection(); |
570 | 600 | busyProperty.set(true); |
571 | - Async.execute(() -> sign(document, options, APPLICATION_NAME, APPLICATION_VERSION)) | |
572 | - .onSucceeded(tmpFile -> { | |
573 | - if(tmpFile != null) { | |
574 | - signedTemporaryFileProperty.set(tmpFile); | |
575 | - pdfView.load(tmpFile, pageIndex); | |
576 | - busyProperty.set(false); | |
601 | + MainApp.serialExecutor.execute(AsyncTask.create(() -> { | |
602 | + return sign(document, options, APPLICATION_NAME, APPLICATION_VERSION); | |
603 | + }).onSucceeded(tmpFile -> { | |
604 | + if(tmpFile != null) { | |
605 | + signedTemporaryFileProperty.set(tmpFile); | |
606 | + pdfView.load(tmpFile, pageIndex); | |
607 | + busyProperty.set(false); | |
577 | 608 | |
578 | - if(ButtonType.YES == Dialogs.showConfirmation(getPrimaryStage(), APPLICATION_NAME + " " + APPLICATION_VERSION, | |
579 | - "署名が完了しました。\nファイルに名前を付けて保存しますか?")) { | |
580 | - menuFileSave.fire(); | |
581 | - } | |
582 | - lvSignature.getSelectionModel().clearSelection(); | |
609 | + if(ButtonType.YES == Dialogs.showConfirmation(getPrimaryStage(), APPLICATION_NAME + " " + APPLICATION_VERSION, | |
610 | + "署名が完了しました。\nファイルに名前を付けて保存しますか?")) { | |
611 | + menuFileSave.fire(); | |
583 | 612 | } |
584 | - }) | |
585 | - .onCompleted(state -> busyProperty.set(false)); | |
613 | + lvSignature.getSelectionModel().clearSelection(); | |
614 | + } | |
615 | + }).onFinished(state -> { | |
616 | + busyProperty.set(false); | |
617 | + })); | |
586 | 618 | } |
587 | 619 | |
588 | 620 | protected File getFile(DragEvent event) { |
@@ -602,23 +634,15 @@ | ||
602 | 634 | protected boolean checkJpkiAvailability() { |
603 | 635 | boolean isAvailable = JpkiWrapper.isAvailable(); |
604 | 636 | if(!isAvailable) { |
605 | - // JPKI 利用者クライアントソフトがインストールされていない場合、 | |
606 | - // インストールを促すために、クリックで公的個人認証サービスのウェブサイトが開くようにします。 | |
607 | - // ただし、Microsoft Storeアプリではストア以外でのアプリインストールを促すことが禁止されているため | |
608 | - // UWPとして実行されている場合にはウェブサイトを開く機能を提供せずメッセージ表示のみに留めています。 | |
609 | - if(Datastore.isRunningAsUWP()) { | |
610 | - toast.show(Toast.GREEN, "構成", "JPKI 利用者クライアントソフトが見つかりません。"); | |
611 | - } else { | |
612 | - Runnable actionOnClick = wrap(() -> { | |
613 | - toast.hide(); | |
614 | - Desktop.getDesktop().browse(URI.create("https://www.jpki.go.jp/download/win.html")); | |
615 | - }); | |
616 | - toast.show(Toast.GREEN, "事前準備", "" | |
617 | - + "JPKI 利用者クライアントソフトをインストールしてください。\n" | |
618 | - + "ここをクリックするとブラウザーでダウンロードサイトを開きます。", | |
619 | - null, | |
620 | - actionOnClick); | |
621 | - } | |
637 | + toast.show( | |
638 | + Toast.COLOR_SUCCESS, | |
639 | + "事前準備", | |
640 | + "JPKI 利用者クライアントソフトをインストールしてください。\n" + | |
641 | + "ここをクリックするとブラウザーでダウンロードサイトを開きます。", | |
642 | + Unchecked.runnable(() -> { | |
643 | + toast.hide(); | |
644 | + Desktop.getDesktop().browse(URI.create("https://www.jpki.go.jp/download/win.html")); | |
645 | + })); | |
622 | 646 | } |
623 | 647 | return isAvailable; |
624 | 648 | } |
@@ -1,6 +1,9 @@ | ||
1 | 1 | .dialog-pane > .content { |
2 | + -fx-font-family: "Meiryo"; | |
3 | + -fx-font-size: 12; | |
2 | 4 | -fx-padding: 0.833em 0.833em 0 0.833em; |
3 | 5 | } |
6 | + | |
4 | 7 | #ivImage { |
5 | 8 | -fx-background-color: white; |
6 | 9 | } |
@@ -1,22 +1,14 @@ | ||
1 | 1 | package net.osdn.jpki.pdf_signer; |
2 | 2 | |
3 | 3 | import javafx.beans.binding.DoubleBinding; |
4 | -import javafx.beans.binding.ObjectBinding; | |
5 | 4 | import javafx.beans.property.DoubleProperty; |
6 | -import javafx.beans.value.ChangeListener; | |
7 | -import javafx.beans.value.ObservableValue; | |
8 | 5 | import javafx.event.ActionEvent; |
9 | -import javafx.event.Event; | |
10 | -import javafx.event.EventHandler; | |
11 | 6 | import javafx.fxml.FXML; |
12 | 7 | import javafx.fxml.Initializable; |
13 | -import javafx.geometry.Bounds; | |
14 | 8 | import javafx.geometry.Dimension2D; |
15 | -import javafx.geometry.Rectangle2D; | |
16 | 9 | import javafx.scene.Node; |
17 | 10 | import javafx.scene.control.Button; |
18 | 11 | import javafx.scene.control.ButtonType; |
19 | -import javafx.scene.control.Dialog; | |
20 | 12 | import javafx.scene.control.DialogEvent; |
21 | 13 | import javafx.scene.control.TextField; |
22 | 14 | import javafx.scene.image.Image; |
@@ -25,9 +17,9 @@ | ||
25 | 17 | import javafx.stage.Stage; |
26 | 18 | import javafx.stage.Window; |
27 | 19 | import javafx.util.Callback; |
28 | -import net.osdn.util.javafx.event.SilentCallback; | |
29 | -import net.osdn.util.javafx.event.SilentEventHandler; | |
20 | +import net.osdn.util.javafx.Unchecked; | |
30 | 21 | import net.osdn.util.javafx.fxml.Fxml; |
22 | +import net.osdn.util.javafx.scene.SceneUtil; | |
31 | 23 | import net.osdn.util.javafx.scene.control.DialogEx; |
32 | 24 | |
33 | 25 | import java.io.ByteArrayInputStream; |
@@ -40,6 +32,7 @@ | ||
40 | 32 | import java.security.NoSuchAlgorithmException; |
41 | 33 | import java.util.Arrays; |
42 | 34 | import java.util.ResourceBundle; |
35 | +import java.util.concurrent.Callable; | |
43 | 36 | import java.util.prefs.Preferences; |
44 | 37 | |
45 | 38 | public class SignatureDialog extends DialogEx<Signature> implements Initializable { |
@@ -60,8 +53,12 @@ | ||
60 | 53 | Node content = Fxml.load(this); |
61 | 54 | getDialogPane().setContent(content); |
62 | 55 | getDialogPane().getButtonTypes().addAll(ButtonType.OK, ButtonType.CANCEL); |
63 | - setOnShowing(wrap(this::dialog_onShowing)); | |
64 | - setResultConverter(wrap(this::dialog_onResult)); | |
56 | + setOnShowing(event -> SceneUtil.invokeAfterLayout(getDialogPane(), Unchecked.runnable(() -> { | |
57 | + dialog_onShowing(event); | |
58 | + }))); | |
59 | + setResultConverter(param -> Unchecked.execute(() -> { | |
60 | + return dialog_onResult(param); | |
61 | + })); | |
65 | 62 | |
66 | 63 | if(signature != null) { |
67 | 64 | ivImage.setImage(signature.getImage()); |
@@ -71,6 +68,14 @@ | ||
71 | 68 | } else { |
72 | 69 | btnBrowse.requestFocus(); |
73 | 70 | } |
71 | + | |
72 | + Node btn; | |
73 | + if((btn = getDialogPane().lookupButton(ButtonType.OK)) != null) { | |
74 | + btn.setStyle("-fx-font-family: Meiryo; -fx-font-size: 12; -fx-min-width: 96"); | |
75 | + } | |
76 | + if((btn = getDialogPane().lookupButton(ButtonType.CANCEL)) != null) { | |
77 | + btn.setStyle("-fx-font-family: Meiryo; -fx-font-size: 12; -fx-min-width: 96"); | |
78 | + } | |
74 | 79 | } |
75 | 80 | |
76 | 81 | @FXML Button btnBrowse; |
@@ -111,10 +116,18 @@ | ||
111 | 116 | } |
112 | 117 | }); |
113 | 118 | |
114 | - btnBrowse.setOnAction(wrap(this::tfBrowse_onAction)); | |
115 | - tfTitle.setOnAction(wrap(this::tfTitle_onAction)); | |
116 | - tfWidthMillis.setOnAction(wrap(this::tfWidthMillis_onAction)); | |
117 | - tfHeightMillis.setOnAction(wrap(this::tfHeightMillis_onAction)); | |
119 | + btnBrowse.setOnAction(event -> Unchecked.execute(() -> { | |
120 | + tfBrowse_onAction(event); | |
121 | + })); | |
122 | + tfTitle.setOnAction(event -> Unchecked.execute(() -> { | |
123 | + tfTitle_onAction(event); | |
124 | + })); | |
125 | + tfWidthMillis.setOnAction(event -> Unchecked.execute(() -> { | |
126 | + tfWidthMillis_onAction(event); | |
127 | + })); | |
128 | + tfHeightMillis.setOnAction(event -> Unchecked.execute(() -> { | |
129 | + tfHeightMillis_onAction(event); | |
130 | + })); | |
118 | 131 | } |
119 | 132 | |
120 | 133 | void dialog_onShowing(DialogEvent event) { |
@@ -234,14 +247,4 @@ | ||
234 | 247 | } |
235 | 248 | return new Dimension2D(prefWidth, prefHeight); |
236 | 249 | } |
237 | - | |
238 | - @SuppressWarnings("overloads") | |
239 | - protected <T extends Event> EventHandler<T> wrap(SilentEventHandler<T> handler) { | |
240 | - return SilentEventHandler.wrap(handler); | |
241 | - } | |
242 | - | |
243 | - @SuppressWarnings("overloads") | |
244 | - protected <P, R> Callback<P, R> wrap(SilentCallback<P, R> callback) { | |
245 | - return SilentCallback.wrap(callback); | |
246 | - } | |
247 | 250 | } |
@@ -1,3 +1,4 @@ | ||
1 | 1 | .label { |
2 | - -fx-font: 13px 'Meiryo'; | |
2 | + -fx-font-family: "Meiryo"; | |
3 | + -fx-font-size: 14; | |
3 | 4 | } |
@@ -9,7 +9,7 @@ | ||
9 | 9 | import javafx.scene.control.MenuItem; |
10 | 10 | import javafx.scene.image.ImageView; |
11 | 11 | import javafx.util.Callback; |
12 | -import net.osdn.util.javafx.event.SilentEventHandler; | |
12 | +import net.osdn.util.javafx.Unchecked; | |
13 | 13 | import net.osdn.util.javafx.fxml.Fxml; |
14 | 14 | |
15 | 15 | public class SignatureListCell extends ListCell<Signature> { |
@@ -27,8 +27,12 @@ | ||
27 | 27 | Node node = Fxml.load(this); |
28 | 28 | setText(null); |
29 | 29 | setGraphic(node); |
30 | - setOnMousePressed(SilentEventHandler.wrap(factory.mainApp::lvSignature_cell_onMousePressed)); | |
31 | - setOnMouseClicked(SilentEventHandler.wrap(factory.mainApp::lvSignature_cell_onMouseClicked)); | |
30 | + setOnMousePressed(event -> Unchecked.execute(() -> { | |
31 | + this.factory.mainApp.lvSignature_cell_onMousePressed(event); | |
32 | + })); | |
33 | + setOnMouseClicked(event -> Unchecked.execute(() -> { | |
34 | + this.factory.mainApp.lvSignature_cell_onMouseClicked(event); | |
35 | + })); | |
32 | 36 | } |
33 | 37 | |
34 | 38 | @Override |
@@ -72,9 +76,15 @@ | ||
72 | 76 | |
73 | 77 | public Factory(MainApp mainApp) { |
74 | 78 | this.mainApp = mainApp; |
75 | - menuAddSignature.setOnAction(SilentEventHandler.wrap(mainApp::btnAddSignature_onAction)); | |
76 | - menuEditSignature.setOnAction(SilentEventHandler.wrap(mainApp::btnEditSignature_onAction)); | |
77 | - menuRemoveSignature.setOnAction(SilentEventHandler.wrap(mainApp::btnRemoveSignature_onAction)); | |
79 | + menuAddSignature.setOnAction(event -> Unchecked.execute(() -> { | |
80 | + this.mainApp.btnAddSignature_onAction(event); | |
81 | + })); | |
82 | + menuEditSignature.setOnAction(event -> Unchecked.execute(() -> { | |
83 | + this.mainApp.btnEditSignature_onAction(event); | |
84 | + })); | |
85 | + menuRemoveSignature.setOnAction(event -> Unchecked.execute(() -> { | |
86 | + this.mainApp.btnRemoveSignature_onAction(event); | |
87 | + })); | |
78 | 88 | } |
79 | 89 | |
80 | 90 | @Override |
@@ -1,7 +1,7 @@ | ||
1 | 1 | TextFlow { |
2 | - -fx-padding: 15px; | |
2 | + -fx-padding: 15; | |
3 | 3 | -fx-font-family: "Meiryo"; |
4 | - -fx-font-size: 12px; | |
4 | + -fx-font-size: 12; | |
5 | 5 | -fx-background-color: #FAFAFA; |
6 | 6 | } |
7 | 7 | TextFlow Text { |
@@ -12,7 +12,9 @@ | ||
12 | 12 | import javafx.scene.text.Text; |
13 | 13 | import javafx.scene.text.TextFlow; |
14 | 14 | import javafx.stage.Window; |
15 | +import net.osdn.util.javafx.Unchecked; | |
15 | 16 | import net.osdn.util.javafx.fxml.Fxml; |
17 | +import net.osdn.util.javafx.scene.SceneUtil; | |
16 | 18 | import net.osdn.util.javafx.scene.control.DialogEx; |
17 | 19 | |
18 | 20 | import java.awt.Desktop; |
@@ -39,12 +41,19 @@ | ||
39 | 41 | getDialogPane().getButtonTypes().addAll(ButtonType.CLOSE); |
40 | 42 | scrollPane.prefViewportWidthProperty().bind(textFlow.widthProperty()); |
41 | 43 | scrollPane.prefViewportHeightProperty().bind(textFlow.heightProperty()); |
42 | - setOnShown(event -> { Platform.runLater(wrap(this::dialog_onReady)); }); | |
44 | + setOnShown(event -> SceneUtil.invokeAfterLayout(getDialogPane(), Unchecked.runnable(() -> { | |
45 | + dialog_onReady(); | |
46 | + }))); | |
43 | 47 | getDialogPane().getContent().setOpacity(0.0); |
44 | 48 | |
45 | 49 | Node[] nodes = build(license); |
46 | 50 | textFlow.getChildren().addAll(nodes); |
47 | 51 | |
52 | + Node btn; | |
53 | + if((btn = getDialogPane().lookupButton(ButtonType.CLOSE)) != null) { | |
54 | + btn.setStyle("-fx-font-family: Meiryo; -fx-font-size: 12"); | |
55 | + } | |
56 | + | |
48 | 57 | // ダイアログが表示されるまで「閉じる」ボタンを無効にしておきます。 |
49 | 58 | // これでtextFlowに初期フォーカスが当たるようになります。 |
50 | 59 | getDialogPane().lookupButton(ButtonType.CLOSE).disableProperty().bind( |
@@ -1,73 +1,18 @@ | ||
1 | -#background { | |
1 | +.container { | |
2 | 2 | -fx-background-color: rgb(39, 40, 34); |
3 | 3 | -fx-background-radius: 5; |
4 | - -fx-effect: dropshadow(three-pass-box, black, 10, 0, 1, 1); | |
4 | + -fx-effect: dropshadow(two-pass-box, black, 10, 0, 1, 1); | |
5 | 5 | } |
6 | 6 | |
7 | -#btnClose { | |
7 | +.label.title { | |
8 | + -fx-padding: 7 12 0 12; | |
8 | 9 | -fx-font-size: 16; |
9 | - -fx-padding: 1 8 0 0; | |
10 | - -fx-min-width: 0; | |
11 | - -fx-min-height: 0; | |
12 | - -fx-background-color: transparent; | |
13 | - -fx-background-radius: 0; | |
14 | - -fx-background-insets: 0; | |
15 | - -fx-text-fill: #808080; | |
16 | -} | |
17 | -#btnClose:hover { | |
18 | - -fx-text-fill: #F0F0F0; | |
19 | -} | |
20 | -#btnClose:pressed { | |
21 | - -fx-text-fill: #C0C0C0; | |
22 | -} | |
23 | - | |
24 | -#lblTitle { | |
25 | - -fx-font-size: 12; | |
26 | 10 | -fx-wrap-text: false; |
27 | - -fx-padding: 0 18 0 0; | |
28 | 11 | } |
29 | 12 | |
30 | -#lblMessage { | |
31 | - -fx-font-size: 12; | |
13 | +.label.message { | |
14 | + -fx-padding: 0 12 7 12; | |
15 | + -fx-font-size: 14; | |
32 | 16 | -fx-wrap-text: true; |
17 | + -fx-text-fill: #f0f0f0; | |
33 | 18 | } |
34 | - | |
35 | -.scroll-pane { | |
36 | - -fx-min-height: 0; | |
37 | - -fx-hbar-policy: never; | |
38 | - -fx-vbar-policy: as-needed; | |
39 | - -fx-background-color: transparent; | |
40 | -} | |
41 | -.scroll-pane .viewport { | |
42 | - -fx-background-color: transparent; | |
43 | -} | |
44 | -.scroll-bar:vertical { | |
45 | - -fx-background-color: transparent; | |
46 | - -fx-padding: 0; | |
47 | -} | |
48 | -.scroll-bar:vertical .track { | |
49 | - -fx-background-color: transparent; | |
50 | -} | |
51 | -.scroll-bar:vertical .thumb { | |
52 | - -fx-background-color: #474844; | |
53 | - -fx-background-insets: 0; | |
54 | - -fx-background-radius: 0; | |
55 | -} | |
56 | -.scroll-bar:vertical .thumb:hover { | |
57 | - -fx-background-color: #525250; | |
58 | -} | |
59 | -.scroll-bar:vertical .thumb:pressed { | |
60 | - -fx-background-color: #636460; | |
61 | -} | |
62 | -.scroll-bar:vertical .increment-button, | |
63 | -.scroll-bar:vertical .decrement-button { | |
64 | - -fx-padding: 0px; | |
65 | -} | |
66 | -.scroll-bar:vertical .increment-arrow, | |
67 | -.scroll-bar:vertical .decrement-arrow { | |
68 | - -fx-padding: 0 5 0 5; | |
69 | -} | |
70 | -.scroll-bar:horizontal .increment-arrow, | |
71 | -.scroll-bar:horizontal .decrement-arrow { | |
72 | - -fx-padding: 5 0 5 0; | |
73 | -} |
@@ -7,38 +7,26 @@ | ||
7 | 7 | import javafx.animation.TranslateTransition; |
8 | 8 | import javafx.application.Platform; |
9 | 9 | import javafx.beans.binding.Bindings; |
10 | -import javafx.event.ActionEvent; | |
11 | -import javafx.geometry.Insets; | |
12 | -import javafx.scene.Cursor; | |
13 | -import javafx.scene.control.Button; | |
10 | +import javafx.fxml.FXML; | |
14 | 11 | import javafx.scene.control.Label; |
15 | -import javafx.scene.control.ScrollPane; | |
16 | -import javafx.scene.layout.AnchorPane; | |
17 | -import javafx.scene.layout.BorderPane; | |
18 | -import javafx.scene.layout.StackPane; | |
12 | +import javafx.scene.layout.Pane; | |
19 | 13 | import javafx.scene.paint.Color; |
20 | 14 | import javafx.util.Duration; |
15 | +import net.osdn.util.javafx.Unchecked; | |
16 | +import net.osdn.util.javafx.fxml.Fxml; | |
21 | 17 | |
22 | -public class Toast extends StackPane { | |
23 | - | |
24 | - public static final Color RED = Color.rgb(249, 38, 114); | |
25 | - public static final Color GREEN = Color.rgb(166, 226, 46); | |
26 | - public static final Color BLUE = Color.rgb(102, 217, 239); | |
27 | - | |
28 | - public static final Duration SHORT_PERSISTENT = Duration.millis(2001); | |
29 | - public static final Duration SHORT = Duration.millis(2000); | |
30 | - public static final Duration LONG = Duration.millis(3500); | |
31 | - | |
32 | - private static final Color DEFAULT_COLOR = Color.rgb(240, 240, 240); | |
18 | +public class Toast extends Pane { | |
33 | 19 | |
34 | - private final Data EMPTY_DATA = new Data(null, null, null, null, null); | |
35 | - | |
36 | - private BorderPane borderPane; | |
37 | - private Button btnClose; | |
38 | - private Label lblTitle; | |
39 | - private Label lblMessage; | |
40 | - private Runnable actionOnClick; | |
41 | - | |
20 | + public static final Color COLOR_ERROR = Color.rgb(249, 38, 114); | |
21 | + public static final Color COLOR_SUCCESS = Color.rgb(166, 226, 46); | |
22 | + private static final Duration DURATION = Duration.millis(5000); | |
23 | + | |
24 | + private final Data EMPTY_DATA = new Data(null, null, null, null); | |
25 | + | |
26 | + @FXML private Label lblTitle; | |
27 | + @FXML private Label lblMessage; | |
28 | + private Runnable action; | |
29 | + | |
42 | 30 | private TranslateTransition SHOW_ANIMATION; |
43 | 31 | private TranslateTransition HIDE_ANIMATION; |
44 | 32 | private TranslateTransition currentTransition; |
@@ -46,55 +34,21 @@ | ||
46 | 34 | private Data nextData; |
47 | 35 | |
48 | 36 | public Toast() { |
49 | - getStylesheets().add(Toast.class.getResource("Toast.css").toExternalForm()); | |
37 | + Fxml.load(this, this); | |
50 | 38 | |
51 | - btnClose = new Button("×"); | |
52 | - btnClose.setId("btnClose"); | |
53 | - btnClose.setOnAction(event-> { | |
54 | - btnClose_onAction(event); | |
55 | - }); | |
56 | - | |
57 | - lblTitle = new Label(); | |
58 | - lblTitle.setId("lblTitle"); | |
59 | - lblTitle.setWrapText(false); | |
60 | - | |
61 | - lblMessage = new Label(); | |
62 | - lblMessage.setId("lblMessage"); | |
63 | - lblMessage.setWrapText(true); | |
64 | - | |
65 | - ScrollPane scrollPane = new ScrollPane(lblMessage); | |
66 | - scrollPane.prefViewportHeightProperty().bind(lblMessage.heightProperty()); | |
67 | - | |
68 | - borderPane = new BorderPane(); | |
69 | - borderPane.setId("background"); | |
70 | - borderPane.centerProperty().set(scrollPane); | |
71 | - borderPane.paddingProperty().bind(Bindings | |
72 | - .when(Bindings.isNull(borderPane.topProperty())) | |
73 | - .then(new Insets(3, 25, 3, 10)) | |
74 | - .otherwise(new Insets(3, 8, 3, 10))); | |
75 | - | |
76 | - AnchorPane layer = new AnchorPane(btnClose); | |
77 | - layer.setPickOnBounds(false); | |
78 | - AnchorPane.setTopAnchor(btnClose, 0.0); | |
79 | - AnchorPane.setRightAnchor(btnClose, 0.0); | |
39 | + // | |
40 | + // bindings | |
41 | + // | |
80 | 42 | |
81 | - getChildren().addAll(borderPane, layer); | |
82 | - setVisible(false); | |
83 | - | |
84 | - maxWidthProperty().addListener((observable, oldValue, newValue)-> { | |
85 | - double width = newValue.doubleValue() | |
86 | - - borderPane.getPadding().getLeft() | |
87 | - - borderPane.getPadding().getRight(); | |
88 | - lblTitle.setMaxWidth(width); | |
89 | - lblMessage.setMaxWidth(width); | |
90 | - scrollPane.setMaxWidth(width); | |
91 | - }); | |
92 | - | |
43 | + // | |
44 | + // event handlers | |
45 | + // | |
46 | + | |
93 | 47 | // SHOW_ANIMATION |
94 | 48 | SHOW_ANIMATION = new TranslateTransition(); |
95 | 49 | SHOW_ANIMATION.setNode(this); |
96 | 50 | SHOW_ANIMATION.setInterpolator(Interpolator.EASE_OUT); |
97 | - SHOW_ANIMATION.fromYProperty().bind(Bindings.add(heightProperty(), 40.0)); | |
51 | + SHOW_ANIMATION.fromYProperty().bind(Bindings.add(heightProperty(), 80.0)); | |
98 | 52 | SHOW_ANIMATION.durationProperty().bind(Bindings.createObjectBinding(()-> { |
99 | 53 | return Duration.millis(SHOW_ANIMATION.getFromY() * 5.0); |
100 | 54 | }, SHOW_ANIMATION.fromYProperty())); |
@@ -103,12 +57,12 @@ | ||
103 | 57 | currentTransition = (nextData != null) ? getTransition(nextData) : null; |
104 | 58 | if(currentTransition != null) { |
105 | 59 | currentTransition.play(); |
106 | - } else if(currentData.duration != null && currentData.duration.toMillis() > 0.0) { | |
60 | + } else { | |
107 | 61 | Data data = currentData; |
108 | - new Timeline(new KeyFrame(data.duration, onFinished -> { | |
62 | + new Timeline(new KeyFrame(DURATION, onFinished -> { | |
109 | 63 | if(data == currentData) { |
110 | 64 | //hide |
111 | - show(null, null, null, null); | |
65 | + show(null,null, null); | |
112 | 66 | } |
113 | 67 | })).play(); |
114 | 68 | } |
@@ -121,11 +75,11 @@ | ||
121 | 75 | }); |
122 | 76 | |
123 | 77 | // HIDE_ANIMATION |
124 | - HIDE_ANIMATION = new TranslateTransition(Duration.millis(3000)); | |
78 | + HIDE_ANIMATION = new TranslateTransition(Duration.millis(5000)); | |
125 | 79 | HIDE_ANIMATION.setNode(this); |
126 | 80 | HIDE_ANIMATION.setInterpolator(Interpolator.EASE_IN); |
127 | 81 | HIDE_ANIMATION.setFromY(0); |
128 | - HIDE_ANIMATION.toYProperty().bind(Bindings.add(heightProperty(), 40.0)); | |
82 | + HIDE_ANIMATION.toYProperty().bind(Bindings.add(heightProperty(), 80.0)); | |
129 | 83 | HIDE_ANIMATION.durationProperty().bind(Bindings.createObjectBinding(()-> { |
130 | 84 | return Duration.millis(HIDE_ANIMATION.getToY() * 5.0); |
131 | 85 | }, HIDE_ANIMATION.toYProperty())); |
@@ -144,56 +98,37 @@ | ||
144 | 98 | }); |
145 | 99 | |
146 | 100 | // CLICK ACTION |
147 | - setOnMouseClicked(event -> { | |
148 | - if(actionOnClick != null) { | |
149 | - actionOnClick.run(); | |
101 | + setOnMouseClicked(event -> Unchecked.execute(() -> { | |
102 | + if(action != null) { | |
103 | + action.run(); | |
150 | 104 | } |
151 | - }); | |
105 | + })); | |
152 | 106 | } |
153 | - | |
154 | - protected void btnClose_onAction(ActionEvent event) { | |
155 | - show(null, null, null, null, null); | |
156 | - } | |
157 | - | |
107 | + | |
158 | 108 | public void hide() { |
159 | - if(currentData != null && currentData.isPersistent) { | |
160 | - return; | |
161 | - } | |
162 | - show(null, null, null, null, null); | |
109 | + show(null, null, null); | |
163 | 110 | } |
164 | 111 | |
165 | - public void show(String message) { | |
166 | - show(null, null, message, null); | |
167 | - } | |
168 | - | |
169 | 112 | public void show(String title, String message) { |
170 | - show(null, title, message, null); | |
113 | + show(null, title, message); | |
171 | 114 | } |
172 | - | |
115 | + | |
116 | + public void showError(String title, String message) { | |
117 | + show(COLOR_ERROR, title, message); | |
118 | + } | |
119 | + | |
173 | 120 | public void show(Color color, String title, String message) { |
174 | 121 | show(color, title, message, null); |
175 | 122 | } |
176 | - | |
177 | - public void show(String title, String message, Duration duration) { | |
178 | - show(null, title, message, duration); | |
179 | - } | |
180 | - | |
181 | - public void show(Color color, String message, Duration duration) { | |
182 | - show(color, null, message, duration); | |
183 | - } | |
184 | 123 | |
185 | - public void show(Color color, String title, String message, Duration duration) { | |
186 | - show(color, title, message, duration, null); | |
187 | - } | |
188 | - | |
189 | - public void show(Color color, String title, String message, Duration duration, Runnable actionOnClick) { | |
124 | + public void show(Color color, String title, String message, Runnable action) { | |
190 | 125 | if(!Platform.isFxApplicationThread()) { |
191 | 126 | Platform.runLater(()-> { |
192 | - show(color, title, message, duration); | |
127 | + show(color, title, message, action); | |
193 | 128 | }); |
194 | 129 | return; |
195 | 130 | } |
196 | - Data data = new Data(color, title, message, duration, actionOnClick); | |
131 | + Data data = new Data(color, title, message, action); | |
197 | 132 | if(currentTransition == null) { |
198 | 133 | if(isVisible()) { |
199 | 134 | currentTransition = getTransition(EMPTY_DATA); |
@@ -209,50 +144,43 @@ | ||
209 | 144 | nextData = data; |
210 | 145 | } |
211 | 146 | } |
212 | - | |
147 | + | |
213 | 148 | protected TranslateTransition getTransition(Data data) { |
214 | 149 | currentData = data; |
150 | + action = data.action; | |
215 | 151 | if(data.isEmpty) { |
216 | 152 | return HIDE_ANIMATION; |
217 | 153 | } else { |
154 | + if(data.color == null) { | |
155 | + lblTitle.setTextFill(COLOR_SUCCESS); | |
156 | + } else { | |
157 | + lblTitle.setTextFill(data.color); | |
158 | + } | |
218 | 159 | if(data.title == null || data.title.length() == 0) { |
219 | 160 | lblTitle.setText(""); |
220 | 161 | } else { |
221 | 162 | lblTitle.setText(data.title); |
222 | - lblTitle.setTextFill(data.color != null ? data.color : DEFAULT_COLOR); | |
223 | 163 | } |
224 | - if(lblTitle.getText().isEmpty() && borderPane.getChildren().contains(lblTitle)) { | |
225 | - borderPane.setTop(null); | |
226 | - } else if(!lblTitle.getText().isEmpty() && !borderPane.getChildren().contains(lblTitle)) { | |
227 | - borderPane.setTop(lblTitle); | |
228 | - } | |
229 | - lblMessage.setTextFill(lblTitle.getText().isEmpty() ? data.color : DEFAULT_COLOR); | |
230 | 164 | lblMessage.setText(data.message != null ? data.message : ""); |
231 | - lblMessage.setCursor(data.actionOnClick != null ? Cursor.HAND : Cursor.DEFAULT); | |
232 | 165 | layout(); |
233 | - actionOnClick = data.actionOnClick; | |
234 | 166 | return SHOW_ANIMATION; |
235 | 167 | } |
236 | 168 | } |
237 | - | |
169 | + | |
238 | 170 | private class Data { |
239 | - | |
171 | + | |
240 | 172 | public Color color; |
241 | 173 | public String title; |
242 | 174 | public String message; |
243 | - public Duration duration; | |
244 | - public Runnable actionOnClick; | |
175 | + public Runnable action; | |
245 | 176 | public boolean isEmpty; |
246 | - public boolean isPersistent; | |
247 | - | |
248 | - public Data(Color color, String title, String message, Duration duration, Runnable actionOnClick) { | |
177 | + | |
178 | + public Data(Color color, String title, String message, Runnable action) { | |
249 | 179 | this.color = color; |
250 | 180 | this.title = title; |
251 | 181 | this.message = message; |
252 | - this.duration = duration; | |
253 | - this.actionOnClick = actionOnClick; | |
254 | 182 | this.isEmpty = (title == null || title.isEmpty()) && (message == null || message.isEmpty()); |
255 | - this.isPersistent = (duration == SHORT_PERSISTENT); | |
183 | + this.action = this.isEmpty ? null : action; | |
256 | 184 | } |
257 | 185 | } |
258 | 186 | } |