mirror of
https://github.com/JFormDesigner/FlatLaf.git
synced 2025-12-06 22:10:54 +03:00
Compare commits
390 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e7cdc9cf8c | ||
|
|
2443547b3b | ||
|
|
8424300b5f | ||
|
|
81822cf7f6 | ||
|
|
907956994f | ||
|
|
9246cc0607 | ||
|
|
9f81d147d1 | ||
|
|
b9bd26b2fb | ||
|
|
1838174678 | ||
|
|
2aad301938 | ||
|
|
e18e8e3158 | ||
|
|
049dae6584 | ||
|
|
6a8bf2acc5 | ||
|
|
c45a769aa3 | ||
|
|
e0b0617ad2 | ||
|
|
14ec6f6471 | ||
|
|
c4a1341aa9 | ||
|
|
fc68dfd7bc | ||
|
|
b203ad63ee | ||
|
|
a560be11ed | ||
|
|
ebd6375672 | ||
|
|
502731d3b0 | ||
|
|
283535c429 | ||
|
|
5cef1f6730 | ||
|
|
7d14fbe739 | ||
|
|
e9e1e350eb | ||
|
|
566e42cc40 | ||
|
|
0abfb5922a | ||
|
|
4af8d2f1c5 | ||
|
|
d2d4f73834 | ||
|
|
53fce4e81d | ||
|
|
08c439b46e | ||
|
|
934eb9fc1d | ||
|
|
fd208a3879 | ||
|
|
10b131e111 | ||
|
|
c4c6faa943 | ||
|
|
c7a8d1e1b7 | ||
|
|
b36ac1b824 | ||
|
|
bc6cb492f1 | ||
|
|
ce503cedc3 | ||
|
|
c900c9cc82 | ||
|
|
87b73f26f5 | ||
|
|
be529655d6 | ||
|
|
2a0403a988 | ||
|
|
815e23b930 | ||
|
|
f1c08e7769 | ||
|
|
571f028ca3 | ||
|
|
16d51fe6b4 | ||
|
|
ddf9ed06ab | ||
|
|
1907f80024 | ||
|
|
8c0ccdd227 | ||
|
|
dc098025b6 | ||
|
|
c11222b5c7 | ||
|
|
03bc6eb69b | ||
|
|
1aa339de02 | ||
|
|
531bb2a968 | ||
|
|
800dbf3ba9 | ||
|
|
ff545e6ecd | ||
|
|
961fe38c7e | ||
|
|
19426394e2 | ||
|
|
069a4e8f0b | ||
|
|
a76b02b828 | ||
|
|
fbb9bf5f0c | ||
|
|
f632c355e8 | ||
|
|
e75caf5833 | ||
|
|
b0c8f2cefd | ||
|
|
2136d9f13d | ||
|
|
83fdeb7e0c | ||
|
|
26c77b3118 | ||
|
|
578d445ecb | ||
|
|
3bbc9517af | ||
|
|
a4d7f278cf | ||
|
|
bf0ffc6ac2 | ||
|
|
ace07cd9cb | ||
|
|
a341179426 | ||
|
|
298f0dfd63 | ||
|
|
b8f953cd26 | ||
|
|
a9cfe69ba7 | ||
|
|
b3e0b99e8d | ||
|
|
5bd40baed2 | ||
|
|
d3a70b8bb2 | ||
|
|
71e698603d | ||
|
|
659ead903c | ||
|
|
070c435f40 | ||
|
|
b668a526e3 | ||
|
|
01287d0669 | ||
|
|
ff481d759f | ||
|
|
71248f1708 | ||
|
|
0a0f834f23 | ||
|
|
06cad7ecd8 | ||
|
|
ceba3e2f95 | ||
|
|
61c2fd8794 | ||
|
|
db933fee4f | ||
|
|
2656c2dc40 | ||
|
|
01cfe33865 | ||
|
|
d79a31cc79 | ||
|
|
9efab8b892 | ||
|
|
aae845247a | ||
|
|
3f45a9a75f | ||
|
|
c9016155ae | ||
|
|
1019e8f4af | ||
|
|
8e423b4552 | ||
|
|
0e288c955c | ||
|
|
465dc8a66c | ||
|
|
7e5c599cc0 | ||
|
|
a961001a4b | ||
|
|
0a181f6407 | ||
|
|
27a347db34 | ||
|
|
b228dbb2df | ||
|
|
09cffc4340 | ||
|
|
e79880d305 | ||
|
|
34266761d1 | ||
|
|
77f17eaa3e | ||
|
|
ac70342cb3 | ||
|
|
d2f16dcaf3 | ||
|
|
abcce2bf68 | ||
|
|
514487074b | ||
|
|
f014e2473f | ||
|
|
80981f7027 | ||
|
|
8e6e971b51 | ||
|
|
4bd3b889dc | ||
|
|
464787dc1e | ||
|
|
a2541a9659 | ||
|
|
099dd87241 | ||
|
|
38eb914420 | ||
|
|
162215b1cf | ||
|
|
c6883f7a92 | ||
|
|
584286b794 | ||
|
|
a48713b7ca | ||
|
|
8f10c2d8bf | ||
|
|
5c0de9aa1c | ||
|
|
5553fd6538 | ||
|
|
e3ed47b37c | ||
|
|
976353d770 | ||
|
|
6fc216dff5 | ||
|
|
3f3961d255 | ||
|
|
875637bc6d | ||
|
|
395333cb3d | ||
|
|
870d039541 | ||
|
|
e8c8bece3f | ||
|
|
bd2f5dd6fe | ||
|
|
73f78d47ae | ||
|
|
8f60755f02 | ||
|
|
44c455419b | ||
|
|
129bc9b3ae | ||
|
|
08ba7dd065 | ||
|
|
dd2cf50a39 | ||
|
|
06eeced5b2 | ||
|
|
be23e5709d | ||
|
|
2735185eb9 | ||
|
|
41dd0acfa3 | ||
|
|
115a2df2b0 | ||
|
|
fcbb3aeed1 | ||
|
|
e9cb85127a | ||
|
|
c9c703fe98 | ||
|
|
0141dfbea2 | ||
|
|
fb7dafbc39 | ||
|
|
0660f9a511 | ||
|
|
a39ae5a8c5 | ||
|
|
03e22e3dbf | ||
|
|
d5e9fd0e5c | ||
|
|
141138ebea | ||
|
|
9026efeb26 | ||
|
|
2ab023beb0 | ||
|
|
8e471fd720 | ||
|
|
13cbbd8bc1 | ||
|
|
b08ccc9767 | ||
|
|
801a7023a4 | ||
|
|
dd06b554da | ||
|
|
23f0504b30 | ||
|
|
262d172cde | ||
|
|
be81cb7876 | ||
|
|
aaf9bd33cb | ||
|
|
7381e2141f | ||
|
|
3923d941c1 | ||
|
|
d134c33499 | ||
|
|
a2b615d4a7 | ||
|
|
37ecd9bd4f | ||
|
|
2e1acb7871 | ||
|
|
2250185487 | ||
|
|
97a1bf90a4 | ||
|
|
73cb63c9f9 | ||
|
|
f61f6b6006 | ||
|
|
7d3ffbc45a | ||
|
|
93ac6fa88a | ||
|
|
09d19a13b7 | ||
|
|
9eaee8d2c4 | ||
|
|
4da0c342f8 | ||
|
|
70fed22737 | ||
|
|
acb62e347a | ||
|
|
78d06d82d6 | ||
|
|
4777bdd250 | ||
|
|
266e9d92d5 | ||
|
|
54c14d0dc8 | ||
|
|
d59d353c2e | ||
|
|
204da2175b | ||
|
|
a8f659f2ac | ||
|
|
a878ebc368 | ||
|
|
152f235ca1 | ||
|
|
d094709dc8 | ||
|
|
97d5792341 | ||
|
|
9429ba7d48 | ||
|
|
af89dd13c1 | ||
|
|
60c6c5b37a | ||
|
|
5ed40cab1d | ||
|
|
1bebfe9cf2 | ||
|
|
e2618c37a2 | ||
|
|
f2ab848c46 | ||
|
|
93b82c0e97 | ||
|
|
4ac5ad06f2 | ||
|
|
a3788038bb | ||
|
|
12af2de99e | ||
|
|
225b722b1b | ||
|
|
1d9c8ca65e | ||
|
|
e51ffe2a1c | ||
|
|
c706a79f74 | ||
|
|
2608061d48 | ||
|
|
df1634de3d | ||
|
|
4aeabea3fe | ||
|
|
eb30f9d5bf | ||
|
|
de718f847c | ||
|
|
8ee6588d46 | ||
|
|
7c25f087fb | ||
|
|
d0b0f098d9 | ||
|
|
8835e20bfc | ||
|
|
92258f3ba3 | ||
|
|
1bda7595dd | ||
|
|
60557fc8c8 | ||
|
|
1a4a7831f6 | ||
|
|
d1415a8c53 | ||
|
|
0d4f33ac6e | ||
|
|
87100bef7b | ||
|
|
958dfa8ae9 | ||
|
|
d3752573e7 | ||
|
|
cfd07cbcc8 | ||
|
|
33e6ce1673 | ||
|
|
00ccda83f9 | ||
|
|
8d66cce6eb | ||
|
|
ba35fb7525 | ||
|
|
4fd2b24b10 | ||
|
|
5f7a33b085 | ||
|
|
65bc5b1f31 | ||
|
|
dd155e9f89 | ||
|
|
5c4ef3b0f5 | ||
|
|
2129a48cc8 | ||
|
|
c0f2784599 | ||
|
|
fee00b2acb | ||
|
|
ae8093313e | ||
|
|
5a4e321f78 | ||
|
|
9d1ed241b9 | ||
|
|
b63cd241d2 | ||
|
|
1e4f2d85a2 | ||
|
|
5cb7be4a64 | ||
|
|
0cd9068c0e | ||
|
|
9d6afe3bde | ||
|
|
52702b5267 | ||
|
|
cd144ff067 | ||
|
|
dd9784b3f2 | ||
|
|
d781b3d4a7 | ||
|
|
790f490674 | ||
|
|
004a5cb765 | ||
|
|
408b2d8376 | ||
|
|
3aa53ff3be | ||
|
|
73b642799d | ||
|
|
b522500379 | ||
|
|
f736ed401f | ||
|
|
c02f824d74 | ||
|
|
6f9a61de56 | ||
|
|
4cebeda37a | ||
|
|
4275005a64 | ||
|
|
5f40ab130e | ||
|
|
7489526eb7 | ||
|
|
e439d91763 | ||
|
|
793969e39b | ||
|
|
9f7ffe8d77 | ||
|
|
eecb867227 | ||
|
|
bff9f135e6 | ||
|
|
03627281d7 | ||
|
|
c83b4093f0 | ||
|
|
7f9f22df3e | ||
|
|
fd48582a9f | ||
|
|
e5761128f9 | ||
|
|
87dd5a9ebb | ||
|
|
11950f8b4d | ||
|
|
f2ddfadc9d | ||
|
|
b1a7983f18 | ||
|
|
b319cb278b | ||
|
|
78e3d781fc | ||
|
|
34834917b0 | ||
|
|
9446c287e9 | ||
|
|
31b0cf396e | ||
|
|
00bb13c230 | ||
|
|
3bf09ee731 | ||
|
|
5b07941c4c | ||
|
|
23c30ec46d | ||
|
|
22c06300f1 | ||
|
|
37cca1b106 | ||
|
|
f0a49c806e | ||
|
|
a1d5f65588 | ||
|
|
b6789e14a4 | ||
|
|
c72ee30a25 | ||
|
|
686d667c4f | ||
|
|
26d603db5d | ||
|
|
409840aef9 | ||
|
|
1f3c264afe | ||
|
|
cd69d9a1a7 | ||
|
|
a000c8fd99 | ||
|
|
84d05603ef | ||
|
|
9d046ecd1d | ||
|
|
030e1809f3 | ||
|
|
5853bd4a96 | ||
|
|
10695ff51b | ||
|
|
f421659fea | ||
|
|
df4f51eff3 | ||
|
|
7e61d6a850 | ||
|
|
0910bd23c4 | ||
|
|
5a29753912 | ||
|
|
a467356437 | ||
|
|
094967f52a | ||
|
|
757b0812ba | ||
|
|
8f4f5d8c92 | ||
|
|
4e266483ba | ||
|
|
7433dc9cf3 | ||
|
|
409a773e36 | ||
|
|
48bdd5c3df | ||
|
|
5796057a75 | ||
|
|
c8248e91ca | ||
|
|
7317ce44e7 | ||
|
|
10e2a5b1eb | ||
|
|
e675d1b7e2 | ||
|
|
499c4dadd5 | ||
|
|
f550f84acd | ||
|
|
8021f1a7fc | ||
|
|
d50fe606ee | ||
|
|
281f014aa0 | ||
|
|
2f6da3e84a | ||
|
|
f9accc2a7a | ||
|
|
fe15078bbd | ||
|
|
27d4b5eba7 | ||
|
|
e378576632 | ||
|
|
74909da110 | ||
|
|
655bf112ac | ||
|
|
5c3638a5a4 | ||
|
|
2459a3654b | ||
|
|
e9a3456cf5 | ||
|
|
2bcdf774ff | ||
|
|
ef01f23384 | ||
|
|
ab7bbb6593 | ||
|
|
f2dad88875 | ||
|
|
c474565ff5 | ||
|
|
fd9dbbd7e6 | ||
|
|
43ab095e0f | ||
|
|
41e2888bf1 | ||
|
|
e7d5e22960 | ||
|
|
3f3884193d | ||
|
|
dfccabc2b9 | ||
|
|
af7c181596 | ||
|
|
8e84112837 | ||
|
|
822cd16daa | ||
|
|
33ea84004d | ||
|
|
8dbbe20840 | ||
|
|
d990ccc4ab | ||
|
|
9f16249898 | ||
|
|
62fc3139cf | ||
|
|
452452dcc9 | ||
|
|
b6fb06bc65 | ||
|
|
aac6bd1b7c | ||
|
|
d260001cbd | ||
|
|
9c470d77cb | ||
|
|
269075657d | ||
|
|
d0029beb22 | ||
|
|
4b4837e3a1 | ||
|
|
d0160b8b6d | ||
|
|
ea351935b2 | ||
|
|
20ccc2951e | ||
|
|
6f9bad1bdf | ||
|
|
39a0d514a8 | ||
|
|
ad82c591cc | ||
|
|
32ceb168d5 | ||
|
|
16146f4c88 | ||
|
|
60febbf3f8 | ||
|
|
4960b30cfb | ||
|
|
56c161fea0 | ||
|
|
27c439d728 | ||
|
|
78d5e03a66 | ||
|
|
f25e647b6a | ||
|
|
e44212fc65 | ||
|
|
a483403774 | ||
|
|
69750dba19 | ||
|
|
af962b99be |
228
.editorconfig
228
.editorconfig
@@ -6,3 +6,231 @@ insert_final_newline = true
|
|||||||
charset = latin1
|
charset = latin1
|
||||||
indent_style = tab
|
indent_style = tab
|
||||||
indent_size = 4
|
indent_size = 4
|
||||||
|
|
||||||
|
[*.java]
|
||||||
|
indent_style = tab
|
||||||
|
ij_continuation_indent_size = 4
|
||||||
|
ij_java_align_consecutive_assignments = false
|
||||||
|
ij_java_align_consecutive_variable_declarations = false
|
||||||
|
ij_java_align_group_field_declarations = false
|
||||||
|
ij_java_align_multiline_annotation_parameters = false
|
||||||
|
ij_java_align_multiline_array_initializer_expression = false
|
||||||
|
ij_java_align_multiline_assignment = false
|
||||||
|
ij_java_align_multiline_binary_operation = false
|
||||||
|
ij_java_align_multiline_chained_methods = false
|
||||||
|
ij_java_align_multiline_extends_list = false
|
||||||
|
ij_java_align_multiline_for = true
|
||||||
|
ij_java_align_multiline_method_parentheses = false
|
||||||
|
ij_java_align_multiline_parameters = false
|
||||||
|
ij_java_align_multiline_parameters_in_calls = false
|
||||||
|
ij_java_align_multiline_parenthesized_expression = false
|
||||||
|
ij_java_align_multiline_resources = false
|
||||||
|
ij_java_align_multiline_ternary_operation = false
|
||||||
|
ij_java_align_multiline_text_blocks = false
|
||||||
|
ij_java_align_multiline_throws_list = false
|
||||||
|
ij_java_align_subsequent_simple_methods = false
|
||||||
|
ij_java_align_throws_keyword = false
|
||||||
|
ij_java_annotation_parameter_wrap = off
|
||||||
|
ij_java_array_initializer_new_line_after_left_brace = false
|
||||||
|
ij_java_array_initializer_right_brace_on_new_line = false
|
||||||
|
ij_java_array_initializer_wrap = normal
|
||||||
|
ij_java_assert_statement_colon_on_next_line = false
|
||||||
|
ij_java_assert_statement_wrap = off
|
||||||
|
ij_java_assignment_wrap = off
|
||||||
|
ij_java_binary_operation_sign_on_next_line = false
|
||||||
|
ij_java_binary_operation_wrap = off
|
||||||
|
ij_java_blank_lines_after_anonymous_class_header = 0
|
||||||
|
ij_java_blank_lines_after_class_header = 0
|
||||||
|
ij_java_blank_lines_after_imports = 1
|
||||||
|
ij_java_blank_lines_after_package = 1
|
||||||
|
ij_java_blank_lines_around_class = 1
|
||||||
|
ij_java_blank_lines_around_field = 0
|
||||||
|
ij_java_blank_lines_around_field_in_interface = 0
|
||||||
|
ij_java_blank_lines_around_initializer = 1
|
||||||
|
ij_java_blank_lines_around_method = 1
|
||||||
|
ij_java_blank_lines_around_method_in_interface = 1
|
||||||
|
ij_java_blank_lines_before_class_end = 0
|
||||||
|
ij_java_blank_lines_before_imports = 1
|
||||||
|
ij_java_blank_lines_before_method_body = 0
|
||||||
|
ij_java_blank_lines_before_package = 0
|
||||||
|
ij_java_block_brace_style = next_line_if_wrapped
|
||||||
|
ij_java_block_comment_at_first_column = true
|
||||||
|
ij_java_call_parameters_new_line_after_left_paren = false
|
||||||
|
ij_java_call_parameters_right_paren_on_new_line = false
|
||||||
|
ij_java_call_parameters_wrap = normal
|
||||||
|
ij_java_case_statement_on_separate_line = true
|
||||||
|
ij_java_catch_on_new_line = false
|
||||||
|
ij_java_class_annotation_wrap = split_into_lines
|
||||||
|
ij_java_class_brace_style = next_line
|
||||||
|
ij_java_class_count_to_use_import_on_demand = 99
|
||||||
|
ij_java_class_names_in_javadoc = 1
|
||||||
|
ij_java_do_not_indent_top_level_class_members = false
|
||||||
|
ij_java_do_not_wrap_after_single_annotation = false
|
||||||
|
ij_java_do_while_brace_force = never
|
||||||
|
ij_java_doc_add_blank_line_after_description = true
|
||||||
|
ij_java_doc_add_blank_line_after_param_comments = false
|
||||||
|
ij_java_doc_add_blank_line_after_return = false
|
||||||
|
ij_java_doc_add_p_tag_on_empty_lines = true
|
||||||
|
ij_java_doc_align_exception_comments = true
|
||||||
|
ij_java_doc_align_param_comments = true
|
||||||
|
ij_java_doc_do_not_wrap_if_one_line = false
|
||||||
|
ij_java_doc_enable_formatting = true
|
||||||
|
ij_java_doc_enable_leading_asterisks = true
|
||||||
|
ij_java_doc_indent_on_continuation = false
|
||||||
|
ij_java_doc_keep_empty_lines = true
|
||||||
|
ij_java_doc_keep_empty_parameter_tag = true
|
||||||
|
ij_java_doc_keep_empty_return_tag = true
|
||||||
|
ij_java_doc_keep_empty_throws_tag = true
|
||||||
|
ij_java_doc_keep_invalid_tags = true
|
||||||
|
ij_java_doc_param_description_on_new_line = false
|
||||||
|
ij_java_doc_preserve_line_breaks = false
|
||||||
|
ij_java_doc_use_throws_not_exception_tag = true
|
||||||
|
ij_java_else_on_new_line = false
|
||||||
|
ij_java_enum_constants_wrap = off
|
||||||
|
ij_java_extends_keyword_wrap = split_into_lines
|
||||||
|
ij_java_extends_list_wrap = normal
|
||||||
|
ij_java_field_annotation_wrap = split_into_lines
|
||||||
|
ij_java_finally_on_new_line = false
|
||||||
|
ij_java_for_brace_force = never
|
||||||
|
ij_java_for_statement_new_line_after_left_paren = false
|
||||||
|
ij_java_for_statement_right_paren_on_new_line = false
|
||||||
|
ij_java_for_statement_wrap = off
|
||||||
|
ij_java_generate_final_locals = false
|
||||||
|
ij_java_generate_final_parameters = false
|
||||||
|
ij_java_if_brace_force = never
|
||||||
|
ij_java_imports_layout = java.**,javax.**,*,$*
|
||||||
|
ij_java_indent_case_from_switch = true
|
||||||
|
ij_java_insert_inner_class_imports = false
|
||||||
|
ij_java_insert_override_annotation = true
|
||||||
|
ij_java_keep_blank_lines_before_right_brace = 1
|
||||||
|
ij_java_keep_blank_lines_between_package_declaration_and_header = 1
|
||||||
|
ij_java_keep_blank_lines_in_code = 1
|
||||||
|
ij_java_keep_blank_lines_in_declarations = 1
|
||||||
|
ij_java_keep_control_statement_in_one_line = false
|
||||||
|
ij_java_keep_first_column_comment = false
|
||||||
|
ij_java_keep_indents_on_empty_lines = false
|
||||||
|
ij_java_keep_line_breaks = false
|
||||||
|
ij_java_keep_multiple_expressions_in_one_line = false
|
||||||
|
ij_java_keep_simple_blocks_in_one_line = false
|
||||||
|
ij_java_keep_simple_classes_in_one_line = false
|
||||||
|
ij_java_keep_simple_lambdas_in_one_line = false
|
||||||
|
ij_java_keep_simple_methods_in_one_line = false
|
||||||
|
ij_java_label_indent_absolute = false
|
||||||
|
ij_java_label_indent_size = 0
|
||||||
|
ij_java_lambda_brace_style = end_of_line
|
||||||
|
ij_java_layout_static_imports_separately = true
|
||||||
|
ij_java_line_comment_add_space = false
|
||||||
|
ij_java_line_comment_at_first_column = true
|
||||||
|
ij_java_method_annotation_wrap = split_into_lines
|
||||||
|
ij_java_method_brace_style = next_line_if_wrapped
|
||||||
|
ij_java_method_call_chain_wrap = normal
|
||||||
|
ij_java_method_parameters_new_line_after_left_paren = false
|
||||||
|
ij_java_method_parameters_right_paren_on_new_line = false
|
||||||
|
ij_java_method_parameters_wrap = normal
|
||||||
|
ij_java_modifier_list_wrap = false
|
||||||
|
ij_java_names_count_to_use_import_on_demand = 3
|
||||||
|
ij_java_parameter_annotation_wrap = off
|
||||||
|
ij_java_parentheses_expression_new_line_after_left_paren = false
|
||||||
|
ij_java_parentheses_expression_right_paren_on_new_line = false
|
||||||
|
ij_java_place_assignment_sign_on_next_line = false
|
||||||
|
ij_java_prefer_longer_names = true
|
||||||
|
ij_java_prefer_parameters_wrap = false
|
||||||
|
ij_java_repeat_synchronized = true
|
||||||
|
ij_java_replace_instanceof_and_cast = false
|
||||||
|
ij_java_replace_null_check = true
|
||||||
|
ij_java_replace_sum_lambda_with_method_ref = true
|
||||||
|
ij_java_resource_list_new_line_after_left_paren = false
|
||||||
|
ij_java_resource_list_right_paren_on_new_line = false
|
||||||
|
ij_java_resource_list_wrap = normal
|
||||||
|
ij_java_space_after_closing_angle_bracket_in_type_argument = false
|
||||||
|
ij_java_space_after_colon = true
|
||||||
|
ij_java_space_after_comma = true
|
||||||
|
ij_java_space_after_comma_in_type_arguments = true
|
||||||
|
ij_java_space_after_for_semicolon = true
|
||||||
|
ij_java_space_after_quest = true
|
||||||
|
ij_java_space_after_type_cast = true
|
||||||
|
ij_java_space_before_annotation_array_initializer_left_brace = true
|
||||||
|
ij_java_space_before_annotation_parameter_list = false
|
||||||
|
ij_java_space_before_array_initializer_left_brace = true
|
||||||
|
ij_java_space_before_catch_keyword = true
|
||||||
|
ij_java_space_before_catch_left_brace = true
|
||||||
|
ij_java_space_before_catch_parentheses = false
|
||||||
|
ij_java_space_before_class_left_brace = true
|
||||||
|
ij_java_space_before_colon = true
|
||||||
|
ij_java_space_before_colon_in_foreach = true
|
||||||
|
ij_java_space_before_comma = false
|
||||||
|
ij_java_space_before_do_left_brace = true
|
||||||
|
ij_java_space_before_else_keyword = true
|
||||||
|
ij_java_space_before_else_left_brace = true
|
||||||
|
ij_java_space_before_finally_keyword = true
|
||||||
|
ij_java_space_before_finally_left_brace = true
|
||||||
|
ij_java_space_before_for_left_brace = true
|
||||||
|
ij_java_space_before_for_parentheses = false
|
||||||
|
ij_java_space_before_for_semicolon = false
|
||||||
|
ij_java_space_before_if_left_brace = true
|
||||||
|
ij_java_space_before_if_parentheses = false
|
||||||
|
ij_java_space_before_method_call_parentheses = false
|
||||||
|
ij_java_space_before_method_left_brace = true
|
||||||
|
ij_java_space_before_method_parentheses = false
|
||||||
|
ij_java_space_before_opening_angle_bracket_in_type_parameter = false
|
||||||
|
ij_java_space_before_quest = true
|
||||||
|
ij_java_space_before_switch_left_brace = true
|
||||||
|
ij_java_space_before_switch_parentheses = false
|
||||||
|
ij_java_space_before_synchronized_left_brace = true
|
||||||
|
ij_java_space_before_synchronized_parentheses = false
|
||||||
|
ij_java_space_before_try_left_brace = true
|
||||||
|
ij_java_space_before_try_parentheses = false
|
||||||
|
ij_java_space_before_type_parameter_list = false
|
||||||
|
ij_java_space_before_while_keyword = true
|
||||||
|
ij_java_space_before_while_left_brace = true
|
||||||
|
ij_java_space_before_while_parentheses = false
|
||||||
|
ij_java_space_inside_one_line_enum_braces = false
|
||||||
|
ij_java_space_within_empty_array_initializer_braces = false
|
||||||
|
ij_java_space_within_empty_method_call_parentheses = false
|
||||||
|
ij_java_space_within_empty_method_parentheses = false
|
||||||
|
ij_java_spaces_around_additive_operators = true
|
||||||
|
ij_java_spaces_around_assignment_operators = true
|
||||||
|
ij_java_spaces_around_bitwise_operators = true
|
||||||
|
ij_java_spaces_around_equality_operators = true
|
||||||
|
ij_java_spaces_around_lambda_arrow = true
|
||||||
|
ij_java_spaces_around_logical_operators = true
|
||||||
|
ij_java_spaces_around_method_ref_dbl_colon = false
|
||||||
|
ij_java_spaces_around_multiplicative_operators = true
|
||||||
|
ij_java_spaces_around_relational_operators = true
|
||||||
|
ij_java_spaces_around_shift_operators = true
|
||||||
|
ij_java_spaces_around_type_bounds_in_type_parameters = true
|
||||||
|
ij_java_spaces_around_unary_operator = false
|
||||||
|
ij_java_spaces_within_angle_brackets = false
|
||||||
|
ij_java_spaces_within_annotation_parentheses = true
|
||||||
|
ij_java_spaces_within_array_initializer_braces = true
|
||||||
|
ij_java_spaces_within_braces = false
|
||||||
|
ij_java_spaces_within_brackets = false
|
||||||
|
ij_java_spaces_within_cast_parentheses = false
|
||||||
|
ij_java_spaces_within_catch_parentheses = true
|
||||||
|
ij_java_spaces_within_for_parentheses = true
|
||||||
|
ij_java_spaces_within_if_parentheses = true
|
||||||
|
ij_java_spaces_within_method_call_parentheses = true
|
||||||
|
ij_java_spaces_within_method_parentheses = true
|
||||||
|
ij_java_spaces_within_parentheses = false
|
||||||
|
ij_java_spaces_within_switch_parentheses = true
|
||||||
|
ij_java_spaces_within_synchronized_parentheses = true
|
||||||
|
ij_java_spaces_within_try_parentheses = true
|
||||||
|
ij_java_spaces_within_while_parentheses = true
|
||||||
|
ij_java_special_else_if_treatment = true
|
||||||
|
ij_java_subclass_name_suffix = Impl
|
||||||
|
ij_java_ternary_operation_signs_on_next_line = true
|
||||||
|
ij_java_ternary_operation_wrap = on_every_item
|
||||||
|
ij_java_test_name_suffix = Test
|
||||||
|
ij_java_throws_keyword_wrap = normal
|
||||||
|
ij_java_throws_list_wrap = normal
|
||||||
|
ij_java_use_external_annotations = false
|
||||||
|
ij_java_use_fq_class_names = false
|
||||||
|
ij_java_use_relative_indents = false
|
||||||
|
ij_java_use_single_class_imports = true
|
||||||
|
ij_java_variable_annotation_wrap = off
|
||||||
|
ij_java_visibility = public
|
||||||
|
ij_java_while_brace_force = never
|
||||||
|
ij_java_while_on_new_line = false
|
||||||
|
ij_java_wrap_comments = false
|
||||||
|
ij_java_wrap_first_method_in_call_chain = false
|
||||||
|
ij_java_wrap_long_lines = false
|
||||||
|
|||||||
28
.travis.yml
28
.travis.yml
@@ -5,7 +5,7 @@ jdk:
|
|||||||
- openjdk8
|
- openjdk8
|
||||||
- openjdk9
|
- openjdk9
|
||||||
- openjdk11
|
- openjdk11
|
||||||
- openjdk13
|
- openjdk14
|
||||||
|
|
||||||
before_cache:
|
before_cache:
|
||||||
- rm -f $HOME/.gradle/caches/modules-2/modules-2.lock
|
- rm -f $HOME/.gradle/caches/modules-2/modules-2.lock
|
||||||
@@ -19,11 +19,21 @@ before_install:
|
|||||||
- ./gradlew --version
|
- ./gradlew --version
|
||||||
- java -version
|
- java -version
|
||||||
|
|
||||||
deploy:
|
stages:
|
||||||
provider: script
|
- name: test
|
||||||
script: ./gradlew bintrayUpload
|
- name: snapshot
|
||||||
skip_cleanup: true # to upload artifacts created during the build
|
if: branch = master AND type IN (push) AND tag IS blank
|
||||||
on:
|
- name: release
|
||||||
branch: master
|
if: type IN (push) AND tag IS present
|
||||||
jdk: openjdk11
|
|
||||||
tags: true
|
jobs:
|
||||||
|
include:
|
||||||
|
# publish snapshot to oss.jfrog.org
|
||||||
|
- stage: snapshot
|
||||||
|
jdk: openjdk11
|
||||||
|
script: ./gradlew artifactoryPublish
|
||||||
|
|
||||||
|
# release a new stable version to bintray
|
||||||
|
- stage: release
|
||||||
|
jdk: openjdk11
|
||||||
|
script: ./gradlew bintrayUpload -Drelease=true
|
||||||
|
|||||||
273
CHANGELOG.md
273
CHANGELOG.md
@@ -1,6 +1,279 @@
|
|||||||
FlatLaf Change Log
|
FlatLaf Change Log
|
||||||
==================
|
==================
|
||||||
|
|
||||||
|
## 0.36
|
||||||
|
|
||||||
|
- ScrollBar: Made styling more flexible by supporting insets and arc for track
|
||||||
|
and thumb. (issue #103)
|
||||||
|
- ComboBox: Minimum width is now 72 pixels (was ~50 for non-editable and ~130
|
||||||
|
for editable comboboxes).
|
||||||
|
- ComboBox: Support custom borders in combobox editors. (issue #102)
|
||||||
|
- Button: Support non-square icon-only buttons. (issue #110)
|
||||||
|
- Ubuntu Linux: Fixed poorly rendered font. (issue #105)
|
||||||
|
- macOS Catalina: Use Helvetica Neue font.
|
||||||
|
- `FlatInspector` added (see [FlatLaf Extras](flatlaf-extras)).
|
||||||
|
|
||||||
|
|
||||||
|
## 0.35
|
||||||
|
|
||||||
|
- Added drop shadows to popup menus, combobox popups, tooltips and internal
|
||||||
|
frames. (issue #94)
|
||||||
|
- Support different component border colors to indicate errors, warnings or
|
||||||
|
custom state (set client property `JComponent.outline` to `error`, `warning`
|
||||||
|
or any `java.awt.Color`).
|
||||||
|
- Button and ToggleButton: Support round button style (set client property
|
||||||
|
`JButton.buttonType` to `roundRect`).
|
||||||
|
- ComboBox, Spinner and TextField: Support round border style (set client
|
||||||
|
property `JComponent.roundRect` to `true`).
|
||||||
|
- Paint nicely rounded buttons, comboboxes, spinners and text fields when
|
||||||
|
setting `Button.arc`, `Component.arc` or `TextComponent.arc` to a large value
|
||||||
|
(e.g. 1000).
|
||||||
|
- Added Java 9 module descriptor to `flatlaf-extras-<version>.jar` and
|
||||||
|
`flatlaf-swingx-<version>.jar`.
|
||||||
|
- CheckBox and RadioButton: Flag `opaque` is no longer ignored when checkbox or
|
||||||
|
radio button is used as table cell renderer. (issue #77)
|
||||||
|
- FileChooser: Use system icons. (issue #100)
|
||||||
|
- FileChooser: Fixed missing labels in file chooser when running on Java 9 or
|
||||||
|
later. (issue #98)
|
||||||
|
- PasswordField: Do not apply minimum width if `columns` property is greater
|
||||||
|
than zero.
|
||||||
|
|
||||||
|
|
||||||
|
## 0.34
|
||||||
|
|
||||||
|
- Menus: New menu item renderer brings stable left margins, right aligned
|
||||||
|
accelerators and larger gap between text and accelerator. This makes menus
|
||||||
|
look more modern and more similar to native platform menus.
|
||||||
|
- New underline menu selection style that displays selected menu items similar
|
||||||
|
to tabs (to enable use `UIManager.put( "MenuItem.selectionType", "underline"
|
||||||
|
);`).
|
||||||
|
- Menus: Fixed text color of selected menu items that use HTML. (issue #87)
|
||||||
|
- Menus: On Windows, pressing <kbd>F10</kbd> now activates the menu bar without
|
||||||
|
showing a menu popup (as usual on Windows platform). On other platforms the
|
||||||
|
first menu popup is shown.
|
||||||
|
- Menus: On Windows, releasing <kbd>Alt</kbd> key now activates the menu bar (as
|
||||||
|
usual on Windows platform). (issue #43)
|
||||||
|
- Menus: Fixed inconsistent left padding in menu items. (issue #3)
|
||||||
|
- Menus: Fixed: Setting `iconTextGap` property on a menu item did increase left
|
||||||
|
and right margins. (issue #54)
|
||||||
|
- Hide mnemonics if window is deactivated (e.g. <kbd>Alt+Tab</kbd> to another
|
||||||
|
window). (issue #43)
|
||||||
|
- macOS: Enabled drop shadows for popup menus and combobox popups. (issue #94)
|
||||||
|
- macOS: Fixed NPE if using `JMenuBar` in `JInternalFrame` and macOS screen menu
|
||||||
|
bar is enabled (with `-Dapple.laf.useScreenMenuBar=true`). (issue #90)
|
||||||
|
|
||||||
|
|
||||||
|
## 0.33
|
||||||
|
|
||||||
|
- Improved creation of disabled grayscale icons used in disabled buttons, labels
|
||||||
|
and tabs. They now have more contrast and are lighter in light themes and
|
||||||
|
darker in dark themes. (issue #70)
|
||||||
|
- IntelliJ Themes: Fixed ComboBox size and Spinner border in all Material UI
|
||||||
|
Lite themes and limit tree row height in all Material UI Lite themes and some
|
||||||
|
other themes.
|
||||||
|
- IntelliJ Themes: Material UI Lite themes did not work when using
|
||||||
|
[IntelliJ Themes Pack](flatlaf-intellij-themes) addon. (PR #88, issue #89)
|
||||||
|
- IntelliJ Themes: Added Java 9 module descriptor to
|
||||||
|
`flatlaf-intellij-themes-<version>.jar`.
|
||||||
|
|
||||||
|
|
||||||
|
## 0.32
|
||||||
|
|
||||||
|
- New [IntelliJ Themes Pack](flatlaf-intellij-themes) addon bundles many popular
|
||||||
|
open-source 3rd party themes from JetBrains Plugins Repository into a JAR and
|
||||||
|
provides Java classes to use them.
|
||||||
|
- IntelliJ Themes: Fixed button and toggle button colors. (issue #86)
|
||||||
|
- Updated IntelliJ Themes in demo to the latest versions.
|
||||||
|
- ToggleButton: Compute selected background color based on current component
|
||||||
|
background. (issue #32)
|
||||||
|
|
||||||
|
|
||||||
|
## 0.31
|
||||||
|
|
||||||
|
- Focus indication border (or background) no longer hidden when temporary
|
||||||
|
loosing focus (e.g. showing a popup menu).
|
||||||
|
- List, Table and Tree: Item selection color of focused components no longer
|
||||||
|
change from blue to gray when temporary loosing focus (e.g. showing a popup
|
||||||
|
menu).
|
||||||
|
|
||||||
|
|
||||||
|
## 0.30
|
||||||
|
|
||||||
|
- Windows: Fixed rendering of Unicode characters. Previously not all Unicode
|
||||||
|
characters were rendered on Windows. (issue #81)
|
||||||
|
|
||||||
|
|
||||||
|
## 0.29
|
||||||
|
|
||||||
|
- Linux: Fixed scaling if `GDK_SCALE` environment variable is set or if running
|
||||||
|
on JetBrains Runtime. (issue #69)
|
||||||
|
- Tree: Fixed repainting wide selection on focus gained/lost.
|
||||||
|
- ComboBox: No longer ignore `JComboBox.prototypeDisplayValue` when computing
|
||||||
|
popup width. (issue #80)
|
||||||
|
- Support changing default font used for all components with automatic scaling
|
||||||
|
UI if using larger font. Use `UIManager.put( "defaultFont", myFont );`
|
||||||
|
- No longer use system property `sun.java2d.uiScale`. (Java 8 only)
|
||||||
|
- Support specifying custom scale factor in system property `flatlaf.uiScale`
|
||||||
|
also for Java 9 and later.
|
||||||
|
- Demo: Support using own FlatLaf themes (`.properties` files) that are located
|
||||||
|
in working directory of Demo application. Shown in the "Themes" list under
|
||||||
|
category "Current Directory".
|
||||||
|
|
||||||
|
|
||||||
|
## 0.28
|
||||||
|
|
||||||
|
- PasswordField: Warn about enabled Caps Lock.
|
||||||
|
- TabbedPane: Support <kbd>Ctrl+TAB</kbd> / <kbd>Ctrl+Shift+TAB</kbd> to switch
|
||||||
|
to next / previous tab.
|
||||||
|
- TextField, FormattedTextField and PasswordField: Support round borders (see UI
|
||||||
|
default value `TextComponent.arc`). (issue #65)
|
||||||
|
- IntelliJ Themes: Added Gradianto themes to demo.
|
||||||
|
- Button, CheckBox and RadioButton: Fixed NPE when button has children. (PR #68)
|
||||||
|
- ScrollBar: Improved colors.
|
||||||
|
- Reviewed (and tested) all key bindings on Windows and macOS. Linux key
|
||||||
|
bindings are equal to Windows key bindings. macOS key bindings are slightly
|
||||||
|
different for platform specific behavior.
|
||||||
|
- UI default values are no longer based on Metal/Aqua UI defaults.
|
||||||
|
|
||||||
|
|
||||||
|
## 0.27
|
||||||
|
|
||||||
|
- Support `JInternalFrame` and `JDesktopPane`. (issues #39 and #11)
|
||||||
|
- Table: Support positioning the column sort arrow in header right, left, top or
|
||||||
|
bottom. (issue #34)
|
||||||
|
- ProgressBar: Fixed visual artifacts in indeterminate mode, on HiDPI screens at
|
||||||
|
125%, 150% and 175% scaling, when the progress moves around.
|
||||||
|
- TabbedPane: New option to allow tab separators to take full height (to enable
|
||||||
|
use `UIManager.put( "TabbedPane.tabSeparatorsFullHeight", true );`). (issue
|
||||||
|
#59, PR #62)
|
||||||
|
- CheckBox and RadioButton: Do not fill background if `contentAreaFilled` is
|
||||||
|
`false`. (issue #58, PR #63)
|
||||||
|
- ToggleButton: Make toggle button square if it has an icon but no text or text
|
||||||
|
is "..." or a single character.
|
||||||
|
- ToolBar: No longer use special rollover border for buttons in toolbar. (issue
|
||||||
|
#36)
|
||||||
|
- ToolBar: Added empty space around buttons in toolbar and toolbar itself (see
|
||||||
|
UI default values `Button.toolbar.spacingInsets` and `ToolBar.borderMargins`).
|
||||||
|
(issue #56)
|
||||||
|
- Fixed "illegal reflective access operation" warning on macOS when using Java
|
||||||
|
12 or later. (issue #60, PR #61)
|
||||||
|
|
||||||
|
|
||||||
|
## 0.26
|
||||||
|
|
||||||
|
- Menus:
|
||||||
|
- Changed menu bar and popup menu background colors (made brighter in light
|
||||||
|
themes and darker in dark themes).
|
||||||
|
- Highlight items in menu bar on mouse hover. (issue #49)
|
||||||
|
- Popup menus now have empty space at the top and bottom.
|
||||||
|
- Menu items now have larger left and right margins.
|
||||||
|
- Made `JMenu`, `JMenuItem`, `JCheckBoxMenuItem` and `JRadioButtonMenuItem`
|
||||||
|
non-opaque.
|
||||||
|
- TextField, FormattedTextField and PasswordField: Select all text when a text
|
||||||
|
field gains focus for the first time and selection was not set explicitly.
|
||||||
|
This can be configured to newer or always select all text on focus gain (see
|
||||||
|
UI default value `TextComponent.selectAllOnFocusPolicy`).
|
||||||
|
- ProgressBar: Made progress bar paint smooth in indeterminate mode.
|
||||||
|
|
||||||
|
|
||||||
|
## 0.25.1
|
||||||
|
|
||||||
|
Re-release of 0.25 because of problems with Maven Central.
|
||||||
|
|
||||||
|
|
||||||
|
## 0.25
|
||||||
|
|
||||||
|
- Hide menu mnemonics by default and show them only when <kbd>Alt</kbd> key is
|
||||||
|
pressed. (issue #43)
|
||||||
|
- Menu: Fixed vertical alignment of sub-menus. (issue #42)
|
||||||
|
- TabbedPane: In scroll-tab-layout, the cropped line is now hidden. (issue #40)
|
||||||
|
- Tree: UI default value `Tree.textBackground` now has a valid color and is no
|
||||||
|
longer `null`.
|
||||||
|
- Tree on macOS: Fixed <kbd>Left</kbd> and <kbd>Right</kbd> keys to collapse or
|
||||||
|
expand nodes.
|
||||||
|
- ComboBox on macOS: Fixed keyboard navigation and show/hide popup.
|
||||||
|
- Button and ToggleButton: Support per component minimum height (set client
|
||||||
|
property `JComponent.minimumHeight` to an integer). (issue #44)
|
||||||
|
- Button and ToggleButton: Do not apply minimum width if button border was
|
||||||
|
changed (is no longer an instance of `FlatButtonBorder`).
|
||||||
|
- ToggleButton: Renamed toggle button type "underline" to "tab" (value of client
|
||||||
|
property `JButton.buttonType` is now `tab`).
|
||||||
|
- ToggleButton: Support per component styling for tab-style toggle buttons with
|
||||||
|
client properties `JToggleButton.tab.underlineHeight` (integer),
|
||||||
|
`JToggleButton.tab.underlineColor` (Color) and
|
||||||
|
`JToggleButton.tab.selectedBackground` (Color). (issue #45)
|
||||||
|
- ToggleButton: No longer use focus width for tab-style toggle buttons to
|
||||||
|
compute component size, which reduces/fixes component size in "Flat IntelliJ"
|
||||||
|
and "Flat Darcula" themes.
|
||||||
|
- TabbedPane: Support per component tab height (set client property
|
||||||
|
`JTabbedPane.tabHeight` to an integer).
|
||||||
|
- ProgressBar: Support square painting (set client property
|
||||||
|
`JProgressBar.square` to `true`) and larger height even if no string is
|
||||||
|
painted (set client property `JProgressBar.largeHeight` to `true`).
|
||||||
|
|
||||||
|
|
||||||
|
## 0.24
|
||||||
|
|
||||||
|
- Support smooth scrolling with touchpads and high precision mouse wheels.
|
||||||
|
(issue #27)
|
||||||
|
- Changed `.properties` file loading order: Now all core `.properties` files are
|
||||||
|
loaded before loading addon `.properties` files. This makes it easier to
|
||||||
|
overwrite core values in addons. Also, addon loading order can be specified.
|
||||||
|
- TableHeader: Paint column borders if renderer has changed, but delegates to
|
||||||
|
the system default renderer (e.g. done in NetBeans).
|
||||||
|
- Label and ToolTip: Fixed font sizes for HTML headings.
|
||||||
|
- Button and ToggleButton: Support square button style (set client property
|
||||||
|
`JButton.buttonType` to `square`).
|
||||||
|
- ToggleButton: Support underline toggle button style (set client property
|
||||||
|
`JButton.buttonType` to `underline`).
|
||||||
|
- Button and TextComponent: Support per component minimum width (set client
|
||||||
|
property `JComponent.minimumWidth` to an integer).
|
||||||
|
- ScrollPane with Table: The border of buttons that are added to one of the four
|
||||||
|
scroll pane corners are now removed if the center component is a table. Also,
|
||||||
|
these corner buttons are made not focusable.
|
||||||
|
- Table: Replaced `Table.showGrid` with `Table.showHorizontalLines` and
|
||||||
|
`Table.showVerticalLines`. (issue #38)
|
||||||
|
- ProgressBar: Now uses blueish color for the progress part in "Flat Dark"
|
||||||
|
theme. In the "Flat Darcula" theme, it remains light gray.
|
||||||
|
- Improved Swing system colors `controlHighlight`, `controlLtHighlight`,
|
||||||
|
`controlShadow` and `controlDkShadow`.
|
||||||
|
|
||||||
|
|
||||||
|
## 0.23.1
|
||||||
|
|
||||||
|
- Tree: Fixed wide selection if scrolled horizontally.
|
||||||
|
- ComboBox: Fixed NPE in Oracle SQL Developer settings.
|
||||||
|
- IntelliJ Themes: Fixed checkbox colors in Material UI Lite dark themes.
|
||||||
|
|
||||||
|
|
||||||
|
## 0.23
|
||||||
|
|
||||||
|
- Updated colors in "Flat Light" and "Flat IntelliJ" themes with colors from
|
||||||
|
"IntelliJ Light Theme", which provides blue coloring that better matches
|
||||||
|
platform colors.
|
||||||
|
- Tree: Support wide selection (enabled by default).
|
||||||
|
- Table: Hide grid and changed intercell spacing to zero.
|
||||||
|
- List, Table and Tree: Added colors for drag-and-drop. Added "enable drag and
|
||||||
|
drop" checkbox to Demo on "Data Components" tab.
|
||||||
|
- List and Tree: Hide cell focus indicator (black rectangle) by default. Can be
|
||||||
|
enabled with `List.showCellFocusIndicator=true` /
|
||||||
|
`Tree.showCellFocusIndicator=true`, but then the cell focus indicator is shown
|
||||||
|
only if more than one item is selected.
|
||||||
|
- Table: Hide cell focus indicator (black rectangle) by default if none of the
|
||||||
|
selected cells is editable. Can be show always with
|
||||||
|
`Table.showCellFocusIndicator=true`.
|
||||||
|
- Support basic color functions in `.properties` files: `rgb(red,green,blue)`,
|
||||||
|
`rgba(red,green,blue,alpha)`, `hsl(hue,saturation,lightness)`,
|
||||||
|
`hsla(hue,saturation,lightness,alpha)`, `lighten(color,amount[,options])` and
|
||||||
|
`darken(color,amount[,options])`.
|
||||||
|
- Replaced prefix `@@` with `$` in `.properties` files.
|
||||||
|
- Fixed link color (in HTML text) and separator color in IntelliJ platform
|
||||||
|
themes.
|
||||||
|
- Use logging instead of printing errors to `System.err`.
|
||||||
|
- Updated IntelliJ Themes in demo to the latest versions.
|
||||||
|
- IntelliJ Themes: Fixed link and separator colors.
|
||||||
|
|
||||||
|
|
||||||
## 0.22
|
## 0.22
|
||||||
|
|
||||||
- TextComponent: Support placeholder text that is displayed if text field is
|
- TextComponent: Support placeholder text that is displayed if text field is
|
||||||
|
|||||||
55
README.md
55
README.md
@@ -19,7 +19,8 @@ IntelliJ IDEA 2019.2+ and uses almost the same colors and icons.
|
|||||||
IntelliJ Platform Themes
|
IntelliJ Platform Themes
|
||||||
------------------------
|
------------------------
|
||||||
|
|
||||||
FlatLaf can use 3rd party themes created for IntelliJ Platform:
|
FlatLaf can use 3rd party themes created for IntelliJ Platform (see
|
||||||
|
[IntelliJ Themes Pack](flatlaf-intellij-themes)):
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
@@ -45,20 +46,70 @@ build script:
|
|||||||
|
|
||||||
groupId: com.formdev
|
groupId: com.formdev
|
||||||
artifactId: flatlaf
|
artifactId: flatlaf
|
||||||
version: 0.22
|
version: (see button below)
|
||||||
|
|
||||||
Otherwise download `flatlaf-<version>.jar` here:
|
Otherwise download `flatlaf-<version>.jar` here:
|
||||||
|
|
||||||
[](https://bintray.com/jformdesigner/flatlaf/flatlaf/_latestVersion)
|
[](https://bintray.com/jformdesigner/flatlaf/flatlaf/_latestVersion)
|
||||||
|
|
||||||
|
|
||||||
|
### Snapshots
|
||||||
|
|
||||||
|
FlatLaf snapshot binaries are available in
|
||||||
|
[JFrog Artifactory](https://oss.jfrog.org/artifactory/oss-snapshot-local/com/formdev/).
|
||||||
|
To access the latest snapshot, change the FlatLaf version(s) in the dependencies
|
||||||
|
to `<version>-SNAPSHOT` (e.g. `0.27-SNAPSHOT`) and add the repository
|
||||||
|
`https://oss.jfrog.org/artifactory/oss-snapshot-local` to your build (see
|
||||||
|
[Maven](https://maven.apache.org/guides/mini/guide-multiple-repositories.html)
|
||||||
|
and
|
||||||
|
[Gradle](https://docs.gradle.org/current/userguide/declaring_repositories.html#sec:declaring_custom_repository)
|
||||||
|
docs).
|
||||||
|
|
||||||
|
|
||||||
Addons
|
Addons
|
||||||
------
|
------
|
||||||
|
|
||||||
|
- [IntelliJ Themes Pack](flatlaf-intellij-themes)
|
||||||
|
- [Extras](flatlaf-extras)
|
||||||
- [SwingX](flatlaf-swingx)
|
- [SwingX](flatlaf-swingx)
|
||||||
- [JIDE Common Layer](flatlaf-jide-oss)
|
- [JIDE Common Layer](flatlaf-jide-oss)
|
||||||
|
|
||||||
|
|
||||||
|
Projects using FlatLaf
|
||||||
|
----------------------
|
||||||
|
|
||||||
|
- [NetBeans](https://netbeans.apache.org/) 11.3
|
||||||
|
- [jclasslib bytecode viewer](https://github.com/ingokegel/jclasslib) 5.5
|
||||||
|
- [KeyStore Explorer](https://keystore-explorer.org/) 5.4.3
|
||||||
|
- [OWASP Zed Attack Proxy (ZAP)](https://www.zaproxy.org/) (in weekly releases)
|
||||||
|
- [XMLmind XML Editor](https://www.xmlmind.com/xmleditor/) 9.3 (commercial)
|
||||||
|
- [Total Validator](https://www.totalvalidator.com/) 15 (commercial)
|
||||||
|
- [j-lawyer](https://github.com/jlawyerorg/j-lawyer-org)
|
||||||
|
- [MegaMek](https://github.com/MegaMek/megamek) v0.47.4 and
|
||||||
|
[MekHQ](https://github.com/MegaMek/mekhq) v0.47.5
|
||||||
|
- [GUIslice Builder](https://github.com/ImpulseAdventure/GUIslice-Builder)
|
||||||
|
0.13.b024
|
||||||
|
- [Rest Suite](https://github.com/supanadit/restsuite)
|
||||||
|
- [ControllerBuddy](https://github.com/bwRavencl/ControllerBuddy)
|
||||||
|
- [SpringRemote](https://github.com/HaleyWang/SpringRemote)
|
||||||
|
- [mendelson AS2](https://sourceforge.net/projects/mec-as2/),
|
||||||
|
[AS4](https://sourceforge.net/projects/mendelson-as4/) and
|
||||||
|
[OFTP2](https://sourceforge.net/projects/mendelson-oftp2/) (open-source) and
|
||||||
|
[mendelson AS2](https://mendelson-e-c.com/as2/),
|
||||||
|
[AS4](https://mendelson-e-c.com/as4/) and
|
||||||
|
[OFTP2](https://mendelson-e-c.com/oftp2) (commercial)
|
||||||
|
- [MeteoInfo](https://github.com/meteoinfo/MeteoInfo) 2.1.6
|
||||||
|
- [lsfusion platform](https://github.com/lsfusion/platform)
|
||||||
|
- and more...
|
||||||
|
|
||||||
|
|
||||||
|
Buzz
|
||||||
|
----
|
||||||
|
|
||||||
|
- [What others say about FlatLaf on Twitter](https://twitter.com/search?f=live&q=flatlaf)
|
||||||
|
- [FlatLaf announcement on Reddit](https://www.reddit.com/r/java/comments/dl0hu3/flatlaf_flat_look_and_feel/)
|
||||||
|
|
||||||
|
|
||||||
Documentation
|
Documentation
|
||||||
-------------
|
-------------
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
@@ -14,9 +14,14 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
version = "0.22"
|
val releaseVersion = "0.36"
|
||||||
|
val developmentVersion = "0.37-SNAPSHOT"
|
||||||
|
|
||||||
|
version = if( java.lang.Boolean.getBoolean( "release" ) ) releaseVersion else developmentVersion
|
||||||
|
|
||||||
allprojects {
|
allprojects {
|
||||||
|
version = rootProject.version
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
jcenter()
|
jcenter()
|
||||||
}
|
}
|
||||||
@@ -33,3 +38,39 @@ println( "FlatLaf Version: ${version}" )
|
|||||||
println( "Gradle ${gradle.gradleVersion} at ${gradle.gradleHomeDir}" )
|
println( "Gradle ${gradle.gradleVersion} at ${gradle.gradleHomeDir}" )
|
||||||
println( "Java ${System.getProperty( "java.version" )}" )
|
println( "Java ${System.getProperty( "java.version" )}" )
|
||||||
println()
|
println()
|
||||||
|
|
||||||
|
|
||||||
|
extra["bintray.user"] = System.getenv( "BINTRAY_USER" ) ?: System.getProperty( "bintray.user" )
|
||||||
|
extra["bintray.key"] = System.getenv( "BINTRAY_KEY" ) ?: System.getProperty( "bintray.key" )
|
||||||
|
|
||||||
|
// if true, do not upload to bintray
|
||||||
|
extra["bintray.dryRun"] = false
|
||||||
|
|
||||||
|
// if true, uploaded artifacts are visible to all
|
||||||
|
// if false, only visible to owner when logged into bintray
|
||||||
|
extra["bintray.publish"] = true
|
||||||
|
|
||||||
|
|
||||||
|
allprojects {
|
||||||
|
tasks {
|
||||||
|
withType<JavaCompile>().configureEach {
|
||||||
|
sourceCompatibility = "1.8"
|
||||||
|
targetCompatibility = "1.8"
|
||||||
|
|
||||||
|
options.encoding = "ISO-8859-1"
|
||||||
|
}
|
||||||
|
|
||||||
|
withType<Jar>().configureEach {
|
||||||
|
// manifest for all created JARs
|
||||||
|
manifest.attributes(mapOf(
|
||||||
|
"Implementation-Vendor" to "FormDev Software GmbH",
|
||||||
|
"Implementation-Copyright" to "Copyright (C) ${java.time.LocalDate.now().year} FormDev Software GmbH. All rights reserved.",
|
||||||
|
"Implementation-Version" to project.version))
|
||||||
|
|
||||||
|
// add META-INF/LICENSE to all created JARs
|
||||||
|
from("${rootDir}/LICENSE") {
|
||||||
|
into("META-INF")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
34
buildSrc/build.gradle.kts
Normal file
34
buildSrc/build.gradle.kts
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2020 FormDev Software GmbH
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
plugins {
|
||||||
|
`kotlin-dsl`
|
||||||
|
}
|
||||||
|
|
||||||
|
// required for kotlin-dsl or embedded-kotlin plugins
|
||||||
|
repositories {
|
||||||
|
jcenter()
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
// NOTE: keep plugin versions in sync with settings.gradle.kts
|
||||||
|
|
||||||
|
// "com.jfrog.bintray" plugin
|
||||||
|
implementation( "com.jfrog.bintray.gradle:gradle-bintray-plugin:1.8.4" )
|
||||||
|
|
||||||
|
// "com.jfrog.artifactory" plugin
|
||||||
|
implementation( "org.jfrog.buildinfo:build-info-extractor-gradle:4.13.0" )
|
||||||
|
}
|
||||||
80
buildSrc/src/main/java/ReorderJarEntries.java
Normal file
80
buildSrc/src/main/java/ReorderJarEntries.java
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2020 FormDev Software GmbH
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.StandardOpenOption;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
import java.util.zip.ZipEntry;
|
||||||
|
import java.util.zip.ZipInputStream;
|
||||||
|
import java.util.zip.ZipOutputStream;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reorders entries in a JAR file so that .properties files are placed before .class files,
|
||||||
|
* which is necessary to workaround an issue in NetBeans 11.3 (and older).
|
||||||
|
* See issues #13 and #93.
|
||||||
|
*
|
||||||
|
* @author Karl Tauber
|
||||||
|
*/
|
||||||
|
public class ReorderJarEntries
|
||||||
|
{
|
||||||
|
public static void reorderJarEntries( File jarFile )
|
||||||
|
throws IOException
|
||||||
|
{
|
||||||
|
ByteArrayOutputStream outStream = new ByteArrayOutputStream( (int) jarFile.length() + 1000 );
|
||||||
|
|
||||||
|
try( ZipOutputStream zipOutStream = new ZipOutputStream( outStream ) ) {
|
||||||
|
// 1st pass: copy .properties files
|
||||||
|
copyFiles( zipOutStream, jarFile, name -> name.endsWith( ".properties" ) );
|
||||||
|
|
||||||
|
// 2st pass: copy other files
|
||||||
|
copyFiles( zipOutStream, jarFile, name -> !name.endsWith( ".properties" ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
// replace JAR
|
||||||
|
Files.write( jarFile.toPath(), outStream.toByteArray(),
|
||||||
|
StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING );
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void copyFiles( ZipOutputStream dest, File jarFile, Predicate<String> filter )
|
||||||
|
throws IOException
|
||||||
|
{
|
||||||
|
try( ZipInputStream zipInputStream = new ZipInputStream( new FileInputStream( jarFile ) ) ) {
|
||||||
|
ZipEntry entry;
|
||||||
|
while( (entry = zipInputStream.getNextEntry()) != null ) {
|
||||||
|
if( filter.test( entry.getName() ) ) {
|
||||||
|
dest.putNextEntry( entry );
|
||||||
|
copyFile( zipInputStream, dest );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void copyFile( InputStream src, OutputStream dest )
|
||||||
|
throws IOException
|
||||||
|
{
|
||||||
|
byte[] buf = new byte[8*1024];
|
||||||
|
int len;
|
||||||
|
while( (len = src.read( buf )) > 0 )
|
||||||
|
dest.write( buf, 0, len );
|
||||||
|
dest.flush();
|
||||||
|
}
|
||||||
|
}
|
||||||
44
buildSrc/src/main/kotlin/flatlaf-java9.gradle.kts
Normal file
44
buildSrc/src/main/kotlin/flatlaf-java9.gradle.kts
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2020 FormDev Software GmbH
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
plugins {
|
||||||
|
java
|
||||||
|
}
|
||||||
|
|
||||||
|
if( JavaVersion.current() >= JavaVersion.VERSION_1_9 ) {
|
||||||
|
sourceSets {
|
||||||
|
create( "java9" ) {
|
||||||
|
java {
|
||||||
|
setSrcDirs( listOf( "src/main/java9" ) )
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks {
|
||||||
|
named<JavaCompile>( "compileJava9Java" ) {
|
||||||
|
sourceCompatibility = "9"
|
||||||
|
targetCompatibility = "9"
|
||||||
|
}
|
||||||
|
|
||||||
|
jar {
|
||||||
|
manifest.attributes( "Multi-Release" to "true" )
|
||||||
|
|
||||||
|
into( "META-INF/versions/9" ) {
|
||||||
|
from( sourceSets["java9"].output )
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
64
buildSrc/src/main/kotlin/flatlaf-module-info.gradle.kts
Normal file
64
buildSrc/src/main/kotlin/flatlaf-module-info.gradle.kts
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2020 FormDev Software GmbH
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
open class ModuleInfoExtension {
|
||||||
|
var paths: ArrayList<String> = ArrayList()
|
||||||
|
|
||||||
|
fun dependsOn( vararg paths: String ) {
|
||||||
|
this.paths.addAll( paths )
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val extension = project.extensions.create<ModuleInfoExtension>( "flatlafModuleInfo" )
|
||||||
|
|
||||||
|
|
||||||
|
plugins {
|
||||||
|
java
|
||||||
|
}
|
||||||
|
|
||||||
|
if( JavaVersion.current() >= JavaVersion.VERSION_1_9 ) {
|
||||||
|
sourceSets {
|
||||||
|
create( "module-info" ) {
|
||||||
|
java {
|
||||||
|
// include "src/main/java" here to get compile errors if classes are
|
||||||
|
// used from other modules that are not specified in module dependencies
|
||||||
|
setSrcDirs( listOf( "src/main/module-info", "src/main/java" ) )
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks {
|
||||||
|
named<JavaCompile>( "compileModuleInfoJava" ) {
|
||||||
|
sourceCompatibility = "9"
|
||||||
|
targetCompatibility = "9"
|
||||||
|
|
||||||
|
dependsOn( extension.paths )
|
||||||
|
|
||||||
|
options.compilerArgs.add( "--module-path" )
|
||||||
|
options.compilerArgs.add( configurations.runtimeClasspath.get().asPath )
|
||||||
|
}
|
||||||
|
|
||||||
|
jar {
|
||||||
|
manifest.attributes( "Multi-Release" to "true" )
|
||||||
|
|
||||||
|
into( "META-INF/versions/9" ) {
|
||||||
|
from( sourceSets["module-info"].output ) {
|
||||||
|
include( "module-info.class" )
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
116
buildSrc/src/main/kotlin/flatlaf-publish.gradle.kts
Normal file
116
buildSrc/src/main/kotlin/flatlaf-publish.gradle.kts
Normal file
@@ -0,0 +1,116 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2020 FormDev Software GmbH
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
open class PublishExtension {
|
||||||
|
var artifactId: String? = null
|
||||||
|
var name: String? = null
|
||||||
|
var description: String? = null
|
||||||
|
}
|
||||||
|
|
||||||
|
val extension = project.extensions.create<PublishExtension>( "flatlafPublish" )
|
||||||
|
|
||||||
|
|
||||||
|
plugins {
|
||||||
|
`maven-publish`
|
||||||
|
id( "com.jfrog.bintray" )
|
||||||
|
id( "com.jfrog.artifactory" )
|
||||||
|
}
|
||||||
|
|
||||||
|
publishing {
|
||||||
|
publications {
|
||||||
|
create<MavenPublication>( "maven" ) {
|
||||||
|
afterEvaluate {
|
||||||
|
artifactId = extension.artifactId
|
||||||
|
}
|
||||||
|
groupId = "com.formdev"
|
||||||
|
|
||||||
|
from( components["java"] )
|
||||||
|
|
||||||
|
pom {
|
||||||
|
afterEvaluate {
|
||||||
|
this@pom.name.set( extension.name )
|
||||||
|
this@pom.description.set( extension.description )
|
||||||
|
}
|
||||||
|
url.set( "https://github.com/JFormDesigner/FlatLaf" )
|
||||||
|
|
||||||
|
licenses {
|
||||||
|
license {
|
||||||
|
name.set( "The Apache License, Version 2.0" )
|
||||||
|
url.set( "https://www.apache.org/licenses/LICENSE-2.0.txt" )
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
developers {
|
||||||
|
developer {
|
||||||
|
name.set( "Karl Tauber" )
|
||||||
|
organization.set( "FormDev Software GmbH" )
|
||||||
|
organizationUrl.set( "https://www.formdev.com/" )
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
scm {
|
||||||
|
url.set( "https://github.com/JFormDesigner/FlatLaf" )
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bintray {
|
||||||
|
user = rootProject.extra["bintray.user"] as String?
|
||||||
|
key = rootProject.extra["bintray.key"] as String?
|
||||||
|
|
||||||
|
setPublications( "maven" )
|
||||||
|
|
||||||
|
with( pkg ) {
|
||||||
|
repo = "flatlaf"
|
||||||
|
afterEvaluate {
|
||||||
|
this@with.name = extension.artifactId
|
||||||
|
}
|
||||||
|
setLicenses( "Apache-2.0" )
|
||||||
|
vcsUrl = "https://github.com/JFormDesigner/FlatLaf"
|
||||||
|
|
||||||
|
with( version ) {
|
||||||
|
name = project.version.toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
publish = rootProject.extra["bintray.publish"] as Boolean
|
||||||
|
dryRun = rootProject.extra["bintray.dryRun"] as Boolean
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
artifactory {
|
||||||
|
setContextUrl( "https://oss.jfrog.org" )
|
||||||
|
|
||||||
|
publish( closureOf<org.jfrog.gradle.plugin.artifactory.dsl.PublisherConfig> {
|
||||||
|
repository( delegateClosureOf<groovy.lang.GroovyObject> {
|
||||||
|
setProperty( "repoKey", "oss-snapshot-local" )
|
||||||
|
setProperty( "username", rootProject.extra["bintray.user"] as String? )
|
||||||
|
setProperty( "password", rootProject.extra["bintray.key"] as String? )
|
||||||
|
} )
|
||||||
|
|
||||||
|
defaults( delegateClosureOf<groovy.lang.GroovyObject> {
|
||||||
|
invokeMethod( "publications", "maven" )
|
||||||
|
setProperty( "publishArtifacts", true )
|
||||||
|
setProperty( "publishPom", true )
|
||||||
|
} )
|
||||||
|
} )
|
||||||
|
|
||||||
|
resolve( delegateClosureOf<org.jfrog.gradle.plugin.artifactory.dsl.ResolverConfig> {
|
||||||
|
setProperty( "repoKey", "jcenter" )
|
||||||
|
} )
|
||||||
|
}
|
||||||
@@ -5,7 +5,7 @@
|
|||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
@@ -14,134 +14,48 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
version = rootProject.version
|
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
`java-library`
|
`java-library`
|
||||||
`maven-publish`
|
`flatlaf-module-info`
|
||||||
id( "com.jfrog.bintray" ) version "1.8.4"
|
`flatlaf-java9`
|
||||||
}
|
`flatlaf-publish`
|
||||||
|
|
||||||
if( JavaVersion.current() >= JavaVersion.VERSION_1_9 ) {
|
|
||||||
sourceSets {
|
|
||||||
create( "module-info" ) {
|
|
||||||
java {
|
|
||||||
// include "src/main/java" here to get compile errors if classes are
|
|
||||||
// used from other modules that are not specified in module dependencies
|
|
||||||
setSrcDirs( listOf( "src/main/module-info", "src/main/java" ) )
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
java {
|
java {
|
||||||
sourceCompatibility = JavaVersion.VERSION_1_8
|
withSourcesJar()
|
||||||
targetCompatibility = JavaVersion.VERSION_1_8
|
withJavadocJar()
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks {
|
tasks {
|
||||||
assemble {
|
|
||||||
dependsOn(
|
|
||||||
"sourcesJar",
|
|
||||||
"javadocJar"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
if( JavaVersion.current() >= JavaVersion.VERSION_1_9 ) {
|
|
||||||
named<JavaCompile>( "compileModuleInfoJava" ) {
|
|
||||||
sourceCompatibility = "9"
|
|
||||||
targetCompatibility = "9"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
jar {
|
jar {
|
||||||
archiveBaseName.set( "flatlaf" )
|
archiveBaseName.set( "flatlaf" )
|
||||||
|
|
||||||
if( JavaVersion.current() >= JavaVersion.VERSION_1_9 ) {
|
doLast {
|
||||||
from( sourceSets["module-info"].output ) {
|
ReorderJarEntries.reorderJarEntries( outputs.files.singleFile );
|
||||||
include( "module-info.class" )
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
javadoc {
|
javadoc {
|
||||||
options {
|
options {
|
||||||
this as StandardJavadocDocletOptions
|
this as StandardJavadocDocletOptions
|
||||||
|
use( true )
|
||||||
tags = listOf( "uiDefault", "clientProperty" )
|
tags = listOf( "uiDefault", "clientProperty" )
|
||||||
|
addStringOption( "Xdoclint:all,-missing", "-Xdoclint:all,-missing" )
|
||||||
}
|
}
|
||||||
isFailOnError = false
|
isFailOnError = false
|
||||||
}
|
}
|
||||||
|
|
||||||
register( "sourcesJar", Jar::class ) {
|
named<Jar>("sourcesJar" ) {
|
||||||
archiveBaseName.set( "flatlaf" )
|
archiveBaseName.set( "flatlaf" )
|
||||||
archiveClassifier.set( "sources" )
|
|
||||||
|
|
||||||
from( sourceSets.main.get().allJava )
|
|
||||||
}
|
}
|
||||||
|
|
||||||
register( "javadocJar", Jar::class ) {
|
named<Jar>("javadocJar" ) {
|
||||||
archiveBaseName.set( "flatlaf" )
|
archiveBaseName.set( "flatlaf" )
|
||||||
archiveClassifier.set( "javadoc" )
|
|
||||||
|
|
||||||
from( javadoc )
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
publishing {
|
flatlafPublish {
|
||||||
publications {
|
artifactId = "flatlaf"
|
||||||
create<MavenPublication>( "maven" ) {
|
name = "FlatLaf"
|
||||||
artifactId = "flatlaf"
|
description = "Flat Look and Feel"
|
||||||
groupId = "com.formdev"
|
|
||||||
|
|
||||||
from( components["java"] )
|
|
||||||
|
|
||||||
artifact( tasks["sourcesJar"] )
|
|
||||||
artifact( tasks["javadocJar"] )
|
|
||||||
|
|
||||||
pom {
|
|
||||||
name.set( "FlatLaf" )
|
|
||||||
description.set( "Flat Look and Feel" )
|
|
||||||
url.set( "https://github.com/JFormDesigner/FlatLaf" )
|
|
||||||
|
|
||||||
licenses {
|
|
||||||
license {
|
|
||||||
name.set( "The Apache License, Version 2.0" )
|
|
||||||
url.set( "http://www.apache.org/licenses/LICENSE-2.0.txt" )
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
developers {
|
|
||||||
developer {
|
|
||||||
name.set( "Karl Tauber" )
|
|
||||||
organization.set( "FormDev Software GmbH" )
|
|
||||||
organizationUrl.set( "https://www.formdev.com/" )
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
scm {
|
|
||||||
url.set( "https://github.com/JFormDesigner/FlatLaf" )
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bintray {
|
|
||||||
user = System.getenv( "BINTRAY_USER" ) ?: System.getProperty( "bintray.user" )
|
|
||||||
key = System.getenv( "BINTRAY_KEY" ) ?: System.getProperty( "bintray.key" )
|
|
||||||
|
|
||||||
setPublications( "maven" )
|
|
||||||
|
|
||||||
with( pkg ) {
|
|
||||||
repo = "flatlaf"
|
|
||||||
name = "flatlaf"
|
|
||||||
setLicenses( "Apache-2.0" )
|
|
||||||
vcsUrl = "https://github.com/JFormDesigner/FlatLaf"
|
|
||||||
|
|
||||||
with( version ) {
|
|
||||||
name = project.version.toString()
|
|
||||||
}
|
|
||||||
|
|
||||||
publish = true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
package com.formdev.flatlaf;
|
package com.formdev.flatlaf;
|
||||||
|
|
||||||
|
import java.awt.Color;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import javax.swing.JComponent;
|
import javax.swing.JComponent;
|
||||||
|
|
||||||
@@ -27,25 +28,64 @@ public interface FlatClientProperties
|
|||||||
/**
|
/**
|
||||||
* Specifies type of a button.
|
* Specifies type of a button.
|
||||||
* <p>
|
* <p>
|
||||||
* <strong>Component</strong> {@link javax.swing.JButton}<br>
|
* <strong>Components</strong> {@link javax.swing.JButton} and {@link javax.swing.JToggleButton}<br>
|
||||||
* <strong>Value type</strong> {@link java.lang.String}<br>
|
* <strong>Value type</strong> {@link java.lang.String}<br>
|
||||||
* <strong>Allowed Values</strong> {@link BUTTON_TYPE_HELP}
|
* <strong>Allowed Values</strong> {@link #BUTTON_TYPE_SQUARE}, {@link #BUTTON_TYPE_ROUND_RECT},
|
||||||
|
* {@link #BUTTON_TYPE_TAB}, {@link #BUTTON_TYPE_HELP} and {@link BUTTON_TYPE_TOOLBAR_BUTTON}
|
||||||
*/
|
*/
|
||||||
String BUTTON_TYPE = "JButton.buttonType";
|
String BUTTON_TYPE = "JButton.buttonType";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Paint the button with square edges.
|
||||||
|
* <p>
|
||||||
|
* <strong>Components</strong> {@link javax.swing.JButton} and {@link javax.swing.JToggleButton}
|
||||||
|
*
|
||||||
|
* @see #BUTTON_TYPE
|
||||||
|
*/
|
||||||
|
String BUTTON_TYPE_SQUARE = "square";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Paint the button with round edges.
|
||||||
|
* <p>
|
||||||
|
* <strong>Components</strong> {@link javax.swing.JButton} and {@link javax.swing.JToggleButton}
|
||||||
|
*
|
||||||
|
* @see #BUTTON_TYPE
|
||||||
|
*/
|
||||||
|
String BUTTON_TYPE_ROUND_RECT = "roundRect";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Paint the toggle button in tab style.
|
||||||
|
* <p>
|
||||||
|
* <strong>Components</strong> {@link javax.swing.JToggleButton}
|
||||||
|
*
|
||||||
|
* @see #BUTTON_TYPE
|
||||||
|
*/
|
||||||
|
String BUTTON_TYPE_TAB = "tab";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Paint a help button (circle with question mark).
|
* Paint a help button (circle with question mark).
|
||||||
|
* <p>
|
||||||
|
* <strong>Components</strong> {@link javax.swing.JButton}
|
||||||
*
|
*
|
||||||
* @see #BUTTON_TYPE
|
* @see #BUTTON_TYPE
|
||||||
*/
|
*/
|
||||||
String BUTTON_TYPE_HELP = "help";
|
String BUTTON_TYPE_HELP = "help";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Paint the button in toolbar style.
|
||||||
|
* <p>
|
||||||
|
* <strong>Components</strong> {@link javax.swing.JButton} and {@link javax.swing.JToggleButton}
|
||||||
|
*
|
||||||
|
* @see #BUTTON_TYPE
|
||||||
|
*/
|
||||||
|
String BUTTON_TYPE_TOOLBAR_BUTTON = "toolBarButton";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Specifies selected state of a checkbox.
|
* Specifies selected state of a checkbox.
|
||||||
* <p>
|
* <p>
|
||||||
* <strong>Component</strong> {@link javax.swing.JCheckBox}<br>
|
* <strong>Component</strong> {@link javax.swing.JCheckBox}<br>
|
||||||
* <strong>Value type</strong> {@link java.lang.String}<br>
|
* <strong>Value type</strong> {@link java.lang.String}<br>
|
||||||
* <strong>Allowed Values</strong> {@link SELECTED_STATE_INDETERMINATE}
|
* <strong>Allowed Values</strong> {@link #SELECTED_STATE_INDETERMINATE}
|
||||||
*/
|
*/
|
||||||
String SELECTED_STATE = "JButton.selectedState";
|
String SELECTED_STATE = "JButton.selectedState";
|
||||||
|
|
||||||
@@ -56,6 +96,85 @@ public interface FlatClientProperties
|
|||||||
*/
|
*/
|
||||||
String SELECTED_STATE_INDETERMINATE = "indeterminate";
|
String SELECTED_STATE_INDETERMINATE = "indeterminate";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specifies minimum width of a component.
|
||||||
|
* <p>
|
||||||
|
* <strong>Component</strong> {@link javax.swing.JButton}, {@link javax.swing.JToggleButton},
|
||||||
|
* {@link javax.swing.JComboBox}, {@link javax.swing.JSpinner} and {@link javax.swing.text.JTextComponent}<br>
|
||||||
|
* <strong>Value type</strong> {@link java.lang.Integer}<br>
|
||||||
|
*/
|
||||||
|
String MINIMUM_WIDTH = "JComponent.minimumWidth";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specifies minimum height of a component.
|
||||||
|
* <p>
|
||||||
|
* <strong>Component</strong> {@link javax.swing.JButton} and {@link javax.swing.JToggleButton}<br>
|
||||||
|
* <strong>Value type</strong> {@link java.lang.Integer}<br>
|
||||||
|
*/
|
||||||
|
String MINIMUM_HEIGHT = "JComponent.minimumHeight";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specifies the outline color of the component border.
|
||||||
|
* <p>
|
||||||
|
* <strong>Components</strong> {@link javax.swing.JButton}, {@link javax.swing.JComboBox},
|
||||||
|
* {@link javax.swing.JFormattedTextField}, {@link javax.swing.JPasswordField},
|
||||||
|
* {@link javax.swing.JScrollPane}, {@link javax.swing.JSpinner},
|
||||||
|
* {@link javax.swing.JTextField} and {@link javax.swing.JToggleButton}<br>
|
||||||
|
* <strong>Value type</strong> {@link java.lang.String} or {@link java.awt.Color} or {@link java.awt.Color}[2]<br>
|
||||||
|
* <strong>Allowed Values</strong> {@link #OUTLINE_ERROR}, {@link #OUTLINE_WARNING},
|
||||||
|
* any color (type {@link java.awt.Color}) or an array of two colors (type {@link java.awt.Color}[2])
|
||||||
|
* where the first color is for focused state and the second for unfocused state
|
||||||
|
*/
|
||||||
|
String OUTLINE = "JComponent.outline";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Paint the component border in another color (usually reddish) to indicate an error.
|
||||||
|
*
|
||||||
|
* @see #OUTLINE
|
||||||
|
*/
|
||||||
|
String OUTLINE_ERROR = "error";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Paint the component border in another color (usually yellowish) to indicate a warning.
|
||||||
|
*
|
||||||
|
* @see #OUTLINE
|
||||||
|
*/
|
||||||
|
String OUTLINE_WARNING = "warning";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Paint the component with round edges.
|
||||||
|
* <p>
|
||||||
|
* <strong>Components</strong> {@link javax.swing.JComboBox}, {@link javax.swing.JSpinner},
|
||||||
|
* {@link javax.swing.JTextField}, {@link javax.swing.JFormattedTextField} and {@link javax.swing.JPasswordField}
|
||||||
|
* <strong>Value type</strong> {@link java.lang.Boolean}
|
||||||
|
*/
|
||||||
|
String COMPONENT_ROUND_RECT = "JComponent.roundRect";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specifies whether a drop shadow is painted if the component is shown in a popup
|
||||||
|
* or if the component is the owner of another component that is shown in a popup.
|
||||||
|
* <p>
|
||||||
|
* <strong>Component</strong> {@link javax.swing.JComponent}<br>
|
||||||
|
* <strong>Value type</strong> {@link java.lang.Boolean}
|
||||||
|
*/
|
||||||
|
String POPUP_DROP_SHADOW_PAINTED = "Popup.dropShadowPainted";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specifies whether the progress bar has always the larger height even if no string is painted.
|
||||||
|
* <p>
|
||||||
|
* <strong>Component</strong> {@link javax.swing.JProgressBar}<br>
|
||||||
|
* <strong>Value type</strong> {@link java.lang.Boolean}
|
||||||
|
*/
|
||||||
|
String PROGRESS_BAR_LARGE_HEIGHT = "JProgressBar.largeHeight";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specifies whether the progress bar is paint with square edges.
|
||||||
|
* <p>
|
||||||
|
* <strong>Component</strong> {@link javax.swing.JProgressBar}<br>
|
||||||
|
* <strong>Value type</strong> {@link java.lang.Boolean}
|
||||||
|
*/
|
||||||
|
String PROGRESS_BAR_SQUARE = "JProgressBar.square";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Specifies whether the decrease/increase arrow buttons of a scrollbar are shown.
|
* Specifies whether the decrease/increase arrow buttons of a scrollbar are shown.
|
||||||
* <p>
|
* <p>
|
||||||
@@ -80,14 +199,80 @@ public interface FlatClientProperties
|
|||||||
*/
|
*/
|
||||||
String TABBED_PANE_HAS_FULL_BORDER = "JTabbedPane.hasFullBorder";
|
String TABBED_PANE_HAS_FULL_BORDER = "JTabbedPane.hasFullBorder";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specifies the height of a tab.
|
||||||
|
* <p>
|
||||||
|
* <strong>Component</strong> {@link javax.swing.JTabbedPane}<br>
|
||||||
|
* <strong>Value type</strong> {@link java.lang.Integer}
|
||||||
|
*/
|
||||||
|
String TABBED_PANE_TAB_HEIGHT = "JTabbedPane.tabHeight";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specifies whether all text is selected when the text component gains focus.
|
||||||
|
* <p>
|
||||||
|
* <strong>Component</strong> {@link javax.swing.JTextField} (and subclasses)<br>
|
||||||
|
* <strong>Value type</strong> {@link java.lang.String}<br>
|
||||||
|
* <strong>Allowed Values</strong> {@link #SELECT_ALL_ON_FOCUS_POLICY_NEVER},
|
||||||
|
* {@link #SELECT_ALL_ON_FOCUS_POLICY_ONCE} (default) or
|
||||||
|
* {@link #SELECT_ALL_ON_FOCUS_POLICY_ALWAYS}
|
||||||
|
*/
|
||||||
|
String SELECT_ALL_ON_FOCUS_POLICY = "JTextField.selectAllOnFocusPolicy";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Never select all text when the text component gains focus.
|
||||||
|
*
|
||||||
|
* @see #SELECT_ALL_ON_FOCUS_POLICY
|
||||||
|
*/
|
||||||
|
String SELECT_ALL_ON_FOCUS_POLICY_NEVER = "never";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Select all text when the text component gains focus for the first time
|
||||||
|
* and selection was not modified (is at end of text).
|
||||||
|
* This is the default.
|
||||||
|
*
|
||||||
|
* @see #SELECT_ALL_ON_FOCUS_POLICY
|
||||||
|
*/
|
||||||
|
String SELECT_ALL_ON_FOCUS_POLICY_ONCE = "once";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Always select all text when the text component gains focus.
|
||||||
|
*
|
||||||
|
* @see #SELECT_ALL_ON_FOCUS_POLICY
|
||||||
|
*/
|
||||||
|
String SELECT_ALL_ON_FOCUS_POLICY_ALWAYS = "always";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Placeholder text that is only painted if the text field is empty.
|
* Placeholder text that is only painted if the text field is empty.
|
||||||
* <p>
|
* <p>
|
||||||
* <strong>Component</strong> {@link javax.swing.JTextField} or {@link javax.swing.JComboBox}<br>
|
* <strong>Component</strong> {@link javax.swing.JTextField} (and subclasses) or {@link javax.swing.JComboBox}<br>
|
||||||
* <strong>Value type</strong> {@link java.lang.String}
|
* <strong>Value type</strong> {@link java.lang.String}
|
||||||
*/
|
*/
|
||||||
String PLACEHOLDER_TEXT = "JTextField.placeholderText";
|
String PLACEHOLDER_TEXT = "JTextField.placeholderText";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Height of underline if toggle button type is {@link #BUTTON_TYPE_TAB}.
|
||||||
|
* <p>
|
||||||
|
* <strong>Component</strong> {@link javax.swing.JToggleButton}<br>
|
||||||
|
* <strong>Value type</strong> {@link java.lang.Integer}
|
||||||
|
*/
|
||||||
|
String TAB_BUTTON_UNDERLINE_HEIGHT = "JToggleButton.tab.underlineHeight";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Color of underline if toggle button type is {@link #BUTTON_TYPE_TAB}.
|
||||||
|
* <p>
|
||||||
|
* <strong>Component</strong> {@link javax.swing.JToggleButton}<br>
|
||||||
|
* <strong>Value type</strong> {@link java.awt.Color}
|
||||||
|
*/
|
||||||
|
String TAB_BUTTON_UNDERLINE_COLOR = "JToggleButton.tab.underlineColor";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Background color if selected and toggle button type is {@link #BUTTON_TYPE_TAB}.
|
||||||
|
* <p>
|
||||||
|
* <strong>Component</strong> {@link javax.swing.JToggleButton}<br>
|
||||||
|
* <strong>Value type</strong> {@link java.awt.Color}
|
||||||
|
*/
|
||||||
|
String TAB_BUTTON_SELECTED_BACKGROUND = "JToggleButton.tab.selectedBackground";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks whether a client property of a component has the given value.
|
* Checks whether a client property of a component has the given value.
|
||||||
*/
|
*/
|
||||||
@@ -103,4 +288,32 @@ public interface FlatClientProperties
|
|||||||
Object value = c.getClientProperty( key );
|
Object value = c.getClientProperty( key );
|
||||||
return (value instanceof Boolean) ? (boolean) value : defaultValue;
|
return (value instanceof Boolean) ? (boolean) value : defaultValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether a client property of a component is an integer and returns its value.
|
||||||
|
* If the client property is not set, or not an integer, defaultValue is returned.
|
||||||
|
*/
|
||||||
|
static int clientPropertyInt( JComponent c, String key, int defaultValue ) {
|
||||||
|
Object value = c.getClientProperty( key );
|
||||||
|
return (value instanceof Integer) ? (int) value : defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether a client property of a component is a color and returns its value.
|
||||||
|
* If the client property is not set, or not a color, defaultValue is returned.
|
||||||
|
*/
|
||||||
|
static Color clientPropertyColor( JComponent c, String key, Color defaultValue ) {
|
||||||
|
Object value = c.getClientProperty( key );
|
||||||
|
return (value instanceof Color) ? (Color) value : defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int clientPropertyChoice( JComponent c, String key, String... choices ) {
|
||||||
|
Object value = c.getClientProperty( key );
|
||||||
|
for( int i = 0; i < choices.length; i++ ) {
|
||||||
|
if( choices[i].equals( value ) )
|
||||||
|
return i;
|
||||||
|
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,11 +32,11 @@ public class FlatDarculaLaf
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getName() {
|
public String getName() {
|
||||||
return "Flat Darcula";
|
return "FlatLaf Darcula";
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getDescription() {
|
public String getDescription() {
|
||||||
return "Flat Darcula Look and Feel";
|
return "FlatLaf Darcula Look and Feel";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,12 +32,12 @@ public class FlatDarkLaf
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getName() {
|
public String getName() {
|
||||||
return "Flat Dark";
|
return "FlatLaf Dark";
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getDescription() {
|
public String getDescription() {
|
||||||
return "Flat Dark Look and Feel";
|
return "FlatLaf Dark Look and Feel";
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -17,17 +17,19 @@
|
|||||||
package com.formdev.flatlaf;
|
package com.formdev.flatlaf;
|
||||||
|
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
import javax.swing.LookAndFeel;
|
||||||
|
import javax.swing.UIDefaults;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Addon for FlatLaf UI defaults.
|
* Addon for FlatLaf UI defaults.
|
||||||
*
|
*
|
||||||
* Allows loading of additional .properties files from addon JARs.
|
* Allows loading of additional .properties files from addon JARs.
|
||||||
* {@link java.util.ServiceLoader} is used to load extensions of this class from addon JARs.
|
* {@link java.util.ServiceLoader} is used to load extensions of this class from addon JARs.
|
||||||
*
|
* <p>
|
||||||
* If you extend this class in a addon JAR, you also have to add a text file named
|
* If you extend this class in a addon JAR, you also have to add a text file named
|
||||||
* {@code META-INF/services/com.formdev.flatlaf.FlatDefaultsAddon}
|
* {@code META-INF/services/com.formdev.flatlaf.FlatDefaultsAddon}
|
||||||
* to the addon JAR. The file must contain a single line with the class name.
|
* to the addon JAR. The file must contain a single line with the class name.
|
||||||
*
|
* <p>
|
||||||
* See 'flatlaf-swingx' addon for an example
|
* See 'flatlaf-swingx' addon for an example
|
||||||
*
|
*
|
||||||
* @author Karl Tauber
|
* @author Karl Tauber
|
||||||
@@ -37,6 +39,33 @@ public abstract class FlatDefaultsAddon
|
|||||||
/**
|
/**
|
||||||
* Finds an addon .properties file for the given LaF class and returns
|
* Finds an addon .properties file for the given LaF class and returns
|
||||||
* it as input stream. Or {@code null} if not found.
|
* it as input stream. Or {@code null} if not found.
|
||||||
|
* <p>
|
||||||
|
* This default implementation finds addon .properties file for the given LaF class
|
||||||
|
* in the same package as the subclass.
|
||||||
|
* <p>
|
||||||
|
* Override this method to load addon .properties files from other locations.
|
||||||
*/
|
*/
|
||||||
public abstract InputStream getDefaults( Class<?> lafClass );
|
public InputStream getDefaults( Class<?> lafClass ) {
|
||||||
|
Class<?> addonClass = this.getClass();
|
||||||
|
String propertiesName = '/' + addonClass.getPackage().getName().replace( '.', '/' )
|
||||||
|
+ '/' + lafClass.getSimpleName() + ".properties";
|
||||||
|
return addonClass.getResourceAsStream( propertiesName );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allows modifying UI defaults after loading UI defaults.
|
||||||
|
* The default implementation does nothing.
|
||||||
|
*/
|
||||||
|
public void afterDefaultsLoading( LookAndFeel laf, UIDefaults defaults ) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the priority used to sort addon loading.
|
||||||
|
* The order is only important if you want overwrite UI defaults of other addons.
|
||||||
|
* Lower numbers mean higher priority.
|
||||||
|
* Returns 10000 by default.
|
||||||
|
*/
|
||||||
|
public int getPriority() {
|
||||||
|
return 10000;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,89 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2020 FormDev Software GmbH
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.formdev.flatlaf;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default color palette for action icons and object icons.
|
||||||
|
* <p>
|
||||||
|
* The idea is to use only this well defined set of colors in SVG icons and
|
||||||
|
* then they are replaced at runtime to dark variants or to other theme colors.
|
||||||
|
* Then a single SVG icon (light variant) can be used for dark themes too.
|
||||||
|
* IntelliJ Platform uses this mechanism to allow themes to change IntelliJ Platform icons.
|
||||||
|
* <p>
|
||||||
|
* Use the {@code *_DARK} colors only in {@code *_dark.svg} files.
|
||||||
|
* <p>
|
||||||
|
* The colors are based on IntelliJ Platform
|
||||||
|
* <a href="https://jetbrains.design/intellij/principles/icons/#action-icons">Action icons</a>
|
||||||
|
* and
|
||||||
|
* <a href="https://jetbrains.design/intellij/principles/icons/#noun-icons">Noun icons</a>
|
||||||
|
* <p>
|
||||||
|
* These colors may be changed by IntelliJ Platform themes.
|
||||||
|
* <p>
|
||||||
|
* You may use these colors also in your application (outside of SVG icons), but do
|
||||||
|
* not use the RGB values defined in this enum.<br>
|
||||||
|
* Instead use {@code UIManager.getColor( FlatIconColors.ACTIONS_GREY.key )}.
|
||||||
|
*
|
||||||
|
* @author Karl Tauber
|
||||||
|
*/
|
||||||
|
public enum FlatIconColors
|
||||||
|
{
|
||||||
|
// colors for action icons
|
||||||
|
// see https://jetbrains.design/intellij/principles/icons/#action-icons
|
||||||
|
ACTIONS_RED ( 0xDB5860, "Actions.Red", true, false ),
|
||||||
|
ACTIONS_RED_DARK ( 0xC75450, "Actions.Red", false, true ),
|
||||||
|
ACTIONS_YELLOW ( 0xEDA200, "Actions.Yellow", true, false ),
|
||||||
|
ACTIONS_YELLOW_DARK ( 0xF0A732, "Actions.Yellow", false, true ),
|
||||||
|
ACTIONS_GREEN ( 0x59A869, "Actions.Green", true, false ),
|
||||||
|
ACTIONS_GREEN_DARK ( 0x499C54, "Actions.Green", false, true ),
|
||||||
|
ACTIONS_BLUE ( 0x389FD6, "Actions.Blue", true, false ),
|
||||||
|
ACTIONS_BLUE_DARK ( 0x3592C4, "Actions.Blue", false, true ),
|
||||||
|
ACTIONS_GREY ( 0x6E6E6E, "Actions.Grey", true, false ),
|
||||||
|
ACTIONS_GREY_DARK ( 0xAFB1B3, "Actions.Grey", false, true ),
|
||||||
|
ACTIONS_GREYINLINE ( 0x7F8B91, "Actions.GreyInline", true, false ),
|
||||||
|
ACTIONS_GREYINLINE_DARK ( 0x7F8B91, "Actions.GreyInline", false, true ),
|
||||||
|
|
||||||
|
// colors for object icons
|
||||||
|
// see https://jetbrains.design/intellij/principles/icons/#noun-icons
|
||||||
|
OBJECTS_GREY ( 0x9AA7B0, "Objects.Grey" ),
|
||||||
|
OBJECTS_BLUE ( 0x40B6E0, "Objects.Blue" ),
|
||||||
|
OBJECTS_GREEN ( 0x62B543, "Objects.Green" ),
|
||||||
|
OBJECTS_YELLOW ( 0xF4AF3D, "Objects.Yellow" ),
|
||||||
|
OBJECTS_YELLOW_DARK ( 0xD9A343, "Objects.YellowDark" ),
|
||||||
|
OBJECTS_PURPLE ( 0xB99BF8, "Objects.Purple" ),
|
||||||
|
OBJECTS_PINK ( 0xF98B9E, "Objects.Pink" ),
|
||||||
|
OBJECTS_RED ( 0xF26522, "Objects.Red" ),
|
||||||
|
OBJECTS_RED_STATUS ( 0xE05555, "Objects.RedStatus" ),
|
||||||
|
OBJECTS_GREEN_ANDROID ( 0xA4C639, "Objects.GreenAndroid" ),
|
||||||
|
OBJECTS_BLACK_TEXT ( 0x231F20, "Objects.BlackText" );
|
||||||
|
|
||||||
|
public final int rgb;
|
||||||
|
public final String key;
|
||||||
|
|
||||||
|
public final boolean light;
|
||||||
|
public final boolean dark;
|
||||||
|
|
||||||
|
FlatIconColors( int rgb, String key ) {
|
||||||
|
this( rgb, key, true, true );
|
||||||
|
}
|
||||||
|
|
||||||
|
FlatIconColors( int rgb, String key, boolean light, boolean dark ) {
|
||||||
|
this.rgb = rgb;
|
||||||
|
this.key = key;
|
||||||
|
this.light = light;
|
||||||
|
this.dark = dark;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,644 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2020 FormDev Software GmbH
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.formdev.flatlaf;
|
||||||
|
|
||||||
|
import javax.swing.InputMap;
|
||||||
|
import javax.swing.JTextField;
|
||||||
|
import javax.swing.KeyStroke;
|
||||||
|
import javax.swing.LookAndFeel;
|
||||||
|
import javax.swing.UIDefaults;
|
||||||
|
import javax.swing.UIDefaults.LazyValue;
|
||||||
|
import javax.swing.plaf.InputMapUIResource;
|
||||||
|
import com.formdev.flatlaf.util.SystemInfo;
|
||||||
|
import static javax.swing.text.DefaultEditorKit.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Karl Tauber
|
||||||
|
*/
|
||||||
|
class FlatInputMaps
|
||||||
|
{
|
||||||
|
static void initInputMaps( UIDefaults defaults ) {
|
||||||
|
initBasicInputMaps( defaults );
|
||||||
|
initTextComponentInputMaps( defaults );
|
||||||
|
|
||||||
|
if( SystemInfo.IS_MAC )
|
||||||
|
initMacInputMaps( defaults );
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void initBasicInputMaps( UIDefaults defaults ) {
|
||||||
|
defaults.put( "Button.focusInputMap", new UIDefaults.LazyInputMap( new Object[] {
|
||||||
|
"SPACE", "pressed",
|
||||||
|
"released SPACE", "released"
|
||||||
|
} ) );
|
||||||
|
|
||||||
|
modifyInputMap( defaults, "ComboBox.ancestorInputMap",
|
||||||
|
"SPACE", "spacePopup",
|
||||||
|
|
||||||
|
"UP", mac( "selectPrevious2", "selectPrevious" ),
|
||||||
|
"DOWN", mac( "selectNext2", "selectNext" ),
|
||||||
|
"KP_UP", mac( "selectPrevious2", "selectPrevious" ),
|
||||||
|
"KP_DOWN", mac( "selectNext2", "selectNext" ),
|
||||||
|
|
||||||
|
mac( "alt UP", null ), "togglePopup",
|
||||||
|
mac( "alt DOWN", null ), "togglePopup",
|
||||||
|
mac( "alt KP_UP", null ), "togglePopup",
|
||||||
|
mac( "alt KP_DOWN", null ), "togglePopup"
|
||||||
|
);
|
||||||
|
|
||||||
|
if( !SystemInfo.IS_MAC ) {
|
||||||
|
modifyInputMap( defaults, "FileChooser.ancestorInputMap",
|
||||||
|
"F2", "editFileName",
|
||||||
|
"BACK_SPACE", "Go Up"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// join ltr and rtl bindings to fix up/down/etc keys in right-to-left component orientation
|
||||||
|
Object[] bindings = (Object[]) defaults.get( "PopupMenu.selectedWindowInputMapBindings" );
|
||||||
|
Object[] rtlBindings = (Object[]) defaults.get( "PopupMenu.selectedWindowInputMapBindings.RightToLeft" );
|
||||||
|
if( bindings != null && rtlBindings != null ) {
|
||||||
|
Object[] newBindings = new Object[bindings.length + rtlBindings.length];
|
||||||
|
System.arraycopy( bindings, 0, newBindings, 0, bindings.length );
|
||||||
|
System.arraycopy( rtlBindings, 0, newBindings, bindings.length, rtlBindings.length );
|
||||||
|
defaults.put( "PopupMenu.selectedWindowInputMapBindings.RightToLeft", newBindings );
|
||||||
|
}
|
||||||
|
|
||||||
|
modifyInputMap( defaults, "TabbedPane.ancestorInputMap",
|
||||||
|
"ctrl TAB", "navigateNext",
|
||||||
|
"shift ctrl TAB", "navigatePrevious"
|
||||||
|
);
|
||||||
|
|
||||||
|
modifyInputMap( defaults, "Table.ancestorInputMap",
|
||||||
|
// swap to make it consistent with List and Tree
|
||||||
|
"HOME", "selectFirstRow",
|
||||||
|
"END", "selectLastRow",
|
||||||
|
"shift HOME", "selectFirstRowExtendSelection",
|
||||||
|
"shift END", "selectLastRowExtendSelection",
|
||||||
|
mac( "ctrl HOME", null ), "selectFirstColumn",
|
||||||
|
mac( "ctrl END", null ), "selectLastColumn",
|
||||||
|
mac( "shift ctrl HOME", null ), "selectFirstColumnExtendSelection",
|
||||||
|
mac( "shift ctrl END", null ), "selectLastColumnExtendSelection"
|
||||||
|
);
|
||||||
|
|
||||||
|
if( !SystemInfo.IS_MAC ) {
|
||||||
|
modifyInputMap( defaults, "Tree.focusInputMap",
|
||||||
|
"ADD", "expand",
|
||||||
|
"SUBTRACT", "collapse"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void initTextComponentInputMaps( UIDefaults defaults ) {
|
||||||
|
Object[] commonTextComponentBindings = {
|
||||||
|
// move caret one character (without selecting text)
|
||||||
|
"LEFT", backwardAction,
|
||||||
|
"RIGHT", forwardAction,
|
||||||
|
"KP_LEFT", backwardAction,
|
||||||
|
"KP_RIGHT", forwardAction,
|
||||||
|
|
||||||
|
// move caret one character and select text
|
||||||
|
"shift LEFT", selectionBackwardAction,
|
||||||
|
"shift RIGHT", selectionForwardAction,
|
||||||
|
"shift KP_LEFT", selectionBackwardAction,
|
||||||
|
"shift KP_RIGHT", selectionForwardAction,
|
||||||
|
|
||||||
|
// move caret to word (without selecting text)
|
||||||
|
mac( "ctrl LEFT", "alt LEFT" ), previousWordAction,
|
||||||
|
mac( "ctrl RIGHT", "alt RIGHT" ), nextWordAction,
|
||||||
|
mac( "ctrl KP_LEFT", "alt KP_LEFT" ), previousWordAction,
|
||||||
|
mac( "ctrl KP_RIGHT", "alt KP_RIGHT" ), nextWordAction,
|
||||||
|
|
||||||
|
// move caret to word and select text
|
||||||
|
mac( "ctrl shift LEFT", "shift alt LEFT" ), selectionPreviousWordAction,
|
||||||
|
mac( "ctrl shift RIGHT", "shift alt RIGHT" ), selectionNextWordAction,
|
||||||
|
mac( "ctrl shift KP_LEFT", "shift alt KP_LEFT" ), selectionPreviousWordAction,
|
||||||
|
mac( "ctrl shift KP_RIGHT", "shift alt KP_RIGHT" ), selectionNextWordAction,
|
||||||
|
|
||||||
|
// move caret to line begin/end (without selecting text)
|
||||||
|
mac( "HOME", "meta LEFT" ), beginLineAction,
|
||||||
|
mac( "END", "meta RIGHT" ), endLineAction,
|
||||||
|
|
||||||
|
// move caret to line begin/end and select text
|
||||||
|
mac( "shift HOME", "shift meta LEFT" ), selectionBeginLineAction,
|
||||||
|
mac( "shift END", "shift meta RIGHT" ), selectionEndLineAction,
|
||||||
|
|
||||||
|
// select all/none
|
||||||
|
mac( "ctrl A", "meta A" ), selectAllAction,
|
||||||
|
mac( "ctrl BACK_SLASH", "meta BACK_SLASH" ), "unselect", // DefaultEditorKit.unselectAction
|
||||||
|
|
||||||
|
// delete previous/next character
|
||||||
|
"BACK_SPACE", deletePrevCharAction,
|
||||||
|
"shift BACK_SPACE", deletePrevCharAction,
|
||||||
|
"ctrl H", deletePrevCharAction,
|
||||||
|
"DELETE", deleteNextCharAction,
|
||||||
|
|
||||||
|
// delete previous/next word
|
||||||
|
mac( "ctrl BACK_SPACE", "alt BACK_SPACE" ), deletePrevWordAction,
|
||||||
|
mac( "ctrl DELETE", "alt DELETE" ), deleteNextWordAction,
|
||||||
|
|
||||||
|
// clipboard
|
||||||
|
mac( "ctrl X", "meta X" ), cutAction,
|
||||||
|
mac( "ctrl C", "meta C" ), copyAction,
|
||||||
|
mac( "ctrl V", "meta V" ), pasteAction,
|
||||||
|
"CUT", cutAction,
|
||||||
|
"COPY", copyAction,
|
||||||
|
"PASTE", pasteAction,
|
||||||
|
mac( "shift DELETE", null ), cutAction,
|
||||||
|
mac( "control INSERT", null ), copyAction,
|
||||||
|
mac( "shift INSERT", null ), pasteAction,
|
||||||
|
|
||||||
|
// misc
|
||||||
|
"control shift O", "toggle-componentOrientation", // DefaultEditorKit.toggleComponentOrientation
|
||||||
|
};
|
||||||
|
|
||||||
|
Object[] macCommonTextComponentBindings = SystemInfo.IS_MAC ? new Object[] {
|
||||||
|
// move caret one character (without selecting text)
|
||||||
|
"ctrl B", backwardAction,
|
||||||
|
"ctrl F", forwardAction,
|
||||||
|
|
||||||
|
// move caret to document begin/end (without selecting text)
|
||||||
|
"HOME", beginAction,
|
||||||
|
"END", endAction,
|
||||||
|
"meta UP", beginAction,
|
||||||
|
"meta DOWN", endAction,
|
||||||
|
"meta KP_UP", beginAction,
|
||||||
|
"meta KP_DOWN", endAction,
|
||||||
|
"ctrl P", beginAction,
|
||||||
|
"ctrl N", endAction,
|
||||||
|
"ctrl V", endAction,
|
||||||
|
|
||||||
|
// move caret to line begin/end (without selecting text)
|
||||||
|
"meta KP_LEFT", beginLineAction,
|
||||||
|
"meta KP_RIGHT", endLineAction,
|
||||||
|
"ctrl A", beginLineAction,
|
||||||
|
"ctrl E", endLineAction,
|
||||||
|
|
||||||
|
// move caret to document begin/end and select text
|
||||||
|
"shift meta UP", selectionBeginAction,
|
||||||
|
"shift meta DOWN", selectionEndAction,
|
||||||
|
"shift meta KP_UP", selectionBeginAction,
|
||||||
|
"shift meta KP_DOWN", selectionEndAction,
|
||||||
|
"shift HOME", selectionBeginAction,
|
||||||
|
"shift END", selectionEndAction,
|
||||||
|
|
||||||
|
// move caret to line begin/end and select text
|
||||||
|
"shift meta KP_LEFT", selectionBeginLineAction,
|
||||||
|
"shift meta KP_RIGHT", selectionEndLineAction,
|
||||||
|
"shift UP", selectionBeginLineAction,
|
||||||
|
"shift DOWN", selectionEndLineAction,
|
||||||
|
"shift KP_UP", selectionBeginLineAction,
|
||||||
|
"shift KP_DOWN", selectionEndLineAction,
|
||||||
|
|
||||||
|
// delete previous/next word
|
||||||
|
"ctrl W", deletePrevWordAction,
|
||||||
|
"ctrl D", deleteNextCharAction,
|
||||||
|
} : null;
|
||||||
|
|
||||||
|
Object[] singleLineTextComponentBindings = {
|
||||||
|
"ENTER", JTextField.notifyAction,
|
||||||
|
};
|
||||||
|
|
||||||
|
Object[] macSingleLineTextComponentBindings = SystemInfo.IS_MAC ? new Object[] {
|
||||||
|
// move caret to line begin/end (without selecting text)
|
||||||
|
"UP", beginLineAction,
|
||||||
|
"DOWN", endLineAction,
|
||||||
|
"KP_UP", beginLineAction,
|
||||||
|
"KP_DOWN", endLineAction,
|
||||||
|
} : null;
|
||||||
|
|
||||||
|
Object[] formattedTextComponentBindings = {
|
||||||
|
// reset
|
||||||
|
"ESCAPE", "reset-field-edit",
|
||||||
|
|
||||||
|
// increment/decrement
|
||||||
|
"UP", "increment",
|
||||||
|
"DOWN", "decrement",
|
||||||
|
"KP_UP", "increment",
|
||||||
|
"KP_DOWN", "decrement",
|
||||||
|
};
|
||||||
|
|
||||||
|
Object[] passwordTextComponentBindings = {
|
||||||
|
// move caret to line begin/end (without selecting text)
|
||||||
|
mac( "ctrl LEFT", "alt LEFT" ), beginLineAction,
|
||||||
|
mac( "ctrl RIGHT", "alt RIGHT" ), endLineAction,
|
||||||
|
mac( "ctrl KP_LEFT", "alt KP_LEFT" ), beginLineAction,
|
||||||
|
mac( "ctrl KP_RIGHT", "alt KP_RIGHT" ), endLineAction,
|
||||||
|
|
||||||
|
// move caret to line begin/end and select text
|
||||||
|
mac( "ctrl shift LEFT", "shift alt LEFT" ), selectionBeginLineAction,
|
||||||
|
mac( "ctrl shift RIGHT", "shift alt RIGHT" ), selectionEndLineAction,
|
||||||
|
mac( "ctrl shift KP_LEFT", "shift alt KP_LEFT" ), selectionBeginLineAction,
|
||||||
|
mac( "ctrl shift KP_RIGHT", "shift alt KP_RIGHT" ), selectionEndLineAction,
|
||||||
|
|
||||||
|
// delete previous/next word
|
||||||
|
mac( "ctrl BACK_SPACE", "alt BACK_SPACE" ), null,
|
||||||
|
mac( "ctrl DELETE", "alt DELETE" ), null,
|
||||||
|
};
|
||||||
|
|
||||||
|
Object[] multiLineTextComponentBindings = {
|
||||||
|
// move caret one line (without selecting text)
|
||||||
|
"UP", upAction,
|
||||||
|
"DOWN", downAction,
|
||||||
|
"KP_UP", upAction,
|
||||||
|
"KP_DOWN", downAction,
|
||||||
|
|
||||||
|
// move caret one line and select text
|
||||||
|
"shift UP", selectionUpAction,
|
||||||
|
"shift DOWN", selectionDownAction,
|
||||||
|
"shift KP_UP", selectionUpAction,
|
||||||
|
"shift KP_DOWN", selectionDownAction,
|
||||||
|
|
||||||
|
// move caret one page (without selecting text)
|
||||||
|
"PAGE_UP", pageUpAction,
|
||||||
|
"PAGE_DOWN", pageDownAction,
|
||||||
|
|
||||||
|
// move caret one page and select text
|
||||||
|
"shift PAGE_UP", "selection-page-up", // DefaultEditorKit.selectionPageUpAction
|
||||||
|
"shift PAGE_DOWN", "selection-page-down", // DefaultEditorKit.selectionPageDownAction
|
||||||
|
mac( "ctrl shift PAGE_UP", "shift meta PAGE_UP" ), "selection-page-left", // DefaultEditorKit.selectionPageLeftAction
|
||||||
|
mac( "ctrl shift PAGE_DOWN", "shift meta PAGE_DOWN" ), "selection-page-right", // DefaultEditorKit.selectionPageRightAction
|
||||||
|
|
||||||
|
// move caret to document begin/end (without selecting text)
|
||||||
|
mac( "ctrl HOME", "meta UP" ), beginAction,
|
||||||
|
mac( "ctrl END", "meta DOWN" ), endAction,
|
||||||
|
|
||||||
|
// move caret to document begin/end and select text
|
||||||
|
mac( "ctrl shift HOME", "shift meta UP" ), selectionBeginAction,
|
||||||
|
mac( "ctrl shift END", "shift meta DOWN" ), selectionEndAction,
|
||||||
|
|
||||||
|
// misc
|
||||||
|
"ENTER", insertBreakAction,
|
||||||
|
"TAB", insertTabAction,
|
||||||
|
|
||||||
|
// links
|
||||||
|
mac( "ctrl T", "meta T" ), "next-link-action",
|
||||||
|
mac( "ctrl shift T", "shift meta T" ), "previous-link-action",
|
||||||
|
mac( "ctrl SPACE", "meta SPACE" ), "activate-link-action",
|
||||||
|
};
|
||||||
|
|
||||||
|
Object[] macMultiLineTextComponentBindings = SystemInfo.IS_MAC ? new Object[] {
|
||||||
|
// move caret one line (without selecting text)
|
||||||
|
"ctrl N", downAction,
|
||||||
|
"ctrl P", upAction,
|
||||||
|
|
||||||
|
// move caret to beginning/end of paragraph and select text
|
||||||
|
"shift alt UP", selectionBeginParagraphAction,
|
||||||
|
"shift alt DOWN", selectionEndParagraphAction,
|
||||||
|
"shift alt KP_UP", selectionBeginParagraphAction,
|
||||||
|
"shift alt KP_DOWN", selectionEndParagraphAction,
|
||||||
|
|
||||||
|
// move caret one page (without selecting text)
|
||||||
|
"ctrl V", pageDownAction,
|
||||||
|
} : null;
|
||||||
|
|
||||||
|
defaults.put( "TextField.focusInputMap", new LazyInputMapEx(
|
||||||
|
commonTextComponentBindings,
|
||||||
|
macCommonTextComponentBindings,
|
||||||
|
singleLineTextComponentBindings,
|
||||||
|
macSingleLineTextComponentBindings
|
||||||
|
) );
|
||||||
|
defaults.put( "FormattedTextField.focusInputMap", new LazyInputMapEx(
|
||||||
|
commonTextComponentBindings,
|
||||||
|
macCommonTextComponentBindings,
|
||||||
|
singleLineTextComponentBindings,
|
||||||
|
macSingleLineTextComponentBindings,
|
||||||
|
formattedTextComponentBindings
|
||||||
|
) );
|
||||||
|
defaults.put( "PasswordField.focusInputMap", new LazyInputMapEx(
|
||||||
|
commonTextComponentBindings,
|
||||||
|
macCommonTextComponentBindings,
|
||||||
|
singleLineTextComponentBindings,
|
||||||
|
macSingleLineTextComponentBindings,
|
||||||
|
passwordTextComponentBindings
|
||||||
|
) );
|
||||||
|
|
||||||
|
Object multiLineInputMap = new LazyInputMapEx(
|
||||||
|
commonTextComponentBindings,
|
||||||
|
macCommonTextComponentBindings,
|
||||||
|
multiLineTextComponentBindings,
|
||||||
|
macMultiLineTextComponentBindings
|
||||||
|
);
|
||||||
|
defaults.put( "TextArea.focusInputMap", multiLineInputMap );
|
||||||
|
defaults.put( "TextPane.focusInputMap", multiLineInputMap );
|
||||||
|
defaults.put( "EditorPane.focusInputMap", multiLineInputMap );
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void initMacInputMaps( UIDefaults defaults ) {
|
||||||
|
// list
|
||||||
|
modifyInputMap( defaults, "List.focusInputMap",
|
||||||
|
"meta A", "selectAll",
|
||||||
|
"meta C", "copy",
|
||||||
|
"meta V", "paste",
|
||||||
|
"meta X", "cut",
|
||||||
|
|
||||||
|
// let parent scroll pane do the macOS typical scrolling without changing selection
|
||||||
|
"HOME", null,
|
||||||
|
"END", null,
|
||||||
|
"PAGE_UP", null,
|
||||||
|
"PAGE_DOWN", null,
|
||||||
|
|
||||||
|
"ctrl A", null,
|
||||||
|
"ctrl BACK_SLASH", null,
|
||||||
|
"ctrl C", null,
|
||||||
|
"ctrl DOWN", null,
|
||||||
|
"ctrl END", null,
|
||||||
|
"ctrl HOME", null,
|
||||||
|
"ctrl INSERT", null,
|
||||||
|
"ctrl KP_DOWN", null,
|
||||||
|
"ctrl KP_LEFT", null,
|
||||||
|
"ctrl KP_RIGHT", null,
|
||||||
|
"ctrl KP_UP", null,
|
||||||
|
"ctrl LEFT", null,
|
||||||
|
"ctrl PAGE_DOWN", null,
|
||||||
|
"ctrl PAGE_UP", null,
|
||||||
|
"ctrl RIGHT", null,
|
||||||
|
"ctrl SLASH", null,
|
||||||
|
"ctrl SPACE", null,
|
||||||
|
"ctrl UP", null,
|
||||||
|
"ctrl V", null,
|
||||||
|
"ctrl X", null,
|
||||||
|
"SPACE", null,
|
||||||
|
"shift ctrl DOWN", null,
|
||||||
|
"shift ctrl END", null,
|
||||||
|
"shift ctrl HOME", null,
|
||||||
|
"shift ctrl KP_DOWN", null,
|
||||||
|
"shift ctrl KP_LEFT", null,
|
||||||
|
"shift ctrl KP_RIGHT", null,
|
||||||
|
"shift ctrl KP_UP", null,
|
||||||
|
"shift ctrl LEFT", null,
|
||||||
|
"shift ctrl PAGE_DOWN", null,
|
||||||
|
"shift ctrl PAGE_UP", null,
|
||||||
|
"shift ctrl RIGHT", null,
|
||||||
|
"shift ctrl SPACE", null,
|
||||||
|
"shift ctrl UP", null,
|
||||||
|
"shift DELETE", null,
|
||||||
|
"shift INSERT", null,
|
||||||
|
"shift SPACE", null
|
||||||
|
);
|
||||||
|
modifyInputMap( defaults, "List.focusInputMap.RightToLeft",
|
||||||
|
"ctrl KP_LEFT", null,
|
||||||
|
"ctrl KP_RIGHT", null,
|
||||||
|
"ctrl LEFT", null,
|
||||||
|
"ctrl RIGHT", null,
|
||||||
|
"shift ctrl KP_LEFT", null,
|
||||||
|
"shift ctrl KP_RIGHT", null,
|
||||||
|
"shift ctrl LEFT", null,
|
||||||
|
"shift ctrl RIGHT", null
|
||||||
|
);
|
||||||
|
|
||||||
|
// scrollpane
|
||||||
|
modifyInputMap( defaults, "ScrollPane.ancestorInputMap",
|
||||||
|
"END", "scrollEnd",
|
||||||
|
"HOME", "scrollHome",
|
||||||
|
|
||||||
|
"ctrl END", null,
|
||||||
|
"ctrl HOME", null,
|
||||||
|
"ctrl PAGE_DOWN", null,
|
||||||
|
"ctrl PAGE_UP", null
|
||||||
|
);
|
||||||
|
modifyInputMap( defaults, "ScrollPane.ancestorInputMap.RightToLeft",
|
||||||
|
"ctrl PAGE_DOWN", null,
|
||||||
|
"ctrl PAGE_UP", null
|
||||||
|
);
|
||||||
|
|
||||||
|
// tabbedpane
|
||||||
|
modifyInputMap( defaults, "TabbedPane.ancestorInputMap",
|
||||||
|
"ctrl UP", null,
|
||||||
|
"ctrl KP_UP", null
|
||||||
|
);
|
||||||
|
modifyInputMap( defaults, "TabbedPane.focusInputMap",
|
||||||
|
"ctrl DOWN", null,
|
||||||
|
"ctrl KP_DOWN", null
|
||||||
|
);
|
||||||
|
|
||||||
|
// table
|
||||||
|
modifyInputMap( defaults, "Table.ancestorInputMap",
|
||||||
|
"alt TAB", "focusHeader",
|
||||||
|
"shift alt TAB", "focusHeader",
|
||||||
|
"meta A", "selectAll",
|
||||||
|
"meta C", "copy",
|
||||||
|
"meta V", "paste",
|
||||||
|
"meta X", "cut",
|
||||||
|
|
||||||
|
// let parent scroll pane do the macOS typical scrolling without changing selection
|
||||||
|
"HOME", null,
|
||||||
|
"END", null,
|
||||||
|
"PAGE_UP", null,
|
||||||
|
"PAGE_DOWN", null,
|
||||||
|
|
||||||
|
"ctrl A", null,
|
||||||
|
"ctrl BACK_SLASH", null,
|
||||||
|
"ctrl C", null,
|
||||||
|
"ctrl DOWN", null,
|
||||||
|
"ctrl END", null,
|
||||||
|
"ctrl HOME", null,
|
||||||
|
"ctrl INSERT", null,
|
||||||
|
"ctrl KP_DOWN", null,
|
||||||
|
"ctrl KP_LEFT", null,
|
||||||
|
"ctrl KP_RIGHT", null,
|
||||||
|
"ctrl KP_UP", null,
|
||||||
|
"ctrl LEFT", null,
|
||||||
|
"ctrl PAGE_DOWN", null,
|
||||||
|
"ctrl PAGE_UP", null,
|
||||||
|
"ctrl RIGHT", null,
|
||||||
|
"ctrl SLASH", null,
|
||||||
|
"ctrl SPACE", null,
|
||||||
|
"ctrl UP", null,
|
||||||
|
"ctrl V", null,
|
||||||
|
"ctrl X", null,
|
||||||
|
"F2", null,
|
||||||
|
"F8", null,
|
||||||
|
"SPACE", null,
|
||||||
|
"shift ctrl DOWN", null,
|
||||||
|
"shift ctrl END", null,
|
||||||
|
"shift ctrl HOME", null,
|
||||||
|
"shift ctrl KP_DOWN", null,
|
||||||
|
"shift ctrl KP_LEFT", null,
|
||||||
|
"shift ctrl KP_RIGHT", null,
|
||||||
|
"shift ctrl KP_UP", null,
|
||||||
|
"shift ctrl LEFT", null,
|
||||||
|
"shift ctrl PAGE_DOWN", null,
|
||||||
|
"shift ctrl PAGE_UP", null,
|
||||||
|
"shift ctrl RIGHT", null,
|
||||||
|
"shift ctrl SPACE", null,
|
||||||
|
"shift ctrl UP", null,
|
||||||
|
"shift DELETE", null,
|
||||||
|
"shift INSERT", null,
|
||||||
|
"shift SPACE", null
|
||||||
|
);
|
||||||
|
modifyInputMap( defaults, "Table.ancestorInputMap.RightToLeft",
|
||||||
|
"ctrl KP_LEFT", null,
|
||||||
|
"ctrl KP_RIGHT", null,
|
||||||
|
"ctrl LEFT", null,
|
||||||
|
"ctrl RIGHT", null,
|
||||||
|
"shift ctrl KP_LEFT", null,
|
||||||
|
"shift ctrl KP_RIGHT", null,
|
||||||
|
"shift ctrl LEFT", null,
|
||||||
|
"shift ctrl RIGHT", null
|
||||||
|
);
|
||||||
|
|
||||||
|
// tree node expanding/collapsing
|
||||||
|
modifyInputMap( defaults, "Tree.focusInputMap",
|
||||||
|
"LEFT", "selectParent",
|
||||||
|
"RIGHT", "selectChild",
|
||||||
|
"KP_LEFT", "selectParent",
|
||||||
|
"KP_RIGHT", "selectChild",
|
||||||
|
"shift LEFT", "selectParent",
|
||||||
|
"shift RIGHT", "selectChild",
|
||||||
|
"shift KP_LEFT", "selectParent",
|
||||||
|
"shift KP_RIGHT", "selectChild",
|
||||||
|
"alt LEFT", "selectParent",
|
||||||
|
"alt RIGHT", "selectChild",
|
||||||
|
"alt KP_LEFT", "selectParent",
|
||||||
|
"alt KP_RIGHT", "selectChild",
|
||||||
|
|
||||||
|
"shift HOME", "selectFirstExtendSelection",
|
||||||
|
"shift END", "selectLastExtendSelection",
|
||||||
|
|
||||||
|
"meta A", "selectAll",
|
||||||
|
"meta C", "copy",
|
||||||
|
"meta V", "paste",
|
||||||
|
"meta X", "cut",
|
||||||
|
|
||||||
|
// let parent scroll pane do the macOS typical scrolling without changing selection
|
||||||
|
"HOME", null,
|
||||||
|
"END", null,
|
||||||
|
"PAGE_UP", null,
|
||||||
|
"PAGE_DOWN", null,
|
||||||
|
|
||||||
|
"ctrl LEFT", null,
|
||||||
|
"ctrl RIGHT", null,
|
||||||
|
"ctrl KP_LEFT", null,
|
||||||
|
"ctrl KP_RIGHT", null,
|
||||||
|
|
||||||
|
"ctrl A", null,
|
||||||
|
"ctrl BACK_SLASH", null,
|
||||||
|
"ctrl C", null,
|
||||||
|
"ctrl DOWN", null,
|
||||||
|
"ctrl END", null,
|
||||||
|
"ctrl HOME", null,
|
||||||
|
"ctrl INSERT", null,
|
||||||
|
"ctrl KP_DOWN", null,
|
||||||
|
"ctrl KP_UP", null,
|
||||||
|
"ctrl PAGE_DOWN", null,
|
||||||
|
"ctrl PAGE_UP", null,
|
||||||
|
"ctrl SLASH", null,
|
||||||
|
"ctrl SPACE", null,
|
||||||
|
"ctrl UP", null,
|
||||||
|
"ctrl V", null,
|
||||||
|
"ctrl X", null,
|
||||||
|
"F2", null,
|
||||||
|
"SPACE", null,
|
||||||
|
"shift ctrl DOWN", null,
|
||||||
|
"shift ctrl END", null,
|
||||||
|
"shift ctrl HOME", null,
|
||||||
|
"shift ctrl KP_DOWN", null,
|
||||||
|
"shift ctrl KP_UP", null,
|
||||||
|
"shift ctrl PAGE_DOWN", null,
|
||||||
|
"shift ctrl PAGE_UP", null,
|
||||||
|
"shift ctrl SPACE", null,
|
||||||
|
"shift ctrl UP", null,
|
||||||
|
"shift DELETE", null,
|
||||||
|
"shift INSERT", null,
|
||||||
|
"shift PAGE_DOWN", null,
|
||||||
|
"shift PAGE_UP", null,
|
||||||
|
"shift SPACE", null
|
||||||
|
);
|
||||||
|
defaults.put( "Tree.focusInputMap.RightToLeft", new UIDefaults.LazyInputMap( new Object[] {
|
||||||
|
"LEFT", "selectChild",
|
||||||
|
"RIGHT", "selectParent",
|
||||||
|
"KP_LEFT", "selectChild",
|
||||||
|
"KP_RIGHT", "selectParent",
|
||||||
|
"shift LEFT", "selectChild",
|
||||||
|
"shift RIGHT", "selectParent",
|
||||||
|
"shift KP_LEFT", "selectChild",
|
||||||
|
"shift KP_RIGHT", "selectParent",
|
||||||
|
"alt LEFT", "selectChild",
|
||||||
|
"alt RIGHT", "selectParent",
|
||||||
|
"alt KP_LEFT", "selectChild",
|
||||||
|
"alt KP_RIGHT", "selectParent"
|
||||||
|
} ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void modifyInputMap( UIDefaults defaults, String key, Object... bindings ) {
|
||||||
|
// Note: not using `defaults.get(key)` here because this would resolve the lazy value
|
||||||
|
defaults.put( key, new LazyModifyInputMap( defaults.remove( key ), bindings ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
private static <T> T mac( T value, T macValue ) {
|
||||||
|
return SystemInfo.IS_MAC ? macValue : value;
|
||||||
|
}
|
||||||
|
|
||||||
|
//---- class LazyInputMapEx -----------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lazily creates a input map.
|
||||||
|
* Similar to {@link UIDefaults.LazyInputMap}, but can use multiple bindings arrays.
|
||||||
|
*/
|
||||||
|
private static class LazyInputMapEx
|
||||||
|
implements LazyValue
|
||||||
|
{
|
||||||
|
private final Object[][] bindingsArray;
|
||||||
|
|
||||||
|
LazyInputMapEx( Object[]... bindingsArray ) {
|
||||||
|
this.bindingsArray = bindingsArray;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object createValue( UIDefaults table ) {
|
||||||
|
InputMap inputMap = new InputMapUIResource();
|
||||||
|
for( Object[] bindings : bindingsArray )
|
||||||
|
LookAndFeel.loadKeyBindings( inputMap, bindings );
|
||||||
|
return inputMap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//---- class LazyModifyInputMap -------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Takes a (lazy) base input map and lazily applies modifications to it specified in bindings.
|
||||||
|
*/
|
||||||
|
private static class LazyModifyInputMap
|
||||||
|
implements LazyValue
|
||||||
|
{
|
||||||
|
private final Object baseInputMap;
|
||||||
|
private final Object[] bindings;
|
||||||
|
|
||||||
|
LazyModifyInputMap( Object baseInputMap, Object[] bindings ) {
|
||||||
|
this.baseInputMap = baseInputMap;
|
||||||
|
this.bindings = bindings;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object createValue( UIDefaults table ) {
|
||||||
|
// get base input map
|
||||||
|
InputMap inputMap = (baseInputMap instanceof LazyValue)
|
||||||
|
? (InputMap) ((LazyValue)baseInputMap).createValue( table )
|
||||||
|
: (InputMap) baseInputMap;
|
||||||
|
|
||||||
|
// modify input map (replace or remove)
|
||||||
|
for( int i = 0; i < bindings.length; i += 2 ) {
|
||||||
|
KeyStroke keyStroke = KeyStroke.getKeyStroke( (String) bindings[i] );
|
||||||
|
if( bindings[i + 1] != null )
|
||||||
|
inputMap.put( keyStroke, bindings[i + 1] );
|
||||||
|
else
|
||||||
|
inputMap.remove( keyStroke );
|
||||||
|
}
|
||||||
|
|
||||||
|
return inputMap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -32,11 +32,11 @@ public class FlatIntelliJLaf
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getName() {
|
public String getName() {
|
||||||
return "Flat IntelliJ";
|
return "FlatLaf IntelliJ";
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getDescription() {
|
public String getDescription() {
|
||||||
return "Flat IntelliJ Look and Feel";
|
return "FlatLaf IntelliJ Look and Feel";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,32 +18,47 @@ package com.formdev.flatlaf;
|
|||||||
|
|
||||||
import java.awt.Color;
|
import java.awt.Color;
|
||||||
import java.awt.Component;
|
import java.awt.Component;
|
||||||
import java.awt.Container;
|
|
||||||
import java.awt.EventQueue;
|
import java.awt.EventQueue;
|
||||||
import java.awt.Font;
|
import java.awt.Font;
|
||||||
import java.awt.KeyEventPostProcessor;
|
import java.awt.Image;
|
||||||
import java.awt.KeyboardFocusManager;
|
import java.awt.RenderingHints;
|
||||||
import java.awt.Toolkit;
|
import java.awt.Toolkit;
|
||||||
import java.awt.Window;
|
import java.awt.Window;
|
||||||
import java.awt.event.KeyEvent;
|
import java.awt.image.FilteredImageSource;
|
||||||
|
import java.awt.image.ImageFilter;
|
||||||
|
import java.awt.image.ImageProducer;
|
||||||
import java.beans.PropertyChangeEvent;
|
import java.beans.PropertyChangeEvent;
|
||||||
import java.beans.PropertyChangeListener;
|
import java.beans.PropertyChangeListener;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Properties;
|
||||||
|
import java.util.ServiceLoader;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
import javax.swing.AbstractButton;
|
import java.util.function.Function;
|
||||||
import javax.swing.JLabel;
|
import java.util.logging.Level;
|
||||||
import javax.swing.JTabbedPane;
|
import java.util.logging.Logger;
|
||||||
|
import javax.swing.BorderFactory;
|
||||||
|
import javax.swing.Icon;
|
||||||
|
import javax.swing.ImageIcon;
|
||||||
|
import javax.swing.JComponent;
|
||||||
import javax.swing.LookAndFeel;
|
import javax.swing.LookAndFeel;
|
||||||
import javax.swing.PopupFactory;
|
import javax.swing.PopupFactory;
|
||||||
import javax.swing.SwingUtilities;
|
import javax.swing.SwingUtilities;
|
||||||
import javax.swing.UIDefaults;
|
import javax.swing.UIDefaults;
|
||||||
import javax.swing.UIManager;
|
import javax.swing.UIManager;
|
||||||
import javax.swing.UnsupportedLookAndFeelException;
|
import javax.swing.UnsupportedLookAndFeelException;
|
||||||
|
import javax.swing.UIDefaults.ActiveValue;
|
||||||
import javax.swing.plaf.ColorUIResource;
|
import javax.swing.plaf.ColorUIResource;
|
||||||
import javax.swing.plaf.FontUIResource;
|
import javax.swing.plaf.FontUIResource;
|
||||||
|
import javax.swing.plaf.UIResource;
|
||||||
import javax.swing.plaf.basic.BasicLookAndFeel;
|
import javax.swing.plaf.basic.BasicLookAndFeel;
|
||||||
import javax.swing.plaf.metal.MetalLookAndFeel;
|
import javax.swing.text.StyleContext;
|
||||||
import javax.swing.text.html.HTMLEditorKit;
|
import javax.swing.text.html.HTMLEditorKit;
|
||||||
|
import com.formdev.flatlaf.ui.FlatPopupFactory;
|
||||||
|
import com.formdev.flatlaf.util.GrayFilter;
|
||||||
|
import com.formdev.flatlaf.util.MultiResolutionImageSupport;
|
||||||
import com.formdev.flatlaf.util.SystemInfo;
|
import com.formdev.flatlaf.util.SystemInfo;
|
||||||
import com.formdev.flatlaf.util.UIScale;
|
import com.formdev.flatlaf.util.UIScale;
|
||||||
|
|
||||||
@@ -55,23 +70,28 @@ import com.formdev.flatlaf.util.UIScale;
|
|||||||
public abstract class FlatLaf
|
public abstract class FlatLaf
|
||||||
extends BasicLookAndFeel
|
extends BasicLookAndFeel
|
||||||
{
|
{
|
||||||
private BasicLookAndFeel base;
|
static final Logger LOG = Logger.getLogger( FlatLaf.class.getName() );
|
||||||
|
private static final String DESKTOPFONTHINTS = "awt.font.desktophints";
|
||||||
|
|
||||||
private String desktopPropertyName;
|
private String desktopPropertyName;
|
||||||
|
private String desktopPropertyName2;
|
||||||
private PropertyChangeListener desktopPropertyListener;
|
private PropertyChangeListener desktopPropertyListener;
|
||||||
|
|
||||||
private KeyEventPostProcessor mnemonicListener;
|
private static boolean aquaLoaded;
|
||||||
private static boolean showMnemonics;
|
private static boolean updateUIPending;
|
||||||
|
|
||||||
|
private PopupFactory oldPopupFactory;
|
||||||
|
private MnemonicHandler mnemonicHandler;
|
||||||
|
|
||||||
private Consumer<UIDefaults> postInitialization;
|
private Consumer<UIDefaults> postInitialization;
|
||||||
|
|
||||||
public static boolean install( LookAndFeel newLookAndFeel ) {
|
public static boolean install( LookAndFeel newLookAndFeel ) {
|
||||||
try {
|
try {
|
||||||
UIManager.setLookAndFeel( newLookAndFeel );
|
UIManager.setLookAndFeel( newLookAndFeel );
|
||||||
return true;
|
return true;
|
||||||
} catch( Exception ex ) {
|
} catch( Exception ex ) {
|
||||||
System.err.println( "Failed to initialize look and feel " + newLookAndFeel.getClass().getName() );
|
LOG.log( Level.SEVERE, "FlatLaf: Failed to initialize look and feel '" + newLookAndFeel.getClass().getName() + "'.", ex );
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -100,23 +120,40 @@ public abstract class FlatLaf
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Icon getDisabledIcon( JComponent component, Icon icon ) {
|
||||||
|
if( icon instanceof ImageIcon ) {
|
||||||
|
Object grayFilter = UIManager.get( "Component.grayFilter" );
|
||||||
|
ImageFilter filter = (grayFilter instanceof ImageFilter)
|
||||||
|
? (ImageFilter) grayFilter
|
||||||
|
: GrayFilter.createDisabledIconFilter( isDark() ); // fallback
|
||||||
|
|
||||||
|
Function<Image, Image> mapper = img -> {
|
||||||
|
ImageProducer producer = new FilteredImageSource( img.getSource(), filter );
|
||||||
|
return Toolkit.getDefaultToolkit().createImage( producer );
|
||||||
|
};
|
||||||
|
|
||||||
|
Image image = ((ImageIcon)icon).getImage();
|
||||||
|
return new ImageIconUIResource( MultiResolutionImageSupport.map( image, mapper ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void initialize() {
|
public void initialize() {
|
||||||
getBase().initialize();
|
if( SystemInfo.IS_MAC )
|
||||||
|
initializeAqua();
|
||||||
|
|
||||||
super.initialize();
|
super.initialize();
|
||||||
|
|
||||||
// make sure that a plain popup factory is used (otherwise sub-menu rendering
|
// install popup factory
|
||||||
// is "jittery" on Mac, where AquaLookAndFeel installs its own popup factory)
|
oldPopupFactory = PopupFactory.getSharedInstance();
|
||||||
if( PopupFactory.getSharedInstance().getClass() != PopupFactory.class )
|
PopupFactory.setSharedInstance( new FlatPopupFactory() );
|
||||||
PopupFactory.setSharedInstance( new PopupFactory() );
|
|
||||||
|
|
||||||
// add mnemonic listener
|
// install mnemonic handler
|
||||||
mnemonicListener = e -> {
|
mnemonicHandler = new MnemonicHandler();
|
||||||
checkShowMnemonics( e );
|
mnemonicHandler.install();
|
||||||
return false;
|
|
||||||
};
|
|
||||||
KeyboardFocusManager.getCurrentKeyboardFocusManager().addKeyEventPostProcessor( mnemonicListener );
|
|
||||||
|
|
||||||
// listen to desktop property changes to update UI if system font or scaling changes
|
// listen to desktop property changes to update UI if system font or scaling changes
|
||||||
if( SystemInfo.IS_WINDOWS ) {
|
if( SystemInfo.IS_WINDOWS ) {
|
||||||
@@ -124,16 +161,32 @@ public abstract class FlatLaf
|
|||||||
// Settings > Ease of Access > Display > Make text bigger (100% - 225%)
|
// Settings > Ease of Access > Display > Make text bigger (100% - 225%)
|
||||||
desktopPropertyName = "win.messagebox.font";
|
desktopPropertyName = "win.messagebox.font";
|
||||||
} else if( SystemInfo.IS_LINUX ) {
|
} else if( SystemInfo.IS_LINUX ) {
|
||||||
|
// Linux/Gnome allows changing font in "Tweaks" app
|
||||||
|
desktopPropertyName = "gnome.Gtk/FontName";
|
||||||
|
|
||||||
// Linux/Gnome allows extra scaling and larger text:
|
// Linux/Gnome allows extra scaling and larger text:
|
||||||
// Settings > Devices > Displays > Scale (100% or 200%)
|
// Settings > Devices > Displays > Scale (100% or 200%)
|
||||||
// Settings > Universal access > Large Text (off or on, 125%)
|
// Settings > Universal access > Large Text (off or on, 125%)
|
||||||
desktopPropertyName = "gnome.Xft/DPI";
|
// "Tweaks" app > Fonts > Scaling Factor (0,5 - 3)
|
||||||
|
desktopPropertyName2 = "gnome.Xft/DPI";
|
||||||
}
|
}
|
||||||
if( desktopPropertyName != null ) {
|
if( desktopPropertyName != null ) {
|
||||||
desktopPropertyListener = e -> {
|
desktopPropertyListener = e -> {
|
||||||
reSetLookAndFeel();
|
String propertyName = e.getPropertyName();
|
||||||
|
if( desktopPropertyName.equals( propertyName ) || propertyName.equals( desktopPropertyName2 ) )
|
||||||
|
reSetLookAndFeel();
|
||||||
|
else if( DESKTOPFONTHINTS.equals( propertyName ) ) {
|
||||||
|
if( UIManager.getLookAndFeel() instanceof FlatLaf ) {
|
||||||
|
putAATextInfo( UIManager.getLookAndFeelDefaults() );
|
||||||
|
updateUILater();
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
Toolkit.getDefaultToolkit().addPropertyChangeListener( desktopPropertyName, desktopPropertyListener );
|
Toolkit toolkit = Toolkit.getDefaultToolkit();
|
||||||
|
toolkit.addPropertyChangeListener( desktopPropertyName, desktopPropertyListener );
|
||||||
|
if( desktopPropertyName2 != null )
|
||||||
|
toolkit.addPropertyChangeListener( desktopPropertyName2, desktopPropertyListener );
|
||||||
|
toolkit.addPropertyChangeListener( DESKTOPFONTHINTS, desktopPropertyListener );
|
||||||
}
|
}
|
||||||
|
|
||||||
// Following code should be ideally in initialize(), but needs color from UI defaults.
|
// Following code should be ideally in initialize(), but needs color from UI defaults.
|
||||||
@@ -153,88 +206,147 @@ public abstract class FlatLaf
|
|||||||
public void uninitialize() {
|
public void uninitialize() {
|
||||||
// remove desktop property listener
|
// remove desktop property listener
|
||||||
if( desktopPropertyListener != null ) {
|
if( desktopPropertyListener != null ) {
|
||||||
Toolkit.getDefaultToolkit().removePropertyChangeListener( desktopPropertyName, desktopPropertyListener );
|
Toolkit toolkit = Toolkit.getDefaultToolkit();
|
||||||
|
toolkit.removePropertyChangeListener( desktopPropertyName, desktopPropertyListener );
|
||||||
|
if( desktopPropertyName2 != null )
|
||||||
|
toolkit.removePropertyChangeListener( desktopPropertyName2, desktopPropertyListener );
|
||||||
|
toolkit.removePropertyChangeListener( DESKTOPFONTHINTS, desktopPropertyListener );
|
||||||
desktopPropertyName = null;
|
desktopPropertyName = null;
|
||||||
|
desktopPropertyName2 = null;
|
||||||
desktopPropertyListener = null;
|
desktopPropertyListener = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// remove mnemonic listener
|
// uninstall popup factory
|
||||||
if( mnemonicListener != null ) {
|
if( oldPopupFactory != null ) {
|
||||||
KeyboardFocusManager.getCurrentKeyboardFocusManager().removeKeyEventPostProcessor( mnemonicListener );
|
PopupFactory.setSharedInstance( oldPopupFactory );
|
||||||
mnemonicListener = null;
|
oldPopupFactory = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// uninstall mnemonic handler
|
||||||
|
if( mnemonicHandler != null ) {
|
||||||
|
mnemonicHandler.uninstall();
|
||||||
|
mnemonicHandler = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// restore default link color
|
// restore default link color
|
||||||
new HTMLEditorKit().getStyleSheet().addRule( "a { color: blue; }" );
|
new HTMLEditorKit().getStyleSheet().addRule( "a { color: blue; }" );
|
||||||
postInitialization = null;
|
postInitialization = null;
|
||||||
|
|
||||||
if( base != null )
|
|
||||||
base.uninitialize();
|
|
||||||
|
|
||||||
super.uninitialize();
|
super.uninitialize();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get/create base LaF. This is used to grab base UI defaults from different LaFs.
|
* Initialize Aqua LaF on macOS, which is required for using Mac screen menubar.
|
||||||
* E.g. on Mac from system dependent LaF, otherwise from Metal LaF.
|
* (at least on Java 8, since 9 it seems to work without it)
|
||||||
|
* <p>
|
||||||
|
* This loads the native library "osxui" and initializes JRSUI.
|
||||||
|
* Because both are not unloaded/uninitialized, Aqua LaF is initialized only once.
|
||||||
*/
|
*/
|
||||||
private BasicLookAndFeel getBase() {
|
private void initializeAqua() {
|
||||||
if( base == null ) {
|
if( aquaLoaded )
|
||||||
if( SystemInfo.IS_MAC ) {
|
return;
|
||||||
// use Mac Aqua LaF as base
|
|
||||||
try {
|
aquaLoaded = true;
|
||||||
base = (BasicLookAndFeel) Class.forName( "com.apple.laf.AquaLookAndFeel" ).newInstance();
|
|
||||||
} catch( Exception ex ) {
|
// create macOS Aqua LaF
|
||||||
ex.printStackTrace();
|
String aquaLafClassName = "com.apple.laf.AquaLookAndFeel";
|
||||||
throw new IllegalStateException();
|
BasicLookAndFeel aquaLaf;
|
||||||
}
|
try {
|
||||||
|
if( SystemInfo.IS_JAVA_9_OR_LATER ) {
|
||||||
|
Method m = UIManager.class.getMethod( "createLookAndFeel", String.class );
|
||||||
|
aquaLaf = (BasicLookAndFeel) m.invoke( null, "Mac OS X" );
|
||||||
} else
|
} else
|
||||||
base = new MetalLookAndFeel();
|
aquaLaf = (BasicLookAndFeel) Class.forName( aquaLafClassName ).newInstance();
|
||||||
|
} catch( Exception ex ) {
|
||||||
|
LOG.log( Level.SEVERE, "FlatLaf: Failed to initialize Aqua look and feel '" + aquaLafClassName + "'.", ex );
|
||||||
|
throw new IllegalStateException();
|
||||||
}
|
}
|
||||||
return base;
|
|
||||||
|
// remember popup factory because aquaLaf.initialize() installs its own
|
||||||
|
// factory, which makes sub-menu rendering "jittery"
|
||||||
|
PopupFactory oldPopupFactory = PopupFactory.getSharedInstance();
|
||||||
|
|
||||||
|
// initialize Aqua LaF
|
||||||
|
aquaLaf.initialize();
|
||||||
|
aquaLaf.uninitialize();
|
||||||
|
|
||||||
|
// restore popup factory
|
||||||
|
PopupFactory.setSharedInstance( oldPopupFactory );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public UIDefaults getDefaults() {
|
public UIDefaults getDefaults() {
|
||||||
UIDefaults defaults = getBase().getDefaults();
|
UIDefaults defaults = super.getDefaults();
|
||||||
|
|
||||||
// add Metal resource bundle, which is required for FlatFileChooserUI
|
// add resource bundle for localized texts
|
||||||
defaults.addResourceBundle( "com.sun.swing.internal.plaf.metal.resources.metal" );
|
defaults.addResourceBundle( "com.formdev.flatlaf.resources.Bundle" );
|
||||||
|
|
||||||
// initialize some defaults (for overriding) that are used in basic UI delegates,
|
// initialize some defaults (for overriding) that are used in UI delegates,
|
||||||
// but are not set in MetalLookAndFeel or BasicLookAndFeel
|
// but are not set in BasicLookAndFeel
|
||||||
Color control = defaults.getColor( "control" );
|
putDefaults( defaults, defaults.getColor( "control" ),
|
||||||
defaults.put( "EditorPane.disabledBackground", control );
|
"EditorPane.disabledBackground",
|
||||||
defaults.put( "EditorPane.inactiveBackground", control );
|
"EditorPane.inactiveBackground",
|
||||||
defaults.put( "FormattedTextField.disabledBackground", control );
|
"FormattedTextField.disabledBackground",
|
||||||
defaults.put( "PasswordField.disabledBackground", control );
|
"PasswordField.disabledBackground",
|
||||||
defaults.put( "TextArea.disabledBackground", control );
|
"Spinner.disabledBackground",
|
||||||
defaults.put( "TextArea.inactiveBackground", control );
|
"TextArea.disabledBackground",
|
||||||
defaults.put( "TextField.disabledBackground", control );
|
"TextArea.inactiveBackground",
|
||||||
defaults.put( "TextPane.disabledBackground", control );
|
"TextField.disabledBackground",
|
||||||
defaults.put( "TextPane.inactiveBackground", control );
|
"TextPane.disabledBackground",
|
||||||
|
"TextPane.inactiveBackground" );
|
||||||
// initialize some own defaults (for overriding)
|
putDefaults( defaults, defaults.getColor( "textInactiveText" ),
|
||||||
defaults.put( "Spinner.disabledBackground", control );
|
"Button.disabledText",
|
||||||
defaults.put( "Spinner.disabledForeground", control );
|
"CheckBox.disabledText",
|
||||||
|
"CheckBoxMenuItem.disabledForeground",
|
||||||
// remember MenuBarUI from Mac Aqua LaF if Mac screen menubar is enabled
|
"Menu.disabledForeground",
|
||||||
boolean useScreenMenuBar = SystemInfo.IS_MAC && "true".equals( System.getProperty( "apple.laf.useScreenMenuBar" ) );
|
"MenuItem.disabledForeground",
|
||||||
Object aquaMenuBarUI = useScreenMenuBar ? defaults.get( "MenuBarUI" ) : null;
|
"RadioButton.disabledText",
|
||||||
|
"RadioButtonMenuItem.disabledForeground",
|
||||||
|
"Spinner.disabledForeground",
|
||||||
|
"ToggleButton.disabledText" );
|
||||||
|
putDefaults( defaults, defaults.getColor( "textText" ),
|
||||||
|
"DesktopIcon.foreground" );
|
||||||
|
|
||||||
initFonts( defaults );
|
initFonts( defaults );
|
||||||
initIconColors( defaults, isDark() );
|
initIconColors( defaults, isDark() );
|
||||||
|
FlatInputMaps.initInputMaps( defaults );
|
||||||
|
|
||||||
|
// get addons and sort them by priority
|
||||||
|
ServiceLoader<FlatDefaultsAddon> addonLoader = ServiceLoader.load( FlatDefaultsAddon.class );
|
||||||
|
List<FlatDefaultsAddon> addons = new ArrayList<>();
|
||||||
|
for( FlatDefaultsAddon addon : addonLoader )
|
||||||
|
addons.add( addon );
|
||||||
|
addons.sort( (addon1, addon2) -> addon1.getPriority() - addon2.getPriority() );
|
||||||
|
|
||||||
// load defaults from properties
|
// load defaults from properties
|
||||||
List<Class<?>> lafClassesForDefaultsLoading = getLafClassesForDefaultsLoading();
|
List<Class<?>> lafClassesForDefaultsLoading = getLafClassesForDefaultsLoading();
|
||||||
if( lafClassesForDefaultsLoading != null )
|
if( lafClassesForDefaultsLoading != null )
|
||||||
UIDefaultsLoader.loadDefaultsFromProperties( lafClassesForDefaultsLoading, defaults );
|
UIDefaultsLoader.loadDefaultsFromProperties( lafClassesForDefaultsLoading, addons, getAdditionalDefaults(), isDark(), defaults );
|
||||||
else
|
else
|
||||||
UIDefaultsLoader.loadDefaultsFromProperties( getClass(), defaults );
|
UIDefaultsLoader.loadDefaultsFromProperties( getClass(), addons, getAdditionalDefaults(), isDark(), defaults );
|
||||||
|
|
||||||
// use Aqua MenuBarUI if Mac screen menubar is enabled
|
// use Aqua MenuBarUI if Mac screen menubar is enabled
|
||||||
if( useScreenMenuBar )
|
if( SystemInfo.IS_MAC && Boolean.getBoolean( "apple.laf.useScreenMenuBar" ) ) {
|
||||||
defaults.put( "MenuBarUI", aquaMenuBarUI );
|
defaults.put( "MenuBarUI", "com.apple.laf.AquaMenuBarUI" );
|
||||||
|
|
||||||
|
// add defaults necessary for AquaMenuBarUI
|
||||||
|
defaults.put( "MenuBar.backgroundPainter", BorderFactory.createEmptyBorder() );
|
||||||
|
}
|
||||||
|
|
||||||
|
// initialize text antialiasing
|
||||||
|
putAATextInfo( defaults );
|
||||||
|
|
||||||
|
// apply additional defaults (e.g. from IntelliJ themes)
|
||||||
|
applyAdditionalDefaults( defaults );
|
||||||
|
|
||||||
|
// allow addons modifying UI defaults
|
||||||
|
for( FlatDefaultsAddon addon : addons )
|
||||||
|
addon.afterDefaultsLoading( this, defaults );
|
||||||
|
|
||||||
|
// add user scale factor to allow layout managers (e.g. MigLayout) to use it
|
||||||
|
defaults.put( "laf.scaleFactor", (ActiveValue) t -> {
|
||||||
|
return UIScale.getUserScaleFactor();
|
||||||
|
} );
|
||||||
|
|
||||||
if( postInitialization != null ) {
|
if( postInitialization != null ) {
|
||||||
postInitialization.accept( defaults );
|
postInitialization.accept( defaults );
|
||||||
@@ -244,7 +356,14 @@ public abstract class FlatLaf
|
|||||||
return defaults;
|
return defaults;
|
||||||
}
|
}
|
||||||
|
|
||||||
List<Class<?>> getLafClassesForDefaultsLoading() {
|
void applyAdditionalDefaults( UIDefaults defaults ) {
|
||||||
|
}
|
||||||
|
|
||||||
|
protected List<Class<?>> getLafClassesForDefaultsLoading() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Properties getAdditionalDefaults() {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -254,17 +373,22 @@ public abstract class FlatLaf
|
|||||||
if( SystemInfo.IS_WINDOWS ) {
|
if( SystemInfo.IS_WINDOWS ) {
|
||||||
Font winFont = (Font) Toolkit.getDefaultToolkit().getDesktopProperty( "win.messagebox.font" );
|
Font winFont = (Font) Toolkit.getDefaultToolkit().getDesktopProperty( "win.messagebox.font" );
|
||||||
if( winFont != null )
|
if( winFont != null )
|
||||||
uiFont = new FontUIResource( winFont );
|
uiFont = createCompositeFont( winFont.getFamily(), winFont.getStyle(), winFont.getSize() );
|
||||||
|
|
||||||
} else if( SystemInfo.IS_MAC ) {
|
} else if( SystemInfo.IS_MAC ) {
|
||||||
Font font = defaults.getFont( "Label.font" );
|
String fontName;
|
||||||
|
if( SystemInfo.IS_MAC_OS_10_15_CATALINA_OR_LATER ) {
|
||||||
if( SystemInfo.IS_MAC_OS_10_11_EL_CAPITAN_OR_LATER ) {
|
// use Helvetica Neue font
|
||||||
|
fontName = "Helvetica Neue";
|
||||||
|
} else if( SystemInfo.IS_MAC_OS_10_11_EL_CAPITAN_OR_LATER ) {
|
||||||
// use San Francisco Text font
|
// use San Francisco Text font
|
||||||
font = new FontUIResource( ".SF NS Text", font.getStyle(), font.getSize() );
|
fontName = ".SF NS Text";
|
||||||
|
} else {
|
||||||
|
// default font on older systems (see com.apple.laf.AquaFonts)
|
||||||
|
fontName = "Lucida Grande";
|
||||||
}
|
}
|
||||||
|
|
||||||
uiFont = (font instanceof FontUIResource) ? (FontUIResource) font : new FontUIResource( font );
|
uiFont = createCompositeFont( fontName, Font.PLAIN, 13 );
|
||||||
|
|
||||||
} else if( SystemInfo.IS_LINUX ) {
|
} else if( SystemInfo.IS_LINUX ) {
|
||||||
Font font = LinuxFontPolicy.getFont();
|
Font font = LinuxFontPolicy.getFont();
|
||||||
@@ -272,19 +396,34 @@ public abstract class FlatLaf
|
|||||||
}
|
}
|
||||||
|
|
||||||
if( uiFont == null )
|
if( uiFont == null )
|
||||||
return;
|
uiFont = createCompositeFont( Font.SANS_SERIF, Font.PLAIN, 12 );
|
||||||
|
|
||||||
uiFont = UIScale.applyCustomScaleFactor( uiFont );
|
uiFont = UIScale.applyCustomScaleFactor( uiFont );
|
||||||
|
|
||||||
|
// use active value for all fonts to allow changing fonts in all components
|
||||||
|
// (similar as in Nimbus L&F) with:
|
||||||
|
// UIManager.put( "defaultFont", myFont );
|
||||||
|
Object activeFont = new ActiveFont( 1 );
|
||||||
|
|
||||||
// override fonts
|
// override fonts
|
||||||
for( Object key : defaults.keySet() ) {
|
for( Object key : defaults.keySet() ) {
|
||||||
if( key instanceof String && ((String)key).endsWith( ".font" ) )
|
if( key instanceof String && (((String)key).endsWith( ".font" ) || ((String)key).endsWith( "Font" )) )
|
||||||
defaults.put( key, uiFont );
|
defaults.put( key, activeFont );
|
||||||
}
|
}
|
||||||
defaults.put( "MenuItem.acceleratorFont", uiFont );
|
|
||||||
|
|
||||||
// use smaller font for progress bar
|
// use smaller font for progress bar
|
||||||
defaults.put( "ProgressBar.font", UIScale.scaleFont( uiFont, 0.85f ) );
|
defaults.put( "ProgressBar.font", new ActiveFont( 0.85f ) );
|
||||||
|
|
||||||
|
// set default font
|
||||||
|
defaults.put( "defaultFont", uiFont );
|
||||||
|
}
|
||||||
|
|
||||||
|
static FontUIResource createCompositeFont( String family, int style, int size ) {
|
||||||
|
// using StyleContext.getFont() here because it uses
|
||||||
|
// sun.font.FontUtilities.getCompositeFontUIResource()
|
||||||
|
// and creates a composite font that is able to display all Unicode characters
|
||||||
|
Font font = StyleContext.getDefaultStyleContext().getFont( family, style, size );
|
||||||
|
return (font instanceof FontUIResource) ? (FontUIResource) font : new FontUIResource( font );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -300,37 +439,59 @@ public abstract class FlatLaf
|
|||||||
* <a href="https://jetbrains.design/intellij/principles/icons/#action-icons">Action icons</a>
|
* <a href="https://jetbrains.design/intellij/principles/icons/#action-icons">Action icons</a>
|
||||||
* and
|
* and
|
||||||
* <a href="https://jetbrains.design/intellij/principles/icons/#noun-icons">Noun icons</a>
|
* <a href="https://jetbrains.design/intellij/principles/icons/#noun-icons">Noun icons</a>
|
||||||
|
* <p>
|
||||||
|
* These colors may be changed by IntelliJ Platform themes.
|
||||||
*/
|
*/
|
||||||
public static void initIconColors( UIDefaults defaults, boolean dark ) {
|
public static void initIconColors( UIDefaults defaults, boolean dark ) {
|
||||||
// colors for action icons
|
for( FlatIconColors c : FlatIconColors.values() ) {
|
||||||
// see https://jetbrains.design/intellij/principles/icons/#action-icons
|
if( c.light == !dark || c.dark == dark )
|
||||||
defaults.put( "Actions.Red", new ColorUIResource( !dark ? 0xDB5860 : 0xC75450 ) );
|
defaults.put( c.key, new ColorUIResource( c.rgb ) );
|
||||||
defaults.put( "Actions.Yellow", new ColorUIResource( !dark ? 0xEDA200 : 0xF0A732 ) );
|
}
|
||||||
defaults.put( "Actions.Green", new ColorUIResource( !dark ? 0x59A869 : 0x499C54 ) );
|
}
|
||||||
defaults.put( "Actions.Blue", new ColorUIResource( !dark ? 0x389FD6 : 0x3592C4 ) );
|
|
||||||
defaults.put( "Actions.Grey", new ColorUIResource( !dark ? 0x6E6E6E : 0xAFB1B3 ) );
|
|
||||||
defaults.put( "Actions.GreyInline", new ColorUIResource( !dark ? 0x7F8B91 : 0x7F8B91 ) );
|
|
||||||
|
|
||||||
// colors for object icons
|
private void putAATextInfo( UIDefaults defaults ) {
|
||||||
// see https://jetbrains.design/intellij/principles/icons/#noun-icons
|
if( SystemInfo.IS_JAVA_9_OR_LATER ) {
|
||||||
defaults.put( "Objects.Grey", new ColorUIResource( 0x9AA7B0 ) );
|
Object desktopHints = Toolkit.getDefaultToolkit().getDesktopProperty( DESKTOPFONTHINTS );
|
||||||
defaults.put( "Objects.Blue", new ColorUIResource( 0x40B6E0 ) );
|
if( desktopHints instanceof Map ) {
|
||||||
defaults.put( "Objects.Green", new ColorUIResource( 0x62B543 ) );
|
@SuppressWarnings( "unchecked" )
|
||||||
defaults.put( "Objects.Yellow", new ColorUIResource( 0xF4AF3D ) );
|
Map<Object, Object> hints = (Map<Object, Object>) desktopHints;
|
||||||
defaults.put( "Objects.YellowDark", new ColorUIResource( 0xD9A343 ) );
|
Object aaHint = hints.get( RenderingHints.KEY_TEXT_ANTIALIASING );
|
||||||
defaults.put( "Objects.Purple", new ColorUIResource( 0xB99BF8 ) );
|
if( aaHint != null &&
|
||||||
defaults.put( "Objects.Pink", new ColorUIResource( 0xF98B9E ) );
|
aaHint != RenderingHints.VALUE_TEXT_ANTIALIAS_OFF &&
|
||||||
defaults.put( "Objects.Red", new ColorUIResource( 0xF26522 ) );
|
aaHint != RenderingHints.VALUE_TEXT_ANTIALIAS_DEFAULT )
|
||||||
defaults.put( "Objects.RedStatus", new ColorUIResource( 0xE05555 ) );
|
{
|
||||||
defaults.put( "Objects.GreenAndroid", new ColorUIResource( 0xA4C639 ) );
|
defaults.put( RenderingHints.KEY_TEXT_ANTIALIASING, aaHint );
|
||||||
defaults.put( "Objects.BlackText", new ColorUIResource( 0x231F20 ) );
|
defaults.put( RenderingHints.KEY_TEXT_LCD_CONTRAST,
|
||||||
|
hints.get( RenderingHints.KEY_TEXT_LCD_CONTRAST ) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Java 8
|
||||||
|
try {
|
||||||
|
Object key = Class.forName( "sun.swing.SwingUtilities2" )
|
||||||
|
.getField( "AA_TEXT_PROPERTY_KEY" )
|
||||||
|
.get( null );
|
||||||
|
Object value = Class.forName( "sun.swing.SwingUtilities2$AATextInfo" )
|
||||||
|
.getMethod( "getAATextInfo", boolean.class )
|
||||||
|
.invoke( null, true );
|
||||||
|
defaults.put( key, value );
|
||||||
|
} catch( Exception ex ) {
|
||||||
|
Logger.getLogger( FlatLaf.class.getName() ).log( Level.SEVERE, null, ex );
|
||||||
|
throw new RuntimeException( ex );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void putDefaults( UIDefaults defaults, Object value, String... keys ) {
|
||||||
|
for( String key : keys )
|
||||||
|
defaults.put( key, value );
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void reSetLookAndFeel() {
|
private static void reSetLookAndFeel() {
|
||||||
EventQueue.invokeLater( () -> {
|
EventQueue.invokeLater( () -> {
|
||||||
|
LookAndFeel lookAndFeel = UIManager.getLookAndFeel();
|
||||||
try {
|
try {
|
||||||
// re-set current LaF
|
// re-set current LaF
|
||||||
LookAndFeel lookAndFeel = UIManager.getLookAndFeel();
|
|
||||||
UIManager.setLookAndFeel( lookAndFeel );
|
UIManager.setLookAndFeel( lookAndFeel );
|
||||||
|
|
||||||
// must fire property change events ourself because old and new LaF are the same
|
// must fire property change events ourself because old and new LaF are the same
|
||||||
@@ -341,13 +502,13 @@ public abstract class FlatLaf
|
|||||||
// update UI
|
// update UI
|
||||||
updateUI();
|
updateUI();
|
||||||
} catch( UnsupportedLookAndFeelException ex ) {
|
} catch( UnsupportedLookAndFeelException ex ) {
|
||||||
ex.printStackTrace();
|
LOG.log( Level.SEVERE, "FlatLaf: Failed to reinitialize look and feel '" + lookAndFeel.getClass().getName() + "'.", ex );
|
||||||
}
|
}
|
||||||
} );
|
} );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update UI of all application windows.
|
* Update UI of all application windows immediately.
|
||||||
* Invoke after changing LaF.
|
* Invoke after changing LaF.
|
||||||
*/
|
*/
|
||||||
public static void updateUI() {
|
public static void updateUI() {
|
||||||
@@ -355,73 +516,83 @@ public abstract class FlatLaf
|
|||||||
SwingUtilities.updateComponentTreeUI( w );
|
SwingUtilities.updateComponentTreeUI( w );
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isShowMnemonics() {
|
/**
|
||||||
return showMnemonics || !UIManager.getBoolean( "Component.hideMnemonics" );
|
* Update UI of all application windows later.
|
||||||
}
|
*/
|
||||||
|
public static void updateUILater() {
|
||||||
|
synchronized( FlatLaf.class ) {
|
||||||
|
if( updateUIPending )
|
||||||
|
return;
|
||||||
|
|
||||||
private static void checkShowMnemonics( KeyEvent e ) {
|
updateUIPending = true;
|
||||||
int keyCode = e.getKeyCode();
|
|
||||||
if( SystemInfo.IS_MAC ) {
|
|
||||||
// Ctrl+Alt keys must be pressed on Mac
|
|
||||||
if( keyCode == KeyEvent.VK_CONTROL || keyCode == KeyEvent.VK_ALT )
|
|
||||||
showMnemonics( e.getID() == KeyEvent.KEY_PRESSED && e.isControlDown() && e.isAltDown() );
|
|
||||||
} else {
|
|
||||||
// Alt key must be pressed on Windows and Linux
|
|
||||||
if( keyCode == KeyEvent.VK_ALT )
|
|
||||||
showMnemonics( e.getID() == KeyEvent.KEY_PRESSED );
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private static void showMnemonics( boolean show ) {
|
EventQueue.invokeLater( () -> {
|
||||||
if( show == showMnemonics )
|
updateUI();
|
||||||
return;
|
synchronized( FlatLaf.class ) {
|
||||||
|
updateUIPending = false;
|
||||||
showMnemonics = show;
|
|
||||||
|
|
||||||
// check whether it is necessary to repaint
|
|
||||||
if( !UIManager.getBoolean( "Component.hideMnemonics" ) )
|
|
||||||
return;
|
|
||||||
|
|
||||||
// get focus owner
|
|
||||||
Component focusOwner = KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner();
|
|
||||||
if( focusOwner == null )
|
|
||||||
return;
|
|
||||||
|
|
||||||
// get focused window
|
|
||||||
Window window = SwingUtilities.windowForComponent( focusOwner );
|
|
||||||
if( window == null )
|
|
||||||
return;
|
|
||||||
|
|
||||||
// repaint components with mnemonics in focused window
|
|
||||||
repaintMnemonics( window );
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void repaintMnemonics( Container container ) {
|
|
||||||
for( Component c : container.getComponents() ) {
|
|
||||||
if( hasMnemonic( c ) )
|
|
||||||
c.repaint();
|
|
||||||
|
|
||||||
if( c instanceof Container )
|
|
||||||
repaintMnemonics( (Container) c );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static boolean hasMnemonic( Component c ) {
|
|
||||||
if( c instanceof JLabel && ((JLabel)c).getDisplayedMnemonicIndex() >= 0 )
|
|
||||||
return true;
|
|
||||||
|
|
||||||
if( c instanceof AbstractButton && ((AbstractButton)c).getDisplayedMnemonicIndex() >= 0 )
|
|
||||||
return true;
|
|
||||||
|
|
||||||
if( c instanceof JTabbedPane ) {
|
|
||||||
JTabbedPane tabPane = (JTabbedPane) c;
|
|
||||||
int tabCount = tabPane.getTabCount();
|
|
||||||
for( int i = 0; i < tabCount; i++ ) {
|
|
||||||
if( tabPane.getDisplayedMnemonicIndexAt( i ) >= 0 )
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isShowMnemonics() {
|
||||||
|
return MnemonicHandler.isShowMnemonics();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void showMnemonics( Component c ) {
|
||||||
|
MnemonicHandler.showMnemonics( true, c );
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void hideMnemonics() {
|
||||||
|
MnemonicHandler.showMnemonics( false, null );
|
||||||
|
}
|
||||||
|
|
||||||
|
//---- class ActiveFont ---------------------------------------------------
|
||||||
|
|
||||||
|
private static class ActiveFont
|
||||||
|
implements ActiveValue
|
||||||
|
{
|
||||||
|
private final float scaleFactor;
|
||||||
|
|
||||||
|
// cache (scaled) font
|
||||||
|
private Font font;
|
||||||
|
private Font lastDefaultFont;
|
||||||
|
|
||||||
|
ActiveFont( float scaleFactor ) {
|
||||||
|
this.scaleFactor = scaleFactor;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
@Override
|
||||||
|
public Object createValue( UIDefaults table ) {
|
||||||
|
Font defaultFont = UIManager.getFont( "defaultFont" );
|
||||||
|
|
||||||
|
if( lastDefaultFont != defaultFont ) {
|
||||||
|
lastDefaultFont = defaultFont;
|
||||||
|
|
||||||
|
if( scaleFactor != 1 ) {
|
||||||
|
// scale font
|
||||||
|
int newFontSize = Math.round( defaultFont.getSize() * scaleFactor );
|
||||||
|
font = new FontUIResource( defaultFont.deriveFont( (float) newFontSize ) );
|
||||||
|
} else {
|
||||||
|
// make sure that font is a UIResource for LaF switching
|
||||||
|
font = (defaultFont instanceof UIResource)
|
||||||
|
? defaultFont
|
||||||
|
: new FontUIResource( defaultFont );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return font;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//---- class ImageIconUIResource ------------------------------------------
|
||||||
|
|
||||||
|
private static class ImageIconUIResource
|
||||||
|
extends ImageIcon
|
||||||
|
implements UIResource
|
||||||
|
{
|
||||||
|
ImageIconUIResource( Image image ) {
|
||||||
|
super( image );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,12 +32,12 @@ public class FlatLightLaf
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getName() {
|
public String getName() {
|
||||||
return "Flat Light";
|
return "FlatLaf Light";
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getDescription() {
|
public String getDescription() {
|
||||||
return "Flat Light Look and Feel";
|
return "FlatLaf Light Look and Feel";
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -0,0 +1,122 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2020 FormDev Software GmbH
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.formdev.flatlaf;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Properties;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A Flat LaF that is able to load UI defaults from properties passed to the constructor.
|
||||||
|
* <p>
|
||||||
|
* Specify the base theme in the properties with {@code @baseTheme=<baseTheme>}.
|
||||||
|
* Allowed values for {@code <baseTheme>} are {@code light} (the default), {@code dark},
|
||||||
|
* {@code intellij} or {@code darcula}.
|
||||||
|
* <p>
|
||||||
|
* The properties are applied after loading the base theme and may overwrite base properties.
|
||||||
|
* All features of FlatLaf properties files are available.
|
||||||
|
*
|
||||||
|
* @author Karl Tauber
|
||||||
|
*/
|
||||||
|
public class FlatPropertiesLaf
|
||||||
|
extends FlatLaf
|
||||||
|
{
|
||||||
|
private final String name;
|
||||||
|
private final String baseTheme;
|
||||||
|
private final boolean dark;
|
||||||
|
private final Properties properties;
|
||||||
|
|
||||||
|
public FlatPropertiesLaf( String name, File propertiesFile )
|
||||||
|
throws IOException
|
||||||
|
{
|
||||||
|
this( name, new FileInputStream( propertiesFile ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
public FlatPropertiesLaf( String name, InputStream in )
|
||||||
|
throws IOException
|
||||||
|
{
|
||||||
|
this( name, loadProperties( in ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Properties loadProperties( InputStream in )
|
||||||
|
throws IOException
|
||||||
|
{
|
||||||
|
Properties properties = new Properties();
|
||||||
|
try( InputStream in2 = in ) {
|
||||||
|
properties.load( in2 );
|
||||||
|
}
|
||||||
|
return properties;
|
||||||
|
}
|
||||||
|
|
||||||
|
public FlatPropertiesLaf( String name, Properties properties ) {
|
||||||
|
this.name = name;
|
||||||
|
this.properties = properties;
|
||||||
|
|
||||||
|
baseTheme = properties.getProperty( "@baseTheme", "light" );
|
||||||
|
dark = "dark".equalsIgnoreCase( baseTheme ) || "darcula".equalsIgnoreCase( baseTheme );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getDescription() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isDark() {
|
||||||
|
return dark;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ArrayList<Class<?>> getLafClassesForDefaultsLoading() {
|
||||||
|
ArrayList<Class<?>> lafClasses = new ArrayList<>();
|
||||||
|
lafClasses.add( FlatLaf.class );
|
||||||
|
switch( baseTheme.toLowerCase() ) {
|
||||||
|
default:
|
||||||
|
case "light":
|
||||||
|
lafClasses.add( FlatLightLaf.class );
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "dark":
|
||||||
|
lafClasses.add( FlatDarkLaf.class );
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "intellij":
|
||||||
|
lafClasses.add( FlatLightLaf.class );
|
||||||
|
lafClasses.add( FlatIntelliJLaf.class );
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "darcula":
|
||||||
|
lafClasses.add( FlatDarkLaf.class );
|
||||||
|
lafClasses.add( FlatDarculaLaf.class );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return lafClasses;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Properties getAdditionalDefaults() {
|
||||||
|
return properties;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -29,6 +29,7 @@ import java.util.HashSet;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.logging.Level;
|
||||||
import javax.swing.UIDefaults;
|
import javax.swing.UIDefaults;
|
||||||
import javax.swing.plaf.ColorUIResource;
|
import javax.swing.plaf.ColorUIResource;
|
||||||
import com.formdev.flatlaf.json.Json;
|
import com.formdev.flatlaf.json.Json;
|
||||||
@@ -72,8 +73,7 @@ public class IntelliJTheme
|
|||||||
try {
|
try {
|
||||||
return FlatLaf.install( createLaf( in ) );
|
return FlatLaf.install( createLaf( in ) );
|
||||||
} catch( Exception ex ) {
|
} catch( Exception ex ) {
|
||||||
System.err.println( "Failed to load IntelliJ theme" );
|
FlatLaf.LOG.log( Level.SEVERE, "FlatLaf: Failed to load IntelliJ theme", ex );
|
||||||
ex.printStackTrace();
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -86,7 +86,7 @@ public class IntelliJTheme
|
|||||||
* Using a buffered input stream is not necessary.
|
* Using a buffered input stream is not necessary.
|
||||||
*/
|
*/
|
||||||
public static FlatLaf createLaf( InputStream in )
|
public static FlatLaf createLaf( InputStream in )
|
||||||
throws IOException, ParseException
|
throws IOException
|
||||||
{
|
{
|
||||||
return createLaf( new IntelliJTheme( in ) );
|
return createLaf( new IntelliJTheme( in ) );
|
||||||
}
|
}
|
||||||
@@ -106,11 +106,13 @@ public class IntelliJTheme
|
|||||||
*/
|
*/
|
||||||
@SuppressWarnings( "unchecked" )
|
@SuppressWarnings( "unchecked" )
|
||||||
public IntelliJTheme( InputStream in )
|
public IntelliJTheme( InputStream in )
|
||||||
throws IOException, ParseException
|
throws IOException
|
||||||
{
|
{
|
||||||
Map<String, Object> json;
|
Map<String, Object> json;
|
||||||
try( Reader reader = new InputStreamReader( in, StandardCharsets.UTF_8 ) ) {
|
try( Reader reader = new InputStreamReader( in, StandardCharsets.UTF_8 ) ) {
|
||||||
json = (Map<String, Object>) Json.parse( reader );
|
json = (Map<String, Object>) Json.parse( reader );
|
||||||
|
} catch( ParseException ex ) {
|
||||||
|
throw new IOException( ex.getMessage(), ex );
|
||||||
}
|
}
|
||||||
|
|
||||||
name = (String) json.get( "name" );
|
name = (String) json.get( "name" );
|
||||||
@@ -132,6 +134,8 @@ public class IntelliJTheme
|
|||||||
defaults.put( "Button.paintShadow", true );
|
defaults.put( "Button.paintShadow", true );
|
||||||
defaults.put( "Button.shadowWidth", dark ? 2 : 1 );
|
defaults.put( "Button.shadowWidth", dark ? 2 : 1 );
|
||||||
|
|
||||||
|
Map<Object, Object> themeSpecificDefaults = removeThemeSpecificDefaults( defaults );
|
||||||
|
|
||||||
loadNamedColors( defaults );
|
loadNamedColors( defaults );
|
||||||
|
|
||||||
// convert Json "ui" structure to UI defaults
|
// convert Json "ui" structure to UI defaults
|
||||||
@@ -183,6 +187,42 @@ public class IntelliJTheme
|
|||||||
if( !uiKeys.contains( "Spinner.background" ) )
|
if( !uiKeys.contains( "Spinner.background" ) )
|
||||||
defaults.put( "Spinner.background", textFieldBackground );
|
defaults.put( "Spinner.background", textFieldBackground );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// fix ToggleButton
|
||||||
|
if( !uiKeys.contains( "ToggleButton.startBackground" ) && !uiKeys.contains( "*.startBackground" ) )
|
||||||
|
defaults.put( "ToggleButton.startBackground", defaults.get( "Button.startBackground" ) );
|
||||||
|
if( !uiKeys.contains( "ToggleButton.endBackground" ) && !uiKeys.contains( "*.endBackground" ) )
|
||||||
|
defaults.put( "ToggleButton.endBackground", defaults.get( "Button.endBackground" ) );
|
||||||
|
if( !uiKeys.contains( "ToggleButton.foreground" ) && uiKeys.contains( "Button.foreground" ) )
|
||||||
|
defaults.put( "ToggleButton.foreground", defaults.get( "Button.foreground" ) );
|
||||||
|
|
||||||
|
// limit tree row height
|
||||||
|
int rowHeight = defaults.getInt( "Tree.rowHeight" );
|
||||||
|
if( rowHeight > 22 )
|
||||||
|
defaults.put( "Tree.rowHeight", 22 );
|
||||||
|
|
||||||
|
// apply theme specific UI defaults at the end to allow overwriting
|
||||||
|
defaults.putAll( themeSpecificDefaults );
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map<Object, Object> removeThemeSpecificDefaults( UIDefaults defaults ) {
|
||||||
|
// search for theme specific UI defaults keys
|
||||||
|
ArrayList<String> themeSpecificKeys = new ArrayList<>();
|
||||||
|
for( Object key : defaults.keySet() ) {
|
||||||
|
if( key instanceof String && ((String)key).startsWith( "[" ) )
|
||||||
|
themeSpecificKeys.add( (String) key );
|
||||||
|
}
|
||||||
|
|
||||||
|
// remove theme specific UI defaults and remember only those for current theme
|
||||||
|
Map<Object, Object> themeSpecificDefaults = new HashMap<>();
|
||||||
|
String currentThemePrefix = '[' + name.replace( ' ', '_' ) + ']';
|
||||||
|
for( String key : themeSpecificKeys ) {
|
||||||
|
Object value = defaults.remove( key );
|
||||||
|
if( key.startsWith( currentThemePrefix ) )
|
||||||
|
themeSpecificDefaults.put( key.substring( currentThemePrefix.length() ), value );
|
||||||
|
}
|
||||||
|
|
||||||
|
return themeSpecificDefaults;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -200,7 +240,7 @@ public class IntelliJTheme
|
|||||||
if( color != null ) {
|
if( color != null ) {
|
||||||
String key = e.getKey();
|
String key = e.getKey();
|
||||||
namedColors.put( key, color );
|
namedColors.put( key, color );
|
||||||
defaults.put( "ColorPalette." + e.getKey(), color );
|
defaults.put( "ColorPalette." + key, color );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -216,6 +256,11 @@ public class IntelliJTheme
|
|||||||
} else {
|
} else {
|
||||||
uiKeys.add( key );
|
uiKeys.add( key );
|
||||||
|
|
||||||
|
// fix ComboBox size and Spinner border in all Material UI Lite themes
|
||||||
|
boolean isMaterialUILite = author.equals( "Mallowigi" );
|
||||||
|
if( isMaterialUILite && (key.equals( "ComboBox.padding" ) || key.equals( "Spinner.border" )) )
|
||||||
|
return; // ignore
|
||||||
|
|
||||||
// map keys
|
// map keys
|
||||||
key = uiKeyMapping.getOrDefault( key, key );
|
key = uiKeyMapping.getOrDefault( key, key );
|
||||||
if( key.isEmpty() )
|
if( key.isEmpty() )
|
||||||
@@ -245,7 +290,7 @@ public class IntelliJTheme
|
|||||||
try {
|
try {
|
||||||
uiValue = UIDefaultsLoader.parseValue( key, valueStr );
|
uiValue = UIDefaultsLoader.parseValue( key, valueStr );
|
||||||
} catch( RuntimeException ex ) {
|
} catch( RuntimeException ex ) {
|
||||||
UIDefaultsLoader.logParseError( key, valueStr, ex );
|
UIDefaultsLoader.logParseError( Level.CONFIG, key, valueStr, ex );
|
||||||
return; // ignore invalid value
|
return; // ignore invalid value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -270,7 +315,7 @@ public class IntelliJTheme
|
|||||||
// (e.g. set ComboBox.buttonEditableBackground to *.background
|
// (e.g. set ComboBox.buttonEditableBackground to *.background
|
||||||
// because it is mapped from ComboBox.ArrowButton.background)
|
// because it is mapped from ComboBox.ArrowButton.background)
|
||||||
String km = uiKeyInverseMapping.getOrDefault( k, (String) k );
|
String km = uiKeyInverseMapping.getOrDefault( k, (String) k );
|
||||||
if( km.endsWith( tail ) && !noWildcardReplace.contains( k ) && !((String)k).startsWith( "CheckBox.icon." ) )
|
if( km.endsWith( tail ) && !((String)k).startsWith( "CheckBox.icon." ) )
|
||||||
defaults.put( k, uiValue );
|
defaults.put( k, uiValue );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -355,33 +400,43 @@ public class IntelliJTheme
|
|||||||
value = "#ffffff";
|
value = "#ffffff";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String key2 = checkboxDuplicateColors.get( key );
|
||||||
|
|
||||||
if( dark )
|
if( dark )
|
||||||
key = StringUtils.removeTrailing( key, ".Dark" );
|
key = StringUtils.removeTrailing( key, ".Dark" );
|
||||||
|
|
||||||
String newKey = checkboxKeyMapping.get( key );
|
String newKey = checkboxKeyMapping.get( key );
|
||||||
if( newKey != null ) {
|
if( newKey != null ) {
|
||||||
ColorUIResource color = toColor( (String) value );
|
ColorUIResource color = toColor( (String) value );
|
||||||
if( color != null )
|
if( color != null ) {
|
||||||
defaults.put( newKey, color );
|
defaults.put( newKey, color );
|
||||||
|
|
||||||
|
if( key2 != null ) {
|
||||||
|
// When IDEA replaces colors in SVGs it uses color values and not the keys
|
||||||
|
// from com.intellij.ide.ui.UITheme.colorPalette, but there are some keys that
|
||||||
|
// have same color value:
|
||||||
|
// - Checkbox.Background.Default.Dark has same color as Checkbox.Background.Selected.Dark
|
||||||
|
// - Checkbox.Border.Default.Dark has same color as Checkbox.Border.Selected.Dark
|
||||||
|
// - Checkbox.Focus.Thin.Default.Dark has same color as Checkbox.Focus.Thin.Selected.Dark
|
||||||
|
//
|
||||||
|
// So if only e.g. Checkbox.Background.Default.Dark is specified in .theme.json,
|
||||||
|
// then this color is also used for Checkbox.Background.Selected.Dark.
|
||||||
|
//
|
||||||
|
// If Checkbox.Background.Default.Dark and Checkbox.Background.Selected.Dark
|
||||||
|
// are specified in .theme.json, then the later specified is used for both.
|
||||||
|
if( dark )
|
||||||
|
key2 = StringUtils.removeTrailing( key2, ".Dark" );
|
||||||
|
|
||||||
|
String newKey2 = checkboxKeyMapping.get( key2 );
|
||||||
|
if( newKey2 != null )
|
||||||
|
defaults.put( newKey2, color );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
checkboxModified = true;
|
checkboxModified = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// When IDEA replaces colors in SVGs it uses color values and not the keys
|
|
||||||
// from com.intellij.ide.ui.UITheme.colorPalette, but there are some keys that
|
|
||||||
// have same color value:
|
|
||||||
// - Checkbox.Background.Default.Dark has same color as Checkbox.Background.Selected.Dark
|
|
||||||
// - Checkbox.Border.Default.Dark has same color as Checkbox.Border.Selected.Dark
|
|
||||||
// - Checkbox.Focus.Thin.Default.Dark has same color as Checkbox.Focus.Thin.Selected.Dark
|
|
||||||
//
|
|
||||||
// So if only e.g. Checkbox.Background.Default.Dark is specified in .theme.json,
|
|
||||||
// then this color is also used for Checkbox.Background.Selected.Dark.
|
|
||||||
// Occurs e.g. in "Dark purple" theme.
|
|
||||||
fixCheckBoxColor( defaults, colorPalette, "Checkbox.Background.Default.Dark", "Checkbox.Background.Selected.Dark" );
|
|
||||||
fixCheckBoxColor( defaults, colorPalette, "Checkbox.Border.Default.Dark", "Checkbox.Border.Selected.Dark" );
|
|
||||||
fixCheckBoxColor( defaults, colorPalette, "Checkbox.Focus.Thin.Default.Dark", "Checkbox.Focus.Thin.Selected.Dark" );
|
|
||||||
|
|
||||||
// remove hover and pressed colors
|
// remove hover and pressed colors
|
||||||
if( checkboxModified ) {
|
if( checkboxModified ) {
|
||||||
defaults.remove( "CheckBox.icon.hoverBorderColor" );
|
defaults.remove( "CheckBox.icon.hoverBorderColor" );
|
||||||
@@ -391,24 +446,17 @@ public class IntelliJTheme
|
|||||||
defaults.remove( "CheckBox.icon.selectedHoverBackground" );
|
defaults.remove( "CheckBox.icon.selectedHoverBackground" );
|
||||||
defaults.remove( "CheckBox.icon.selectedPressedBackground" );
|
defaults.remove( "CheckBox.icon.selectedPressedBackground" );
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private void fixCheckBoxColor( UIDefaults defaults, Map<String, Object> colorPalette, String key1, String key2 ) {
|
// copy values
|
||||||
if( colorPalette.containsKey( key1 ) == colorPalette.containsKey( key2 ) )
|
for( Map.Entry<String, String> e : uiKeyCopying.entrySet() )
|
||||||
return;
|
defaults.put( e.getKey(), defaults.get( e.getValue() ) );
|
||||||
|
|
||||||
String newKey1 = checkboxKeyMapping.get( StringUtils.removeTrailing( key1, ".Dark" ) );
|
|
||||||
String newKey2 = checkboxKeyMapping.get( StringUtils.removeTrailing( key2, ".Dark" ) );
|
|
||||||
if( colorPalette.containsKey( key1 ) )
|
|
||||||
defaults.put( newKey2, defaults.get( newKey1 ) );
|
|
||||||
else
|
|
||||||
defaults.put( newKey1, defaults.get( newKey2 ) );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Map<String, String> uiKeyMapping = new HashMap<>();
|
private static Map<String, String> uiKeyMapping = new HashMap<>();
|
||||||
|
private static Map<String, String> uiKeyCopying = new HashMap<>();
|
||||||
private static Map<String, String> uiKeyInverseMapping = new HashMap<>();
|
private static Map<String, String> uiKeyInverseMapping = new HashMap<>();
|
||||||
private static Map<String, String> checkboxKeyMapping = new HashMap<>();
|
private static Map<String, String> checkboxKeyMapping = new HashMap<>();
|
||||||
private static Set<String> noWildcardReplace = new HashSet<>();
|
private static Map<String, String> checkboxDuplicateColors = new HashMap<>();
|
||||||
|
|
||||||
static {
|
static {
|
||||||
// ComboBox
|
// ComboBox
|
||||||
@@ -419,6 +467,15 @@ public class IntelliJTheme
|
|||||||
uiKeyMapping.put( "ComboBox.ArrowButton.iconColor", "ComboBox.buttonArrowColor" );
|
uiKeyMapping.put( "ComboBox.ArrowButton.iconColor", "ComboBox.buttonArrowColor" );
|
||||||
uiKeyMapping.put( "ComboBox.ArrowButton.nonEditableBackground", "ComboBox.buttonBackground" );
|
uiKeyMapping.put( "ComboBox.ArrowButton.nonEditableBackground", "ComboBox.buttonBackground" );
|
||||||
|
|
||||||
|
// Component
|
||||||
|
uiKeyMapping.put( "Component.inactiveErrorFocusColor", "Component.error.borderColor" );
|
||||||
|
uiKeyMapping.put( "Component.errorFocusColor", "Component.error.focusedBorderColor" );
|
||||||
|
uiKeyMapping.put( "Component.inactiveWarningFocusColor", "Component.warning.borderColor" );
|
||||||
|
uiKeyMapping.put( "Component.warningFocusColor", "Component.warning.focusedBorderColor" );
|
||||||
|
|
||||||
|
// Link
|
||||||
|
uiKeyMapping.put( "Link.activeForeground", "Component.linkColor" );
|
||||||
|
|
||||||
// ProgressBar
|
// ProgressBar
|
||||||
uiKeyMapping.put( "ProgressBar.background", "" ); // ignore
|
uiKeyMapping.put( "ProgressBar.background", "" ); // ignore
|
||||||
uiKeyMapping.put( "ProgressBar.foreground", "" ); // ignore
|
uiKeyMapping.put( "ProgressBar.foreground", "" ); // ignore
|
||||||
@@ -429,12 +486,21 @@ public class IntelliJTheme
|
|||||||
uiKeyMapping.put( "ScrollBar.trackColor", "ScrollBar.track" );
|
uiKeyMapping.put( "ScrollBar.trackColor", "ScrollBar.track" );
|
||||||
uiKeyMapping.put( "ScrollBar.thumbColor", "ScrollBar.thumb" );
|
uiKeyMapping.put( "ScrollBar.thumbColor", "ScrollBar.thumb" );
|
||||||
|
|
||||||
|
// Separator
|
||||||
|
uiKeyMapping.put( "Separator.separatorColor", "Separator.foreground" );
|
||||||
|
|
||||||
// Slider
|
// Slider
|
||||||
uiKeyMapping.put( "Slider.trackWidth", "" ); // ignore (used in Material Theme UI Lite)
|
uiKeyMapping.put( "Slider.trackWidth", "" ); // ignore (used in Material Theme UI Lite)
|
||||||
|
|
||||||
for( Map.Entry<String, String> e : uiKeyMapping.entrySet() )
|
for( Map.Entry<String, String> e : uiKeyMapping.entrySet() )
|
||||||
uiKeyInverseMapping.put( e.getValue(), e.getKey() );
|
uiKeyInverseMapping.put( e.getValue(), e.getKey() );
|
||||||
|
|
||||||
|
uiKeyCopying.put( "ToggleButton.tab.underlineColor", "TabbedPane.underlineColor" );
|
||||||
|
uiKeyCopying.put( "ToggleButton.tab.disabledUnderlineColor", "TabbedPane.disabledUnderlineColor" );
|
||||||
|
uiKeyCopying.put( "ToggleButton.tab.selectedBackground", "TabbedPane.selectedBackground" );
|
||||||
|
uiKeyCopying.put( "ToggleButton.tab.hoverBackground", "TabbedPane.hoverColor" );
|
||||||
|
uiKeyCopying.put( "ToggleButton.tab.focusBackground", "TabbedPane.focusColor" );
|
||||||
|
|
||||||
checkboxKeyMapping.put( "Checkbox.Background.Default", "CheckBox.icon.background" );
|
checkboxKeyMapping.put( "Checkbox.Background.Default", "CheckBox.icon.background" );
|
||||||
checkboxKeyMapping.put( "Checkbox.Background.Disabled", "CheckBox.icon.disabledBackground" );
|
checkboxKeyMapping.put( "Checkbox.Background.Disabled", "CheckBox.icon.disabledBackground" );
|
||||||
checkboxKeyMapping.put( "Checkbox.Border.Default", "CheckBox.icon.borderColor" );
|
checkboxKeyMapping.put( "Checkbox.Border.Default", "CheckBox.icon.borderColor" );
|
||||||
@@ -447,15 +513,13 @@ public class IntelliJTheme
|
|||||||
checkboxKeyMapping.put( "Checkbox.Foreground.Selected", "CheckBox.icon.checkmarkColor" );
|
checkboxKeyMapping.put( "Checkbox.Foreground.Selected", "CheckBox.icon.checkmarkColor" );
|
||||||
checkboxKeyMapping.put( "Checkbox.Focus.Thin.Selected", "CheckBox.icon.selectedFocusedBorderColor" );
|
checkboxKeyMapping.put( "Checkbox.Focus.Thin.Selected", "CheckBox.icon.selectedFocusedBorderColor" );
|
||||||
|
|
||||||
// because FlatLaf uses Button.background and Button.borderColor,
|
checkboxDuplicateColors.put( "Checkbox.Background.Default.Dark", "Checkbox.Background.Selected.Dark" );
|
||||||
// but IDEA uses Button.startBackground and Button.startBorderColor,
|
checkboxDuplicateColors.put( "Checkbox.Border.Default.Dark", "Checkbox.Border.Selected.Dark" );
|
||||||
// our default button background and border colors may be replaced by
|
checkboxDuplicateColors.put( "Checkbox.Focus.Thin.Default.Dark", "Checkbox.Focus.Thin.Selected.Dark" );
|
||||||
// wildcard *.background and *.borderColor colors
|
@SuppressWarnings( "unchecked" )
|
||||||
noWildcardReplace.add( "Button.background" );
|
Map.Entry<String, String>[] entries = checkboxDuplicateColors.entrySet().toArray( new Map.Entry[checkboxDuplicateColors.size()] );
|
||||||
noWildcardReplace.add( "Button.borderColor" );
|
for( Map.Entry<String, String> e : entries )
|
||||||
noWildcardReplace.add( "Button.default.background" );
|
checkboxDuplicateColors.put( e.getValue(), e.getKey() );
|
||||||
noWildcardReplace.add( "Button.default.borderColor" );
|
|
||||||
noWildcardReplace.add( "ToggleButton.background" );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//---- class ThemeLaf -----------------------------------------------------
|
//---- class ThemeLaf -----------------------------------------------------
|
||||||
@@ -489,14 +553,12 @@ public class IntelliJTheme
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public UIDefaults getDefaults() {
|
void applyAdditionalDefaults( UIDefaults defaults ) {
|
||||||
UIDefaults defaults = super.getDefaults();
|
|
||||||
theme.applyProperties( defaults );
|
theme.applyProperties( defaults );
|
||||||
return defaults;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
ArrayList<Class<?>> getLafClassesForDefaultsLoading() {
|
protected ArrayList<Class<?>> getLafClassesForDefaultsLoading() {
|
||||||
ArrayList<Class<?>> lafClasses = new ArrayList<>();
|
ArrayList<Class<?>> lafClasses = new ArrayList<>();
|
||||||
lafClasses.add( FlatLaf.class );
|
lafClasses.add( FlatLaf.class );
|
||||||
lafClasses.add( theme.dark ? FlatDarkLaf.class : FlatLightLaf.class );
|
lafClasses.add( theme.dark ? FlatDarkLaf.class : FlatLightLaf.class );
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
package com.formdev.flatlaf;
|
package com.formdev.flatlaf;
|
||||||
|
|
||||||
import java.awt.Font;
|
import java.awt.Font;
|
||||||
|
import java.awt.GraphicsConfiguration;
|
||||||
import java.awt.GraphicsEnvironment;
|
import java.awt.GraphicsEnvironment;
|
||||||
import java.awt.Toolkit;
|
import java.awt.Toolkit;
|
||||||
import java.io.BufferedReader;
|
import java.io.BufferedReader;
|
||||||
@@ -27,9 +28,10 @@ import java.util.ArrayList;
|
|||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.StringTokenizer;
|
import java.util.StringTokenizer;
|
||||||
import javax.swing.text.StyleContext;
|
import java.util.logging.Level;
|
||||||
import com.formdev.flatlaf.util.StringUtils;
|
import com.formdev.flatlaf.util.StringUtils;
|
||||||
import com.formdev.flatlaf.util.SystemInfo;
|
import com.formdev.flatlaf.util.SystemInfo;
|
||||||
|
import com.formdev.flatlaf.util.UIScale;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Karl Tauber
|
* @author Karl Tauber
|
||||||
@@ -72,6 +74,13 @@ class LinuxFontPolicy
|
|||||||
family = family.isEmpty() ? word : (family + ' ' + word);
|
family = family.isEmpty() ? word : (family + ' ' + word);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Ubuntu font is rendered poorly (except if running in JetBrains VM)
|
||||||
|
// --> use Liberation Sans font
|
||||||
|
if( family.startsWith( "Ubuntu" ) &&
|
||||||
|
!SystemInfo.IS_JETBRAINS_JVM &&
|
||||||
|
!Boolean.parseBoolean( System.getProperty( "flatlaf.useUbuntuFont" ) ) )
|
||||||
|
family = "Liberation Sans";
|
||||||
|
|
||||||
// scale font size
|
// scale font size
|
||||||
double dsize = size * getGnomeFontScale();
|
double dsize = size * getGnomeFontScale();
|
||||||
size = (int) (dsize + 0.5);
|
size = (int) (dsize + 0.5);
|
||||||
@@ -87,9 +96,7 @@ class LinuxFontPolicy
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static Font createFont( String family, int style, int size, double dsize ) {
|
private static Font createFont( String family, int style, int size, double dsize ) {
|
||||||
// using StyleContext.getFont() here because it uses
|
Font font = FlatLaf.createCompositeFont( family, style, size );
|
||||||
// sun.font.FontUtilities.getCompositeFontUIResource()
|
|
||||||
Font font = new StyleContext().getFont( family, style, size );
|
|
||||||
|
|
||||||
// set font size in floating points
|
// set font size in floating points
|
||||||
font = font.deriveFont( style, (float) dsize );
|
font = font.deriveFont( style, (float) dsize );
|
||||||
@@ -98,6 +105,10 @@ class LinuxFontPolicy
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static double getGnomeFontScale() {
|
private static double getGnomeFontScale() {
|
||||||
|
// do not scale font here if JRE scales
|
||||||
|
if( isSystemScaling() )
|
||||||
|
return 96. / 72.;
|
||||||
|
|
||||||
// see class com.sun.java.swing.plaf.gtk.PangoFonts background information
|
// see class com.sun.java.swing.plaf.gtk.PangoFonts background information
|
||||||
|
|
||||||
Object value = Toolkit.getDefaultToolkit().getDesktopProperty( "gnome.Xft/DPI" );
|
Object value = Toolkit.getDefaultToolkit().getDesktopProperty( "gnome.Xft/DPI" );
|
||||||
@@ -161,13 +172,13 @@ class LinuxFontPolicy
|
|||||||
if( "1".equals( strs.get( 5 ) ) )
|
if( "1".equals( strs.get( 5 ) ) )
|
||||||
style |= Font.ITALIC;
|
style |= Font.ITALIC;
|
||||||
} catch( RuntimeException ex ) {
|
} catch( RuntimeException ex ) {
|
||||||
ex.printStackTrace();
|
FlatLaf.LOG.log( Level.CONFIG, "FlatLaf: Failed to parse 'font=" + generalFont + "'.", ex );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// font dpi
|
// font dpi
|
||||||
int dpi = 96;
|
int dpi = 96;
|
||||||
if( forceFontDPI != null ) {
|
if( forceFontDPI != null && !isSystemScaling() ) {
|
||||||
try {
|
try {
|
||||||
dpi = Integer.parseInt( forceFontDPI );
|
dpi = Integer.parseInt( forceFontDPI );
|
||||||
if( dpi <= 0 )
|
if( dpi <= 0 )
|
||||||
@@ -175,7 +186,7 @@ class LinuxFontPolicy
|
|||||||
if( dpi < 50 )
|
if( dpi < 50 )
|
||||||
dpi = 50;
|
dpi = 50;
|
||||||
} catch( NumberFormatException ex ) {
|
} catch( NumberFormatException ex ) {
|
||||||
ex.printStackTrace();
|
FlatLaf.LOG.log( Level.CONFIG, "FlatLaf: Failed to parse 'forceFontDPI=" + forceFontDPI + "'.", ex );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -214,7 +225,7 @@ class LinuxFontPolicy
|
|||||||
while( (line = reader.readLine()) != null )
|
while( (line = reader.readLine()) != null )
|
||||||
lines.add( line );
|
lines.add( line );
|
||||||
} catch( IOException ex ) {
|
} catch( IOException ex ) {
|
||||||
ex.printStackTrace();
|
FlatLaf.LOG.log( Level.CONFIG, "FlatLaf: Failed to read '" + filename + "'.", ex );
|
||||||
}
|
}
|
||||||
return lines;
|
return lines;
|
||||||
}
|
}
|
||||||
@@ -246,4 +257,15 @@ class LinuxFontPolicy
|
|||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the JRE scales, which is the case if:
|
||||||
|
* - environment variable GDK_SCALE is set and running on Java 9 or later
|
||||||
|
* - running on JetBrains Runtime 11 or later and scaling is enabled in system Settings
|
||||||
|
*/
|
||||||
|
private static boolean isSystemScaling() {
|
||||||
|
GraphicsConfiguration gc = GraphicsEnvironment.getLocalGraphicsEnvironment()
|
||||||
|
.getDefaultScreenDevice().getDefaultConfiguration();
|
||||||
|
return UIScale.getSystemScaleFactor( gc ) > 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,259 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2020 FormDev Software GmbH
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.formdev.flatlaf;
|
||||||
|
|
||||||
|
import java.awt.Component;
|
||||||
|
import java.awt.Container;
|
||||||
|
import java.awt.EventQueue;
|
||||||
|
import java.awt.KeyEventPostProcessor;
|
||||||
|
import java.awt.KeyboardFocusManager;
|
||||||
|
import java.awt.Window;
|
||||||
|
import java.awt.event.KeyEvent;
|
||||||
|
import java.awt.event.WindowAdapter;
|
||||||
|
import java.awt.event.WindowEvent;
|
||||||
|
import java.awt.event.WindowListener;
|
||||||
|
import java.lang.ref.WeakReference;
|
||||||
|
import javax.swing.AbstractButton;
|
||||||
|
import javax.swing.JFrame;
|
||||||
|
import javax.swing.JLabel;
|
||||||
|
import javax.swing.JMenu;
|
||||||
|
import javax.swing.JMenuBar;
|
||||||
|
import javax.swing.JRootPane;
|
||||||
|
import javax.swing.JTabbedPane;
|
||||||
|
import javax.swing.MenuElement;
|
||||||
|
import javax.swing.MenuSelectionManager;
|
||||||
|
import javax.swing.SwingUtilities;
|
||||||
|
import javax.swing.UIManager;
|
||||||
|
import javax.swing.event.ChangeEvent;
|
||||||
|
import javax.swing.event.ChangeListener;
|
||||||
|
import com.formdev.flatlaf.util.SystemInfo;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show/hide mnemonics.
|
||||||
|
*
|
||||||
|
* @author Karl Tauber
|
||||||
|
*/
|
||||||
|
class MnemonicHandler
|
||||||
|
implements KeyEventPostProcessor, ChangeListener
|
||||||
|
{
|
||||||
|
private static boolean showMnemonics;
|
||||||
|
private static WeakReference<Window> lastShowMnemonicWindow;
|
||||||
|
private static WindowListener windowListener;
|
||||||
|
|
||||||
|
static boolean isShowMnemonics() {
|
||||||
|
return showMnemonics || !UIManager.getBoolean( "Component.hideMnemonics" );
|
||||||
|
}
|
||||||
|
|
||||||
|
void install() {
|
||||||
|
KeyboardFocusManager.getCurrentKeyboardFocusManager().addKeyEventPostProcessor( this );
|
||||||
|
MenuSelectionManager.defaultManager().addChangeListener( this );
|
||||||
|
}
|
||||||
|
|
||||||
|
void uninstall() {
|
||||||
|
KeyboardFocusManager.getCurrentKeyboardFocusManager().removeKeyEventPostProcessor( this );
|
||||||
|
MenuSelectionManager.defaultManager().removeChangeListener( this );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean postProcessKeyEvent( KeyEvent e ) {
|
||||||
|
int keyCode = e.getKeyCode();
|
||||||
|
if( SystemInfo.IS_MAC ) {
|
||||||
|
// Ctrl+Alt keys must be pressed on Mac
|
||||||
|
if( keyCode == KeyEvent.VK_CONTROL || keyCode == KeyEvent.VK_ALT )
|
||||||
|
showMnemonics( shouldShowMnemonics( e ) && e.isControlDown() && e.isAltDown(), e.getComponent() );
|
||||||
|
} else {
|
||||||
|
// Alt key must be pressed on Windows and Linux
|
||||||
|
if( SystemInfo.IS_WINDOWS )
|
||||||
|
return processKeyEventOnWindows( e );
|
||||||
|
|
||||||
|
if( keyCode == KeyEvent.VK_ALT )
|
||||||
|
showMnemonics( shouldShowMnemonics( e ), e.getComponent() );
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean shouldShowMnemonics( KeyEvent e ) {
|
||||||
|
return e.getID() == KeyEvent.KEY_PRESSED ||
|
||||||
|
MenuSelectionManager.defaultManager().getSelectedPath().length > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int altPressedEventCount;
|
||||||
|
private static boolean selectMenuOnAltReleased;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Special Alt key behavior on Windows.
|
||||||
|
*
|
||||||
|
* Press-and-release Alt key selects first menu (if available) and moves focus
|
||||||
|
* temporary to menu bar. If menu bar has focus (some menu is selected),
|
||||||
|
* pressing Alt key unselects menu and moves focus back to permanent focus owner.
|
||||||
|
*/
|
||||||
|
private boolean processKeyEventOnWindows( KeyEvent e ) {
|
||||||
|
if( e.getKeyCode() != KeyEvent.VK_ALT ) {
|
||||||
|
selectMenuOnAltReleased = false;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( e.getID() == KeyEvent.KEY_PRESSED ) {
|
||||||
|
altPressedEventCount++;
|
||||||
|
|
||||||
|
if( altPressedEventCount == 1 && !e.isConsumed() ) {
|
||||||
|
MenuSelectionManager menuSelectionManager = MenuSelectionManager.defaultManager();
|
||||||
|
selectMenuOnAltReleased = (menuSelectionManager.getSelectedPath().length == 0);
|
||||||
|
|
||||||
|
// if menu is selected when Alt key is pressed then clear menu selection
|
||||||
|
if( !selectMenuOnAltReleased )
|
||||||
|
menuSelectionManager.clearSelectedPath();
|
||||||
|
}
|
||||||
|
|
||||||
|
// show mnemonics
|
||||||
|
showMnemonics( shouldShowMnemonics( e ), e.getComponent() );
|
||||||
|
|
||||||
|
// avoid that the system menu of the window gets focus
|
||||||
|
e.consume();
|
||||||
|
return true;
|
||||||
|
|
||||||
|
} else if( e.getID() == KeyEvent.KEY_RELEASED ) {
|
||||||
|
altPressedEventCount = 0;
|
||||||
|
|
||||||
|
boolean mnemonicsShown = false;
|
||||||
|
if( selectMenuOnAltReleased && !e.isConsumed() ) {
|
||||||
|
MenuSelectionManager menuSelectionManager = MenuSelectionManager.defaultManager();
|
||||||
|
if( menuSelectionManager.getSelectedPath().length == 0 ) {
|
||||||
|
// get menu bar and first menu
|
||||||
|
Component c = e.getComponent();
|
||||||
|
JRootPane rootPane = SwingUtilities.getRootPane( c );
|
||||||
|
Window window = (rootPane != null) ? SwingUtilities.getWindowAncestor( rootPane ) : null;
|
||||||
|
JMenuBar menuBar = (rootPane != null) ? rootPane.getJMenuBar() : null;
|
||||||
|
if( menuBar == null && window instanceof JFrame )
|
||||||
|
menuBar = ((JFrame)window).getJMenuBar();
|
||||||
|
JMenu firstMenu = (menuBar != null) ? menuBar.getMenu( 0 ) : null;
|
||||||
|
|
||||||
|
// select first menu and show mnemonics
|
||||||
|
if( firstMenu != null ) {
|
||||||
|
menuSelectionManager.setSelectedPath( new MenuElement[] { menuBar, firstMenu } );
|
||||||
|
showMnemonics( true, c );
|
||||||
|
mnemonicsShown = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
selectMenuOnAltReleased = false;
|
||||||
|
|
||||||
|
// hide mnemonics
|
||||||
|
if( !mnemonicsShown )
|
||||||
|
showMnemonics( shouldShowMnemonics( e ), e.getComponent() );
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void stateChanged( ChangeEvent e ) {
|
||||||
|
MenuElement[] selectedPath = MenuSelectionManager.defaultManager().getSelectedPath();
|
||||||
|
if( selectedPath.length == 0 && altPressedEventCount == 0 ) {
|
||||||
|
// hide mnemonics when menu selection was canceled
|
||||||
|
showMnemonics( false, null );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void showMnemonics( boolean show, Component c ) {
|
||||||
|
if( show == showMnemonics )
|
||||||
|
return;
|
||||||
|
|
||||||
|
showMnemonics = show;
|
||||||
|
|
||||||
|
// check whether it is necessary to repaint
|
||||||
|
if( !UIManager.getBoolean( "Component.hideMnemonics" ) )
|
||||||
|
return;
|
||||||
|
|
||||||
|
if( show ) {
|
||||||
|
// get root pane
|
||||||
|
JRootPane rootPane = SwingUtilities.getRootPane( c );
|
||||||
|
if( rootPane == null )
|
||||||
|
return;
|
||||||
|
|
||||||
|
// get window
|
||||||
|
Window window = SwingUtilities.getWindowAncestor( rootPane );
|
||||||
|
if( window == null )
|
||||||
|
return;
|
||||||
|
|
||||||
|
// repaint components with mnemonics in focused window
|
||||||
|
repaintMnemonics( window );
|
||||||
|
|
||||||
|
// hide mnemonics if window is deactivated (e.g. Alt+Tab to another window)
|
||||||
|
windowListener = new WindowAdapter() {
|
||||||
|
@Override
|
||||||
|
public void windowDeactivated( WindowEvent e ) {
|
||||||
|
altPressedEventCount = 0;
|
||||||
|
selectMenuOnAltReleased = false;
|
||||||
|
|
||||||
|
// use invokeLater() to avoid that the listener is removed
|
||||||
|
// while the listener queue is iterated to fire this event
|
||||||
|
EventQueue.invokeLater( () -> {
|
||||||
|
showMnemonics( false, null );
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
};
|
||||||
|
window.addWindowListener( windowListener );
|
||||||
|
|
||||||
|
lastShowMnemonicWindow = new WeakReference<>( window );
|
||||||
|
} else if( lastShowMnemonicWindow != null ) {
|
||||||
|
Window window = lastShowMnemonicWindow.get();
|
||||||
|
if( window != null ) {
|
||||||
|
repaintMnemonics( window );
|
||||||
|
|
||||||
|
if( windowListener != null ) {
|
||||||
|
window.removeWindowListener( windowListener );
|
||||||
|
windowListener = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
lastShowMnemonicWindow = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void repaintMnemonics( Container container ) {
|
||||||
|
for( Component c : container.getComponents() ) {
|
||||||
|
if( !c.isVisible() )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if( hasMnemonic( c ) )
|
||||||
|
c.repaint();
|
||||||
|
|
||||||
|
if( c instanceof Container )
|
||||||
|
repaintMnemonics( (Container) c );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean hasMnemonic( Component c ) {
|
||||||
|
if( c instanceof JLabel && ((JLabel)c).getDisplayedMnemonicIndex() >= 0 )
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if( c instanceof AbstractButton && ((AbstractButton)c).getDisplayedMnemonicIndex() >= 0 )
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if( c instanceof JTabbedPane ) {
|
||||||
|
JTabbedPane tabPane = (JTabbedPane) c;
|
||||||
|
int tabCount = tabPane.getTabCount();
|
||||||
|
for( int i = 0; i < tabCount; i++ ) {
|
||||||
|
if( tabPane.getDisplayedMnemonicIndexAt( i ) >= 0 )
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -28,9 +28,10 @@ import java.util.List;
|
|||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
import java.util.ServiceLoader;
|
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
import java.util.logging.Level;
|
||||||
import javax.swing.UIDefaults;
|
import javax.swing.UIDefaults;
|
||||||
|
import javax.swing.UIManager;
|
||||||
import javax.swing.UIDefaults.ActiveValue;
|
import javax.swing.UIDefaults.ActiveValue;
|
||||||
import javax.swing.UIDefaults.LazyValue;
|
import javax.swing.UIDefaults.LazyValue;
|
||||||
import javax.swing.plaf.ColorUIResource;
|
import javax.swing.plaf.ColorUIResource;
|
||||||
@@ -39,7 +40,10 @@ import javax.swing.plaf.InsetsUIResource;
|
|||||||
import com.formdev.flatlaf.ui.FlatEmptyBorder;
|
import com.formdev.flatlaf.ui.FlatEmptyBorder;
|
||||||
import com.formdev.flatlaf.ui.FlatLineBorder;
|
import com.formdev.flatlaf.ui.FlatLineBorder;
|
||||||
import com.formdev.flatlaf.util.ColorFunctions;
|
import com.formdev.flatlaf.util.ColorFunctions;
|
||||||
|
import com.formdev.flatlaf.util.ColorFunctions.ColorFunction;
|
||||||
import com.formdev.flatlaf.util.DerivedColor;
|
import com.formdev.flatlaf.util.DerivedColor;
|
||||||
|
import com.formdev.flatlaf.util.GrayFilter;
|
||||||
|
import com.formdev.flatlaf.util.HSLColor;
|
||||||
import com.formdev.flatlaf.util.StringUtils;
|
import com.formdev.flatlaf.util.StringUtils;
|
||||||
import com.formdev.flatlaf.util.SystemInfo;
|
import com.formdev.flatlaf.util.SystemInfo;
|
||||||
import com.formdev.flatlaf.util.UIScale;
|
import com.formdev.flatlaf.util.UIScale;
|
||||||
@@ -60,11 +64,13 @@ class UIDefaultsLoader
|
|||||||
private static final String TYPE_PREFIX = "{";
|
private static final String TYPE_PREFIX = "{";
|
||||||
private static final String TYPE_PREFIX_END = "}";
|
private static final String TYPE_PREFIX_END = "}";
|
||||||
private static final String VARIABLE_PREFIX = "@";
|
private static final String VARIABLE_PREFIX = "@";
|
||||||
private static final String REF_PREFIX = VARIABLE_PREFIX + "@";
|
private static final String PROPERTY_PREFIX = "$";
|
||||||
private static final String OPTIONAL_PREFIX = "?";
|
private static final String OPTIONAL_PREFIX = "?";
|
||||||
private static final String GLOBAL_PREFIX = "*.";
|
private static final String GLOBAL_PREFIX = "*.";
|
||||||
|
|
||||||
static void loadDefaultsFromProperties( Class<?> lookAndFeelClass, UIDefaults defaults ) {
|
static void loadDefaultsFromProperties( Class<?> lookAndFeelClass, List<FlatDefaultsAddon> addons,
|
||||||
|
Properties additionalDefaults, boolean dark, UIDefaults defaults )
|
||||||
|
{
|
||||||
// determine classes in class hierarchy in reverse order
|
// determine classes in class hierarchy in reverse order
|
||||||
ArrayList<Class<?>> lafClasses = new ArrayList<>();
|
ArrayList<Class<?>> lafClasses = new ArrayList<>();
|
||||||
for( Class<?> lafClass = lookAndFeelClass;
|
for( Class<?> lafClass = lookAndFeelClass;
|
||||||
@@ -74,47 +80,69 @@ class UIDefaultsLoader
|
|||||||
lafClasses.add( 0, lafClass );
|
lafClasses.add( 0, lafClass );
|
||||||
}
|
}
|
||||||
|
|
||||||
loadDefaultsFromProperties( lafClasses, defaults );
|
loadDefaultsFromProperties( lafClasses, addons, additionalDefaults, dark, defaults );
|
||||||
}
|
}
|
||||||
|
|
||||||
static void loadDefaultsFromProperties( List<Class<?>> lafClasses, UIDefaults defaults ) {
|
static void loadDefaultsFromProperties( List<Class<?>> lafClasses, List<FlatDefaultsAddon> addons,
|
||||||
|
Properties additionalDefaults, boolean dark, UIDefaults defaults )
|
||||||
|
{
|
||||||
try {
|
try {
|
||||||
List<ClassLoader> addonClassLoaders = new ArrayList<>();
|
// load core properties files
|
||||||
|
|
||||||
// load properties files
|
|
||||||
Properties properties = new Properties();
|
Properties properties = new Properties();
|
||||||
ServiceLoader<FlatDefaultsAddon> addonLoader = ServiceLoader.load( FlatDefaultsAddon.class );
|
|
||||||
for( Class<?> lafClass : lafClasses ) {
|
for( Class<?> lafClass : lafClasses ) {
|
||||||
// load core properties
|
String propertiesName = '/' + lafClass.getName().replace( '.', '/' ) + ".properties";
|
||||||
String propertiesName = "/" + lafClass.getName().replace( '.', '/' ) + ".properties";
|
|
||||||
try( InputStream in = lafClass.getResourceAsStream( propertiesName ) ) {
|
try( InputStream in = lafClass.getResourceAsStream( propertiesName ) ) {
|
||||||
if( in != null )
|
if( in != null )
|
||||||
properties.load( in );
|
properties.load( in );
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// load properties from addons
|
// load properties from addons
|
||||||
for( FlatDefaultsAddon addon : addonLoader ) {
|
for( FlatDefaultsAddon addon : addons ) {
|
||||||
|
for( Class<?> lafClass : lafClasses ) {
|
||||||
try( InputStream in = addon.getDefaults( lafClass ) ) {
|
try( InputStream in = addon.getDefaults( lafClass ) ) {
|
||||||
if( in != null )
|
if( in != null )
|
||||||
properties.load( in );
|
properties.load( in );
|
||||||
}
|
}
|
||||||
|
|
||||||
ClassLoader addonClassLoader = addon.getClass().getClassLoader();
|
|
||||||
if( !addonClassLoaders.contains( addonClassLoader ) )
|
|
||||||
addonClassLoaders.add( addonClassLoader );
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// collect addon class loaders
|
||||||
|
List<ClassLoader> addonClassLoaders = new ArrayList<>();
|
||||||
|
for( FlatDefaultsAddon addon : addons ) {
|
||||||
|
ClassLoader addonClassLoader = addon.getClass().getClassLoader();
|
||||||
|
if( !addonClassLoaders.contains( addonClassLoader ) )
|
||||||
|
addonClassLoaders.add( addonClassLoader );
|
||||||
|
}
|
||||||
|
|
||||||
|
// add additional defaults
|
||||||
|
if( additionalDefaults != null )
|
||||||
|
properties.putAll( additionalDefaults );
|
||||||
|
|
||||||
// collect all platform specific keys (but do not modify properties)
|
// collect all platform specific keys (but do not modify properties)
|
||||||
ArrayList<String> platformSpecificKeys = new ArrayList<>();
|
ArrayList<String> platformSpecificKeys = new ArrayList<>();
|
||||||
for( Object key : properties.keySet() ) {
|
for( Object okey : properties.keySet() ) {
|
||||||
if( ((String)key).startsWith( "[" ) )
|
String key = (String) okey;
|
||||||
platformSpecificKeys.add( (String) key );
|
if( key.startsWith( "[" ) &&
|
||||||
|
(key.startsWith( "[win]" ) ||
|
||||||
|
key.startsWith( "[mac]" ) ||
|
||||||
|
key.startsWith( "[linux]" ) ||
|
||||||
|
key.startsWith( "[light]" ) ||
|
||||||
|
key.startsWith( "[dark]" )) )
|
||||||
|
platformSpecificKeys.add( key );
|
||||||
}
|
}
|
||||||
|
|
||||||
// remove platform specific properties and re-add only properties
|
// remove platform specific properties and re-add only properties
|
||||||
// for current platform, but with platform prefix removed
|
// for current platform, but with platform prefix removed
|
||||||
if( !platformSpecificKeys.isEmpty() ) {
|
if( !platformSpecificKeys.isEmpty() ) {
|
||||||
|
// handle light/dark specific properties
|
||||||
|
String lightOrDarkPrefix = dark ? "[dark]" : "[light]";
|
||||||
|
for( String key : platformSpecificKeys ) {
|
||||||
|
if( key.startsWith( lightOrDarkPrefix ) )
|
||||||
|
properties.put( key.substring( lightOrDarkPrefix.length() ), properties.remove( key ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
// handle platform specific properties
|
||||||
String platformPrefix =
|
String platformPrefix =
|
||||||
SystemInfo.IS_WINDOWS ? "[win]" :
|
SystemInfo.IS_WINDOWS ? "[win]" :
|
||||||
SystemInfo.IS_MAC ? "[mac]" :
|
SystemInfo.IS_MAC ? "[mac]" :
|
||||||
@@ -141,7 +169,7 @@ class UIDefaultsLoader
|
|||||||
try {
|
try {
|
||||||
globals.put( key.substring( GLOBAL_PREFIX.length() ), parseValue( key, value, resolver, addonClassLoaders ) );
|
globals.put( key.substring( GLOBAL_PREFIX.length() ), parseValue( key, value, resolver, addonClassLoaders ) );
|
||||||
} catch( RuntimeException ex ) {
|
} catch( RuntimeException ex ) {
|
||||||
logParseError( key, value, ex );
|
logParseError( Level.SEVERE, key, value, ex );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -166,26 +194,24 @@ class UIDefaultsLoader
|
|||||||
try {
|
try {
|
||||||
defaults.put( key, parseValue( key, value, resolver, addonClassLoaders ) );
|
defaults.put( key, parseValue( key, value, resolver, addonClassLoaders ) );
|
||||||
} catch( RuntimeException ex ) {
|
} catch( RuntimeException ex ) {
|
||||||
logParseError( key, value, ex );
|
logParseError( Level.SEVERE, key, value, ex );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch( IOException ex ) {
|
} catch( IOException ex ) {
|
||||||
ex.printStackTrace();
|
FlatLaf.LOG.log( Level.SEVERE, "FlatLaf: Failed to load properties files.", ex );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void logParseError( String key, String value, RuntimeException ex ) {
|
static void logParseError( Level level, String key, String value, RuntimeException ex ) {
|
||||||
System.err.println( "Failed to parse: '" + key + '=' + value + '\'' );
|
FlatLaf.LOG.log( level, "FlatLaf: Failed to parse: '" + key + '=' + value + '\'', ex );
|
||||||
System.err.println( " " + ex.getMessage() );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String resolveValue( Properties properties, String value ) {
|
private static String resolveValue( Properties properties, String value ) {
|
||||||
if( !value.startsWith( VARIABLE_PREFIX ) )
|
if( value.startsWith( PROPERTY_PREFIX ) )
|
||||||
|
value = value.substring( PROPERTY_PREFIX.length() );
|
||||||
|
else if( !value.startsWith( VARIABLE_PREFIX ) )
|
||||||
return value;
|
return value;
|
||||||
|
|
||||||
if( value.startsWith( REF_PREFIX ) )
|
|
||||||
value = value.substring( REF_PREFIX.length() );
|
|
||||||
|
|
||||||
boolean optional = false;
|
boolean optional = false;
|
||||||
if( value.startsWith( OPTIONAL_PREFIX ) ) {
|
if( value.startsWith( OPTIONAL_PREFIX ) ) {
|
||||||
value = value.substring( OPTIONAL_PREFIX.length() );
|
value = value.substring( OPTIONAL_PREFIX.length() );
|
||||||
@@ -197,13 +223,14 @@ class UIDefaultsLoader
|
|||||||
if( optional )
|
if( optional )
|
||||||
return "null";
|
return "null";
|
||||||
|
|
||||||
throw new IllegalArgumentException( "variable or reference '" + value + "' not found" );
|
throw new IllegalArgumentException( "variable or property '" + value + "' not found" );
|
||||||
}
|
}
|
||||||
|
|
||||||
return resolveValue( properties, newValue );
|
return resolveValue( properties, newValue );
|
||||||
}
|
}
|
||||||
|
|
||||||
private enum ValueType { UNKNOWN, STRING, INTEGER, BORDER, ICON, INSETS, SIZE, COLOR, SCALEDINTEGER, INSTANCE, CLASS }
|
private enum ValueType { UNKNOWN, STRING, CHARACTER, INTEGER, FLOAT, BORDER, ICON, INSETS, DIMENSION, COLOR,
|
||||||
|
SCALEDINTEGER, SCALEDFLOAT, SCALEDINSETS, SCALEDDIMENSION, INSTANCE, CLASS, GRAYFILTER }
|
||||||
|
|
||||||
static Object parseValue( String key, String value ) {
|
static Object parseValue( String key, String value ) {
|
||||||
return parseValue( key, value, v -> v, Collections.emptyList() );
|
return parseValue( key, value, v -> v, Collections.emptyList() );
|
||||||
@@ -219,12 +246,24 @@ class UIDefaultsLoader
|
|||||||
case "true": return true;
|
case "true": return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// check for function "lazy"
|
||||||
|
// Syntax: lazy(uiKey)
|
||||||
|
if( value.startsWith( "lazy(" ) && value.endsWith( ")" ) ) {
|
||||||
|
String uiKey = value.substring( 5, value.length() - 1 ).trim();
|
||||||
|
return (LazyValue) t -> {
|
||||||
|
return lazyUIManagerGet( uiKey );
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
ValueType valueType = ValueType.UNKNOWN;
|
ValueType valueType = ValueType.UNKNOWN;
|
||||||
|
|
||||||
// check whether value type is specified in the value
|
// check whether value type is specified in the value
|
||||||
if( value.startsWith( "#" ) )
|
if( value.startsWith( "#" ) )
|
||||||
valueType = ValueType.COLOR;
|
valueType = ValueType.COLOR;
|
||||||
else if( value.startsWith( TYPE_PREFIX ) ) {
|
else if( value.startsWith( "\"" ) && value.endsWith( "\"" ) ) {
|
||||||
|
valueType = ValueType.STRING;
|
||||||
|
value = value.substring( 1, value.length() - 1 );
|
||||||
|
} else if( value.startsWith( TYPE_PREFIX ) ) {
|
||||||
int end = value.indexOf( TYPE_PREFIX_END );
|
int end = value.indexOf( TYPE_PREFIX_END );
|
||||||
if( end != -1 ) {
|
if( end != -1 ) {
|
||||||
try {
|
try {
|
||||||
@@ -251,29 +290,39 @@ class UIDefaultsLoader
|
|||||||
key.endsWith( "Margins" ) || key.endsWith( "Insets" ) )
|
key.endsWith( "Margins" ) || key.endsWith( "Insets" ) )
|
||||||
valueType = ValueType.INSETS;
|
valueType = ValueType.INSETS;
|
||||||
else if( key.endsWith( "Size" ) )
|
else if( key.endsWith( "Size" ) )
|
||||||
valueType = ValueType.SIZE;
|
valueType = ValueType.DIMENSION;
|
||||||
else if( key.endsWith( "Width" ) || key.endsWith( "Height" ) )
|
else if( key.endsWith( "Width" ) || key.endsWith( "Height" ) )
|
||||||
valueType = ValueType.INTEGER;
|
valueType = ValueType.INTEGER;
|
||||||
|
else if( key.endsWith( "Char" ) )
|
||||||
|
valueType = ValueType.CHARACTER;
|
||||||
else if( key.endsWith( "UI" ) )
|
else if( key.endsWith( "UI" ) )
|
||||||
valueType = ValueType.STRING;
|
valueType = ValueType.STRING;
|
||||||
|
else if( key.endsWith( "grayFilter" ) )
|
||||||
|
valueType = ValueType.GRAYFILTER;
|
||||||
}
|
}
|
||||||
|
|
||||||
// parse value
|
// parse value
|
||||||
switch( valueType ) {
|
switch( valueType ) {
|
||||||
case STRING: return value;
|
case STRING: return value;
|
||||||
|
case CHARACTER: return parseCharacter( value );
|
||||||
case INTEGER: return parseInteger( value, true );
|
case INTEGER: return parseInteger( value, true );
|
||||||
|
case FLOAT: return parseFloat( value, true );
|
||||||
case BORDER: return parseBorder( value, resolver, addonClassLoaders );
|
case BORDER: return parseBorder( value, resolver, addonClassLoaders );
|
||||||
case ICON: return parseInstance( value, addonClassLoaders );
|
case ICON: return parseInstance( value, addonClassLoaders );
|
||||||
case INSETS: return parseInsets( value );
|
case INSETS: return parseInsets( value );
|
||||||
case SIZE: return parseSize( value );
|
case DIMENSION: return parseDimension( value );
|
||||||
case COLOR: return parseColorOrFunction( value, true );
|
case COLOR: return parseColorOrFunction( value, resolver, true );
|
||||||
case SCALEDINTEGER: return parseScaledInteger( value );
|
case SCALEDINTEGER: return parseScaledInteger( value );
|
||||||
|
case SCALEDFLOAT: return parseScaledFloat( value );
|
||||||
|
case SCALEDINSETS: return parseScaledInsets( value );
|
||||||
|
case SCALEDDIMENSION:return parseScaledDimension( value );
|
||||||
case INSTANCE: return parseInstance( value, addonClassLoaders );
|
case INSTANCE: return parseInstance( value, addonClassLoaders );
|
||||||
case CLASS: return parseClass( value, addonClassLoaders );
|
case CLASS: return parseClass( value, addonClassLoaders );
|
||||||
|
case GRAYFILTER: return parseGrayFilter( value );
|
||||||
case UNKNOWN:
|
case UNKNOWN:
|
||||||
default:
|
default:
|
||||||
// colors
|
// colors
|
||||||
ColorUIResource color = parseColorOrFunction( value, false );
|
Object color = parseColorOrFunction( value, resolver, false );
|
||||||
if( color != null )
|
if( color != null )
|
||||||
return color;
|
return color;
|
||||||
|
|
||||||
@@ -282,6 +331,11 @@ class UIDefaultsLoader
|
|||||||
if( integer != null )
|
if( integer != null )
|
||||||
return integer;
|
return integer;
|
||||||
|
|
||||||
|
// float
|
||||||
|
Float f = parseFloat( value, false );
|
||||||
|
if( f != null )
|
||||||
|
return f;
|
||||||
|
|
||||||
// string
|
// string
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
@@ -289,16 +343,17 @@ class UIDefaultsLoader
|
|||||||
|
|
||||||
private static Object parseBorder( String value, Function<String, String> resolver, List<ClassLoader> addonClassLoaders ) {
|
private static Object parseBorder( String value, Function<String, String> resolver, List<ClassLoader> addonClassLoaders ) {
|
||||||
if( value.indexOf( ',' ) >= 0 ) {
|
if( value.indexOf( ',' ) >= 0 ) {
|
||||||
// top,left,bottom,right[,lineColor]
|
// top,left,bottom,right[,lineColor[,lineThickness]]
|
||||||
List<String> parts = StringUtils.split( value, ',' );
|
List<String> parts = split( value, ',' );
|
||||||
Insets insets = parseInsets( value );
|
Insets insets = parseInsets( value );
|
||||||
ColorUIResource lineColor = (parts.size() == 5)
|
ColorUIResource lineColor = (parts.size() >= 5)
|
||||||
? parseColorOrFunction( resolver.apply( parts.get( 4 ) ), true )
|
? (ColorUIResource) parseColorOrFunction( resolver.apply( parts.get( 4 ) ), resolver, true )
|
||||||
: null;
|
: null;
|
||||||
|
float lineThickness = (parts.size() >= 6) ? parseFloat( parts.get( 5 ), true ) : 1f;
|
||||||
|
|
||||||
return (LazyValue) t -> {
|
return (LazyValue) t -> {
|
||||||
return (lineColor != null)
|
return (lineColor != null)
|
||||||
? new FlatLineBorder( insets, lineColor )
|
? new FlatLineBorder( insets, lineColor, lineThickness )
|
||||||
: new FlatEmptyBorder( insets );
|
: new FlatEmptyBorder( insets );
|
||||||
};
|
};
|
||||||
} else
|
} else
|
||||||
@@ -310,7 +365,7 @@ class UIDefaultsLoader
|
|||||||
try {
|
try {
|
||||||
return findClass( value, addonClassLoaders ).newInstance();
|
return findClass( value, addonClassLoaders ).newInstance();
|
||||||
} catch( InstantiationException | IllegalAccessException | ClassNotFoundException ex ) {
|
} catch( InstantiationException | IllegalAccessException | ClassNotFoundException ex ) {
|
||||||
ex.printStackTrace();
|
FlatLaf.LOG.log( Level.SEVERE, "FlatLaf: Failed to instantiate '" + value + "'.", ex );
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -321,7 +376,7 @@ class UIDefaultsLoader
|
|||||||
try {
|
try {
|
||||||
return findClass( value, addonClassLoaders );
|
return findClass( value, addonClassLoaders );
|
||||||
} catch( ClassNotFoundException ex ) {
|
} catch( ClassNotFoundException ex ) {
|
||||||
ex.printStackTrace();
|
FlatLaf.LOG.log( Level.SEVERE, "FlatLaf: Failed to find class '" + value + "'.", ex );
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -346,7 +401,7 @@ class UIDefaultsLoader
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static Insets parseInsets( String value ) {
|
private static Insets parseInsets( String value ) {
|
||||||
List<String> numbers = StringUtils.split( value, ',' );
|
List<String> numbers = split( value, ',' );
|
||||||
try {
|
try {
|
||||||
return new InsetsUIResource(
|
return new InsetsUIResource(
|
||||||
Integer.parseInt( numbers.get( 0 ) ),
|
Integer.parseInt( numbers.get( 0 ) ),
|
||||||
@@ -358,8 +413,8 @@ class UIDefaultsLoader
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Dimension parseSize( String value ) {
|
private static Dimension parseDimension( String value ) {
|
||||||
List<String> numbers = StringUtils.split( value, ',' );
|
List<String> numbers = split( value, ',' );
|
||||||
try {
|
try {
|
||||||
return new DimensionUIResource(
|
return new DimensionUIResource(
|
||||||
Integer.parseInt( numbers.get( 0 ) ),
|
Integer.parseInt( numbers.get( 0 ) ),
|
||||||
@@ -369,9 +424,9 @@ class UIDefaultsLoader
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ColorUIResource parseColorOrFunction( String value, boolean reportError ) {
|
private static Object parseColorOrFunction( String value, Function<String, String> resolver, boolean reportError ) {
|
||||||
if( value.endsWith( ")" ) )
|
if( value.endsWith( ")" ) )
|
||||||
return parseColorFunctions( value, reportError );
|
return parseColorFunctions( value, resolver, reportError );
|
||||||
|
|
||||||
return parseColor( value, reportError );
|
return parseColor( value, reportError );
|
||||||
}
|
}
|
||||||
@@ -439,7 +494,7 @@ class UIDefaultsLoader
|
|||||||
: (((n >> 8) & 0xffffff) | ((n & 0xff) << 24)); // move alpha from lowest to highest byte
|
: (((n >> 8) & 0xffffff) | ((n & 0xff) << 24)); // move alpha from lowest to highest byte
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ColorUIResource parseColorFunctions( String value, boolean reportError ) {
|
private static Object parseColorFunctions( String value, Function<String, String> resolver, boolean reportError ) {
|
||||||
int paramsStart = value.indexOf( '(' );
|
int paramsStart = value.indexOf( '(' );
|
||||||
if( paramsStart < 0 ) {
|
if( paramsStart < 0 ) {
|
||||||
if( reportError )
|
if( reportError )
|
||||||
@@ -448,37 +503,135 @@ class UIDefaultsLoader
|
|||||||
}
|
}
|
||||||
|
|
||||||
String function = value.substring( 0, paramsStart ).trim();
|
String function = value.substring( 0, paramsStart ).trim();
|
||||||
List<String> params = StringUtils.split( value.substring( paramsStart + 1, value.length() - 1 ), ',' );
|
List<String> params = splitFunctionParams( value.substring( paramsStart + 1, value.length() - 1 ), ',' );
|
||||||
if( params.isEmpty() )
|
if( params.isEmpty() )
|
||||||
throw new IllegalArgumentException( "missing parameters in function '" + value + "'" );
|
throw new IllegalArgumentException( "missing parameters in function '" + value + "'" );
|
||||||
|
|
||||||
switch( function ) {
|
switch( function ) {
|
||||||
case "lighten": return parseColorLightenOrDarken( true, params, reportError );
|
case "rgb": return parseColorRgbOrRgba( false, params, resolver, reportError );
|
||||||
case "darken": return parseColorLightenOrDarken( false, params, reportError );
|
case "rgba": return parseColorRgbOrRgba( true, params, resolver, reportError );
|
||||||
|
case "hsl": return parseColorHslOrHsla( false, params );
|
||||||
|
case "hsla": return parseColorHslOrHsla( true, params );
|
||||||
|
case "lighten": return parseColorHSLIncreaseDecrease( 2, true, params, resolver, reportError );
|
||||||
|
case "darken": return parseColorHSLIncreaseDecrease( 2, false, params, resolver, reportError );
|
||||||
|
case "saturate": return parseColorHSLIncreaseDecrease( 1, true, params, resolver, reportError );
|
||||||
|
case "desaturate": return parseColorHSLIncreaseDecrease( 1, false, params, resolver, reportError );
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new IllegalArgumentException( "unknown color function '" + value + "'" );
|
throw new IllegalArgumentException( "unknown color function '" + value + "'" );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Syntax: lighten(amount[,options]) or darken(amount[,options])
|
* Syntax: rgb(red,green,blue) or rgba(red,green,blue,alpha) or rgba(color,alpha)
|
||||||
* - amount: percentage 0-100%
|
* - red: an integer 0-255 or a percentage 0-100%
|
||||||
* - options: [relative] [autoInverse]
|
* - green: an integer 0-255 or a percentage 0-100%
|
||||||
|
* - blue: an integer 0-255 or a percentage 0-100%
|
||||||
|
* - alpha: an integer 0-255 or a percentage 0-100%
|
||||||
*/
|
*/
|
||||||
private static ColorUIResource parseColorLightenOrDarken( boolean lighten, List<String> params, boolean reportError ) {
|
private static ColorUIResource parseColorRgbOrRgba( boolean hasAlpha, List<String> params,
|
||||||
int amount = parsePercentage( params.get( 0 ) );
|
Function<String, String> resolver, boolean reportError )
|
||||||
boolean relative = false;
|
{
|
||||||
boolean autoInverse = false;
|
if( hasAlpha && params.size() == 2 ) {
|
||||||
|
// syntax rgba(color,alpha), which allows adding alpha to any color
|
||||||
|
String colorStr = params.get( 0 );
|
||||||
|
int alpha = parseInteger( params.get( 1 ), 0, 255, true );
|
||||||
|
|
||||||
if( params.size() >= 2 ) {
|
ColorUIResource color = (ColorUIResource) parseColorOrFunction( resolver.apply( colorStr ), resolver, reportError );
|
||||||
String options = params.get( 1 );
|
return new ColorUIResource( new Color( ((alpha & 0xff) << 24) | (color.getRGB() & 0xffffff), true ) );
|
||||||
relative = options.contains( "relative" );
|
|
||||||
autoInverse = options.contains( "autoInverse" );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return new DerivedColor( lighten
|
int red = parseInteger( params.get( 0 ), 0, 255, true );
|
||||||
? new ColorFunctions.Lighten( amount, relative, autoInverse )
|
int green = parseInteger( params.get( 1 ), 0, 255, true );
|
||||||
: new ColorFunctions.Darken( amount, relative, autoInverse ) );
|
int blue = parseInteger( params.get( 2 ), 0, 255, true );
|
||||||
|
int alpha = hasAlpha ? parseInteger( params.get( 3 ), 0, 255, true ) : 255;
|
||||||
|
|
||||||
|
return hasAlpha
|
||||||
|
? new ColorUIResource( new Color( red, green, blue, alpha ) )
|
||||||
|
: new ColorUIResource( red, green, blue );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Syntax: hsl(hue,saturation,lightness) or hsla(hue,saturation,lightness,alpha)
|
||||||
|
* - hue: an integer 0-360 representing degrees
|
||||||
|
* - saturation: a percentage 0-100%
|
||||||
|
* - lightness: a percentage 0-100%
|
||||||
|
* - alpha: a percentage 0-100%
|
||||||
|
*/
|
||||||
|
private static ColorUIResource parseColorHslOrHsla( boolean hasAlpha, List<String> params ) {
|
||||||
|
int hue = parseInteger( params.get( 0 ), 0, 360, false );
|
||||||
|
int saturation = parsePercentage( params.get( 1 ) );
|
||||||
|
int lightness = parsePercentage( params.get( 2 ) );
|
||||||
|
int alpha = hasAlpha ? parsePercentage( params.get( 3 ) ) : 100;
|
||||||
|
|
||||||
|
float[] hsl = new float[] { hue, saturation, lightness };
|
||||||
|
return new ColorUIResource( HSLColor.toRGB( hsl, alpha / 100f ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Syntax: lighten(color,amount[,options]) or darken(color,amount[,options]) or
|
||||||
|
* saturate(color,amount[,options]) or desaturate(color,amount[,options])
|
||||||
|
* - color: a color (e.g. #f00) or a color function
|
||||||
|
* - amount: percentage 0-100%
|
||||||
|
* - options: [relative] [autoInverse] [noAutoInverse] [lazy] [derived]
|
||||||
|
*/
|
||||||
|
private static Object parseColorHSLIncreaseDecrease( int hslIndex, boolean increase,
|
||||||
|
List<String> params, Function<String, String> resolver, boolean reportError )
|
||||||
|
{
|
||||||
|
String colorStr = params.get( 0 );
|
||||||
|
int amount = parsePercentage( params.get( 1 ) );
|
||||||
|
boolean relative = false;
|
||||||
|
boolean autoInverse = false;
|
||||||
|
boolean lazy = false;
|
||||||
|
boolean derived = false;
|
||||||
|
|
||||||
|
if( params.size() > 2 ) {
|
||||||
|
String options = params.get( 2 );
|
||||||
|
relative = options.contains( "relative" );
|
||||||
|
autoInverse = options.contains( "autoInverse" );
|
||||||
|
lazy = options.contains( "lazy" );
|
||||||
|
derived = options.contains( "derived" );
|
||||||
|
|
||||||
|
// use autoInverse by default for derived colors, except if noAutoInverse is set
|
||||||
|
if( derived && !options.contains( "noAutoInverse" ) )
|
||||||
|
autoInverse = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// create function
|
||||||
|
ColorFunction function = new ColorFunctions.HSLIncreaseDecrease(
|
||||||
|
hslIndex, increase, amount, relative, autoInverse );
|
||||||
|
|
||||||
|
if( lazy ) {
|
||||||
|
return (LazyValue) t -> {
|
||||||
|
Object color = lazyUIManagerGet( colorStr );
|
||||||
|
return (color instanceof Color)
|
||||||
|
? new ColorUIResource( ColorFunctions.applyFunctions( (Color) color, function ) )
|
||||||
|
: null;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// parse base color
|
||||||
|
String resolvedColorStr = resolver.apply( colorStr );
|
||||||
|
ColorUIResource baseColor = (ColorUIResource) parseColorOrFunction( resolvedColorStr, resolver, reportError );
|
||||||
|
|
||||||
|
// apply this function to base color
|
||||||
|
Color newColor = ColorFunctions.applyFunctions( baseColor, function );
|
||||||
|
|
||||||
|
if( derived ) {
|
||||||
|
ColorFunction[] functions;
|
||||||
|
if( baseColor instanceof DerivedColor && resolvedColorStr == colorStr ) {
|
||||||
|
// if the base color is also derived, join the color functions
|
||||||
|
// but only if base color function is specified directly in this function
|
||||||
|
ColorFunction[] baseFunctions = ((DerivedColor)baseColor).getFunctions();
|
||||||
|
functions = new ColorFunction[baseFunctions.length + 1];
|
||||||
|
System.arraycopy( baseFunctions, 0, functions, 0, baseFunctions.length );
|
||||||
|
functions[baseFunctions.length] = function;
|
||||||
|
} else
|
||||||
|
functions = new ColorFunction[] { function };
|
||||||
|
|
||||||
|
return new DerivedColor( newColor, functions );
|
||||||
|
}
|
||||||
|
|
||||||
|
return new ColorUIResource( newColor );
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int parsePercentage( String value ) {
|
private static int parsePercentage( String value ) {
|
||||||
@@ -497,6 +650,24 @@ class UIDefaultsLoader
|
|||||||
return val;
|
return val;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static Character parseCharacter( String value ) {
|
||||||
|
if( value.length() != 1 )
|
||||||
|
throw new IllegalArgumentException( "invalid character '" + value + "'" );
|
||||||
|
return value.charAt( 0 );
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Integer parseInteger( String value, int min, int max, boolean allowPercentage ) {
|
||||||
|
if( allowPercentage && value.endsWith( "%" ) ) {
|
||||||
|
int percent = parsePercentage( value );
|
||||||
|
return (max * percent) / 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
Integer integer = parseInteger( value, true );
|
||||||
|
if( integer.intValue() < min || integer.intValue() > max )
|
||||||
|
throw new NumberFormatException( "integer '" + value + "' out of range (" + min + '-' + max + ')' );
|
||||||
|
return integer;
|
||||||
|
}
|
||||||
|
|
||||||
private static Integer parseInteger( String value, boolean reportError ) {
|
private static Integer parseInteger( String value, boolean reportError ) {
|
||||||
try {
|
try {
|
||||||
return Integer.parseInt( value );
|
return Integer.parseInt( value );
|
||||||
@@ -507,10 +678,112 @@ class UIDefaultsLoader
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static Float parseFloat( String value, boolean reportError ) {
|
||||||
|
try {
|
||||||
|
return Float.parseFloat( value );
|
||||||
|
} catch( NumberFormatException ex ) {
|
||||||
|
if( reportError )
|
||||||
|
throw new NumberFormatException( "invalid float '" + value + "'" );
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
private static ActiveValue parseScaledInteger( String value ) {
|
private static ActiveValue parseScaledInteger( String value ) {
|
||||||
int val = parseInteger( value, true );
|
int val = parseInteger( value, true );
|
||||||
return (ActiveValue) t -> {
|
return (ActiveValue) t -> {
|
||||||
return UIScale.scale( val );
|
return UIScale.scale( val );
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static ActiveValue parseScaledFloat( String value ) {
|
||||||
|
float val = parseFloat( value, true );
|
||||||
|
return (ActiveValue) t -> {
|
||||||
|
return UIScale.scale( val );
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ActiveValue parseScaledInsets( String value ) {
|
||||||
|
Insets insets = parseInsets( value );
|
||||||
|
return (ActiveValue) t -> {
|
||||||
|
return UIScale.scale( insets );
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ActiveValue parseScaledDimension( String value ) {
|
||||||
|
Dimension dimension = parseDimension( value );
|
||||||
|
return (ActiveValue) t -> {
|
||||||
|
return UIScale.scale( dimension );
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Object parseGrayFilter( String value ) {
|
||||||
|
List<String> numbers = split( value, ',' );
|
||||||
|
try {
|
||||||
|
int brightness = Integer.parseInt( numbers.get( 0 ) );
|
||||||
|
int contrast = Integer.parseInt( numbers.get( 1 ) );
|
||||||
|
int alpha = Integer.parseInt( numbers.get( 2 ) );
|
||||||
|
|
||||||
|
return (LazyValue) t -> {
|
||||||
|
return new GrayFilter( brightness, contrast, alpha );
|
||||||
|
};
|
||||||
|
} catch( NumberFormatException ex ) {
|
||||||
|
throw new IllegalArgumentException( "invalid gray filter '" + value + "'" );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Split string and trim parts.
|
||||||
|
*/
|
||||||
|
private static List<String> split( String str, char delim ) {
|
||||||
|
List<String> result = StringUtils.split( str, delim );
|
||||||
|
|
||||||
|
// trim strings
|
||||||
|
int size = result.size();
|
||||||
|
for( int i = 0; i < size; i++ )
|
||||||
|
result.set( i, result.get( i ).trim() );
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Splits function parameters and allows using functions as parameters.
|
||||||
|
* In other words: Delimiters surrounded by '(' and ')' are ignored.
|
||||||
|
*/
|
||||||
|
private static List<String> splitFunctionParams( String str, char delim ) {
|
||||||
|
ArrayList<String> strs = new ArrayList<>();
|
||||||
|
int nestLevel = 0;
|
||||||
|
int start = 0;
|
||||||
|
int strlen = str.length();
|
||||||
|
for( int i = 0; i < strlen; i++ ) {
|
||||||
|
char ch = str.charAt( i );
|
||||||
|
if( ch == '(' )
|
||||||
|
nestLevel++;
|
||||||
|
else if( ch == ')' )
|
||||||
|
nestLevel--;
|
||||||
|
else if( nestLevel == 0 && ch == delim ) {
|
||||||
|
strs.add( str.substring( start, i ).trim() );
|
||||||
|
start = i + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
strs.add( str.substring( start ).trim() );
|
||||||
|
|
||||||
|
return strs;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For use in LazyValue to get value for given key from UIManager and report error
|
||||||
|
* if not found. If key is prefixed by '?', then no error is reported.
|
||||||
|
*/
|
||||||
|
private static Object lazyUIManagerGet( String uiKey ) {
|
||||||
|
boolean optional = false;
|
||||||
|
if( uiKey.startsWith( OPTIONAL_PREFIX ) ) {
|
||||||
|
uiKey = uiKey.substring( OPTIONAL_PREFIX.length() );
|
||||||
|
optional = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Object value = UIManager.get( uiKey );
|
||||||
|
if( value == null && !optional )
|
||||||
|
FlatLaf.LOG.log( Level.SEVERE, "FlatLaf: '" + uiKey + "' not found in UI defaults." );
|
||||||
|
return value;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ public class FlatAscendingSortIcon
|
|||||||
g.setColor( sortIconColor );
|
g.setColor( sortIconColor );
|
||||||
if( chevron ) {
|
if( chevron ) {
|
||||||
// chevron arrow
|
// chevron arrow
|
||||||
Path2D path = FlatUIUtils.createPath( false, 1,5, 5,1, 9,5 );
|
Path2D path = FlatUIUtils.createPath( false, 1,4, 5,0, 9,4 );
|
||||||
g.setStroke( new BasicStroke( 1f ) );
|
g.setStroke( new BasicStroke( 1f ) );
|
||||||
g.draw( path );
|
g.draw( path );
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -0,0 +1,59 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2020 FormDev Software GmbH
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.formdev.flatlaf.icons;
|
||||||
|
|
||||||
|
import java.awt.Component;
|
||||||
|
import java.awt.Graphics2D;
|
||||||
|
import java.awt.geom.Path2D;
|
||||||
|
import java.awt.geom.Rectangle2D;
|
||||||
|
import java.awt.geom.RoundRectangle2D;
|
||||||
|
import javax.swing.UIManager;
|
||||||
|
import com.formdev.flatlaf.ui.FlatUIUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* "caps lock" icon for {@link javax.swing.JPasswordField}.
|
||||||
|
*
|
||||||
|
* @uiDefault PasswordField.capsLockIconColor Color
|
||||||
|
*
|
||||||
|
* @author Karl Tauber
|
||||||
|
*/
|
||||||
|
public class FlatCapsLockIcon
|
||||||
|
extends FlatAbstractIcon
|
||||||
|
{
|
||||||
|
public FlatCapsLockIcon() {
|
||||||
|
super( 16, 16, UIManager.getColor( "PasswordField.capsLockIconColor" ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void paintIcon( Component c, Graphics2D g ) {
|
||||||
|
/*
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
|
||||||
|
<g fill="none" fill-rule="evenodd">
|
||||||
|
<rect width="16" height="16" fill="#6E6E6E" rx="3"/>
|
||||||
|
<rect width="6" height="2" x="5" y="12" fill="#FFF"/>
|
||||||
|
<path fill="#FFF" d="M2,8 L8,2 L14,8 L11,8 L11,10 L5,10 L5,8 L2,8 Z"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
*/
|
||||||
|
|
||||||
|
Path2D path = new Path2D.Float( Path2D.WIND_EVEN_ODD );
|
||||||
|
path.append( new RoundRectangle2D.Float( 0, 0, 16, 16, 6, 6 ), false );
|
||||||
|
path.append( new Rectangle2D.Float( 5, 12, 6, 2 ), false );
|
||||||
|
path.append( FlatUIUtils.createPath( 2,8, 8,2, 14,8, 11,8, 11,10, 5,10, 5,8 ), false );
|
||||||
|
g.fill( path );
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -96,7 +96,7 @@ public class FlatCheckBoxIcon
|
|||||||
boolean selected = indeterminate || (c instanceof AbstractButton && ((AbstractButton)c).isSelected());
|
boolean selected = indeterminate || (c instanceof AbstractButton && ((AbstractButton)c).isSelected());
|
||||||
|
|
||||||
// paint focused border
|
// paint focused border
|
||||||
if( c.hasFocus() && focusWidth > 0 ) {
|
if( FlatUIUtils.isPermanentFocusOwner( c ) && focusWidth > 0 ) {
|
||||||
g2.setColor( focusColor );
|
g2.setColor( focusColor );
|
||||||
paintFocusBorder( g2 );
|
paintFocusBorder( g2 );
|
||||||
}
|
}
|
||||||
@@ -111,13 +111,13 @@ public class FlatCheckBoxIcon
|
|||||||
paintBorder( g2 );
|
paintBorder( g2 );
|
||||||
|
|
||||||
// paint background
|
// paint background
|
||||||
FlatUIUtils.setColor( g2, FlatButtonUI.buttonStateColor( c,
|
g2.setColor( FlatUIUtils.deriveColor( FlatButtonUI.buttonStateColor( c,
|
||||||
selected ? selectedBackground : background,
|
selected ? selectedBackground : background,
|
||||||
disabledBackground,
|
disabledBackground,
|
||||||
focusedBackground,
|
focusedBackground,
|
||||||
selected && selectedHoverBackground != null ? selectedHoverBackground : hoverBackground,
|
selected && selectedHoverBackground != null ? selectedHoverBackground : hoverBackground,
|
||||||
selected && selectedPressedBackground != null ? selectedPressedBackground : pressedBackground ),
|
selected && selectedPressedBackground != null ? selectedPressedBackground : pressedBackground ),
|
||||||
background );
|
background ) );
|
||||||
paintBackground( g2 );
|
paintBackground( g2 );
|
||||||
|
|
||||||
// paint checkmark
|
// paint checkmark
|
||||||
|
|||||||
@@ -30,7 +30,8 @@ import javax.swing.UIManager;
|
|||||||
*
|
*
|
||||||
* @uiDefault MenuItemCheckBox.icon.checkmarkColor Color
|
* @uiDefault MenuItemCheckBox.icon.checkmarkColor Color
|
||||||
* @uiDefault MenuItemCheckBox.icon.disabledCheckmarkColor Color
|
* @uiDefault MenuItemCheckBox.icon.disabledCheckmarkColor Color
|
||||||
* @uiDefault Menu.selectionForeground Color
|
* @uiDefault MenuItem.selectionForeground Color
|
||||||
|
* @uiDefault MenuItem.selectionType String
|
||||||
*
|
*
|
||||||
* @author Karl Tauber
|
* @author Karl Tauber
|
||||||
*/
|
*/
|
||||||
@@ -39,7 +40,7 @@ public class FlatCheckBoxMenuItemIcon
|
|||||||
{
|
{
|
||||||
protected final Color checkmarkColor = UIManager.getColor( "MenuItemCheckBox.icon.checkmarkColor" );
|
protected final Color checkmarkColor = UIManager.getColor( "MenuItemCheckBox.icon.checkmarkColor" );
|
||||||
protected final Color disabledCheckmarkColor = UIManager.getColor( "MenuItemCheckBox.icon.disabledCheckmarkColor" );
|
protected final Color disabledCheckmarkColor = UIManager.getColor( "MenuItemCheckBox.icon.disabledCheckmarkColor" );
|
||||||
protected final Color selectionForeground = UIManager.getColor( "Menu.selectionForeground" );
|
protected final Color selectionForeground = UIManager.getColor( "MenuItem.selectionForeground" );
|
||||||
|
|
||||||
public FlatCheckBoxMenuItemIcon() {
|
public FlatCheckBoxMenuItemIcon() {
|
||||||
super( 15, 15, null );
|
super( 15, 15, null );
|
||||||
@@ -67,9 +68,14 @@ public class FlatCheckBoxMenuItemIcon
|
|||||||
}
|
}
|
||||||
|
|
||||||
private Color getCheckmarkColor( Component c ) {
|
private Color getCheckmarkColor( Component c ) {
|
||||||
if( c instanceof JMenuItem && ((JMenuItem)c).isArmed() )
|
if( c instanceof JMenuItem && ((JMenuItem)c).isArmed() && !isUnderlineSelection() )
|
||||||
return selectionForeground;
|
return selectionForeground;
|
||||||
|
|
||||||
return c.isEnabled() ? checkmarkColor : disabledCheckmarkColor;
|
return c.isEnabled() ? checkmarkColor : disabledCheckmarkColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean isUnderlineSelection() {
|
||||||
|
// not storing value of "MenuItem.selectionType" in class to allow changing at runtime
|
||||||
|
return "underline".equals( UIManager.getString( "MenuItem.selectionType" ) );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ public class FlatDescendingSortIcon
|
|||||||
g.setColor( sortIconColor );
|
g.setColor( sortIconColor );
|
||||||
if( chevron ) {
|
if( chevron ) {
|
||||||
// chevron arrow
|
// chevron arrow
|
||||||
Path2D path = FlatUIUtils.createPath( false, 1,1, 5,5, 9,1 );
|
Path2D path = FlatUIUtils.createPath( false, 1,0, 5,4, 9,0 );
|
||||||
g.setStroke( new BasicStroke( 1f ) );
|
g.setStroke( new BasicStroke( 1f ) );
|
||||||
g.draw( path );
|
g.draw( path );
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -82,7 +82,7 @@ public class FlatHelpButtonIcon
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
boolean enabled = c.isEnabled();
|
boolean enabled = c.isEnabled();
|
||||||
boolean focused = c.hasFocus();
|
boolean focused = FlatUIUtils.isPermanentFocusOwner( c );
|
||||||
|
|
||||||
// paint focused border
|
// paint focused border
|
||||||
if( focused ) {
|
if( focused ) {
|
||||||
@@ -100,12 +100,12 @@ public class FlatHelpButtonIcon
|
|||||||
g2.fill( new Ellipse2D.Float( focusWidth + 0.5f, focusWidth + 0.5f, 21, 21 ) );
|
g2.fill( new Ellipse2D.Float( focusWidth + 0.5f, focusWidth + 0.5f, 21, 21 ) );
|
||||||
|
|
||||||
// paint background
|
// paint background
|
||||||
FlatUIUtils.setColor( g2, FlatButtonUI.buttonStateColor( c,
|
g2.setColor( FlatUIUtils.deriveColor( FlatButtonUI.buttonStateColor( c,
|
||||||
background,
|
background,
|
||||||
disabledBackground,
|
disabledBackground,
|
||||||
focusedBackground,
|
focusedBackground,
|
||||||
hoverBackground,
|
hoverBackground,
|
||||||
pressedBackground ), background );
|
pressedBackground ), background ) );
|
||||||
g2.fill( new Ellipse2D.Float( focusWidth + 1.5f, focusWidth + 1.5f, 19, 19 ) );
|
g2.fill( new Ellipse2D.Float( focusWidth + 1.5f, focusWidth + 1.5f, 19, 19 ) );
|
||||||
|
|
||||||
// paint question mark
|
// paint question mark
|
||||||
|
|||||||
@@ -0,0 +1,61 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2019 FormDev Software GmbH
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.formdev.flatlaf.icons;
|
||||||
|
|
||||||
|
import java.awt.Color;
|
||||||
|
import java.awt.Component;
|
||||||
|
import java.awt.Dimension;
|
||||||
|
import java.awt.Graphics2D;
|
||||||
|
import javax.swing.UIManager;
|
||||||
|
import com.formdev.flatlaf.ui.FlatButtonUI;
|
||||||
|
import com.formdev.flatlaf.ui.FlatUIUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base class for internal frame icons.
|
||||||
|
*
|
||||||
|
* @uiDefault InternalFrame.buttonSize Dimension
|
||||||
|
* @uiDefault InternalFrame.buttonHoverBackground Color
|
||||||
|
* @uiDefault InternalFrame.buttonPressedBackground Color
|
||||||
|
*
|
||||||
|
* @author Karl Tauber
|
||||||
|
*/
|
||||||
|
public abstract class FlatInternalFrameAbstractIcon
|
||||||
|
extends FlatAbstractIcon
|
||||||
|
{
|
||||||
|
private final Color hoverBackground;
|
||||||
|
private final Color pressedBackground;
|
||||||
|
|
||||||
|
public FlatInternalFrameAbstractIcon() {
|
||||||
|
this( UIManager.getDimension( "InternalFrame.buttonSize" ),
|
||||||
|
UIManager.getColor( "InternalFrame.buttonHoverBackground" ),
|
||||||
|
UIManager.getColor( "InternalFrame.buttonPressedBackground" ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
public FlatInternalFrameAbstractIcon( Dimension size, Color hoverBackground, Color pressedBackground ) {
|
||||||
|
super( size.width, size.height, null );
|
||||||
|
this.hoverBackground = hoverBackground;
|
||||||
|
this.pressedBackground = pressedBackground;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void paintBackground( Component c, Graphics2D g ) {
|
||||||
|
Color background = FlatButtonUI.buttonStateColor( c, null, null, null, hoverBackground, pressedBackground );
|
||||||
|
if( background != null ) {
|
||||||
|
g.setColor( FlatUIUtils.deriveColor( background, c.getBackground() ) );
|
||||||
|
g.fillRect( 0, 0, width, height );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2019 FormDev Software GmbH
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.formdev.flatlaf.icons;
|
||||||
|
|
||||||
|
import java.awt.BasicStroke;
|
||||||
|
import java.awt.Color;
|
||||||
|
import java.awt.Component;
|
||||||
|
import java.awt.Graphics2D;
|
||||||
|
import java.awt.geom.Line2D;
|
||||||
|
import java.awt.geom.Path2D;
|
||||||
|
import javax.swing.UIManager;
|
||||||
|
import com.formdev.flatlaf.ui.FlatButtonUI;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* "close" icon for {@link javax.swing.JInternalFrame}.
|
||||||
|
*
|
||||||
|
* @uiDefault InternalFrame.buttonSize Dimension
|
||||||
|
* @uiDefault InternalFrame.closeHoverBackground Color
|
||||||
|
* @uiDefault InternalFrame.closePressedBackground Color
|
||||||
|
* @uiDefault InternalFrame.closeHoverForeground Color
|
||||||
|
* @uiDefault InternalFrame.closePressedForeground Color
|
||||||
|
*
|
||||||
|
* @author Karl Tauber
|
||||||
|
*/
|
||||||
|
public class FlatInternalFrameCloseIcon
|
||||||
|
extends FlatInternalFrameAbstractIcon
|
||||||
|
{
|
||||||
|
private final Color hoverForeground = UIManager.getColor( "InternalFrame.closeHoverForeground" );
|
||||||
|
private final Color pressedForeground = UIManager.getColor( "InternalFrame.closePressedForeground" );
|
||||||
|
|
||||||
|
public FlatInternalFrameCloseIcon() {
|
||||||
|
super( UIManager.getDimension( "InternalFrame.buttonSize" ),
|
||||||
|
UIManager.getColor( "InternalFrame.closeHoverBackground" ),
|
||||||
|
UIManager.getColor( "InternalFrame.closePressedBackground" ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void paintIcon( Component c, Graphics2D g ) {
|
||||||
|
paintBackground( c, g );
|
||||||
|
|
||||||
|
g.setColor( FlatButtonUI.buttonStateColor( c, c.getForeground(), null, null, hoverForeground, pressedForeground ) );
|
||||||
|
|
||||||
|
float mx = width / 2;
|
||||||
|
float my = height / 2;
|
||||||
|
float r = 3.25f;
|
||||||
|
|
||||||
|
Path2D path = new Path2D.Float( Path2D.WIND_EVEN_ODD );
|
||||||
|
path.append( new Line2D.Float( mx - r, my - r, mx + r, my + r ), false );
|
||||||
|
path.append( new Line2D.Float( mx - r, my + r, mx + r, my - r ), false );
|
||||||
|
g.setStroke( new BasicStroke( 1f ) );
|
||||||
|
g.draw( path );
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2019 FormDev Software GmbH
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.formdev.flatlaf.icons;
|
||||||
|
|
||||||
|
import java.awt.Component;
|
||||||
|
import java.awt.Graphics2D;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* "iconify" icon for {@link javax.swing.JInternalFrame}.
|
||||||
|
*
|
||||||
|
* @author Karl Tauber
|
||||||
|
*/
|
||||||
|
public class FlatInternalFrameIconifyIcon
|
||||||
|
extends FlatInternalFrameAbstractIcon
|
||||||
|
{
|
||||||
|
public FlatInternalFrameIconifyIcon() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void paintIcon( Component c, Graphics2D g ) {
|
||||||
|
paintBackground( c, g );
|
||||||
|
|
||||||
|
g.setColor( c.getForeground() );
|
||||||
|
g.fillRect( (width / 2) - 4, height / 2, 8, 1 );
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2019 FormDev Software GmbH
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.formdev.flatlaf.icons;
|
||||||
|
|
||||||
|
import java.awt.Component;
|
||||||
|
import java.awt.Graphics2D;
|
||||||
|
import com.formdev.flatlaf.ui.FlatUIUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* "maximize" icon for {@link javax.swing.JInternalFrame}.
|
||||||
|
*
|
||||||
|
* @author Karl Tauber
|
||||||
|
*/
|
||||||
|
public class FlatInternalFrameMaximizeIcon
|
||||||
|
extends FlatInternalFrameAbstractIcon
|
||||||
|
{
|
||||||
|
public FlatInternalFrameMaximizeIcon() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void paintIcon( Component c, Graphics2D g ) {
|
||||||
|
paintBackground( c, g );
|
||||||
|
|
||||||
|
g.setColor( c.getForeground() );
|
||||||
|
g.fill( FlatUIUtils.createRectangle( (width / 2) - 4, (height / 2) - 4, 8, 8, 1 ) );
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2019 FormDev Software GmbH
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.formdev.flatlaf.icons;
|
||||||
|
|
||||||
|
import java.awt.Component;
|
||||||
|
import java.awt.Graphics2D;
|
||||||
|
import java.awt.geom.Area;
|
||||||
|
import java.awt.geom.Path2D;
|
||||||
|
import java.awt.geom.Rectangle2D;
|
||||||
|
import com.formdev.flatlaf.ui.FlatUIUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* "restore" (or "minimize") icon for {@link javax.swing.JInternalFrame}.
|
||||||
|
*
|
||||||
|
* @author Karl Tauber
|
||||||
|
*/
|
||||||
|
public class FlatInternalFrameRestoreIcon
|
||||||
|
extends FlatInternalFrameAbstractIcon
|
||||||
|
{
|
||||||
|
public FlatInternalFrameRestoreIcon() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void paintIcon( Component c, Graphics2D g ) {
|
||||||
|
paintBackground( c, g );
|
||||||
|
|
||||||
|
g.setColor( c.getForeground() );
|
||||||
|
|
||||||
|
int x = (width / 2) - 4;
|
||||||
|
int y = (height / 2) - 4;
|
||||||
|
Path2D r1 = FlatUIUtils.createRectangle( x + 1, y - 1, 8, 8, 1 );
|
||||||
|
Path2D r2 = FlatUIUtils.createRectangle( x - 1, y + 1, 8, 8, 1 );
|
||||||
|
|
||||||
|
Area area = new Area( r1 );
|
||||||
|
area.subtract( new Area( new Rectangle2D.Float( x - 1, y + 1, 8, 8 ) ) );
|
||||||
|
g.fill( area );
|
||||||
|
|
||||||
|
g.fill( r2 );
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -32,6 +32,7 @@ import com.formdev.flatlaf.ui.FlatUIUtils;
|
|||||||
* @uiDefault Menu.icon.arrowColor Color
|
* @uiDefault Menu.icon.arrowColor Color
|
||||||
* @uiDefault Menu.icon.disabledArrowColor Color
|
* @uiDefault Menu.icon.disabledArrowColor Color
|
||||||
* @uiDefault Menu.selectionForeground Color
|
* @uiDefault Menu.selectionForeground Color
|
||||||
|
* @uiDefault MenuItem.selectionType String
|
||||||
*
|
*
|
||||||
* @author Karl Tauber
|
* @author Karl Tauber
|
||||||
*/
|
*/
|
||||||
@@ -65,9 +66,14 @@ public class FlatMenuArrowIcon
|
|||||||
}
|
}
|
||||||
|
|
||||||
private Color getArrowColor( Component c ) {
|
private Color getArrowColor( Component c ) {
|
||||||
if( c instanceof JMenu && ((JMenu)c).isSelected() )
|
if( c instanceof JMenu && ((JMenu)c).isSelected() && !isUnderlineSelection() )
|
||||||
return selectionForeground;
|
return selectionForeground;
|
||||||
|
|
||||||
return c.isEnabled() ? arrowColor : disabledArrowColor;
|
return c.isEnabled() ? arrowColor : disabledArrowColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean isUnderlineSelection() {
|
||||||
|
// not storing value of "MenuItem.selectionType" in class to allow changing at runtime
|
||||||
|
return "underline".equals( UIManager.getString( "MenuItem.selectionType" ) );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ package com.formdev.flatlaf.ui;
|
|||||||
import static com.formdev.flatlaf.util.UIScale.scale;
|
import static com.formdev.flatlaf.util.UIScale.scale;
|
||||||
import java.awt.BasicStroke;
|
import java.awt.BasicStroke;
|
||||||
import java.awt.Color;
|
import java.awt.Color;
|
||||||
|
import java.awt.Container;
|
||||||
import java.awt.Dimension;
|
import java.awt.Dimension;
|
||||||
import java.awt.Graphics;
|
import java.awt.Graphics;
|
||||||
import java.awt.Graphics2D;
|
import java.awt.Graphics2D;
|
||||||
@@ -26,6 +27,7 @@ import java.awt.Shape;
|
|||||||
import java.awt.event.MouseAdapter;
|
import java.awt.event.MouseAdapter;
|
||||||
import java.awt.event.MouseEvent;
|
import java.awt.event.MouseEvent;
|
||||||
import java.awt.geom.Path2D;
|
import java.awt.geom.Path2D;
|
||||||
|
import javax.swing.JComponent;
|
||||||
import javax.swing.plaf.UIResource;
|
import javax.swing.plaf.UIResource;
|
||||||
import javax.swing.plaf.basic.BasicArrowButton;
|
import javax.swing.plaf.basic.BasicArrowButton;
|
||||||
|
|
||||||
@@ -111,6 +113,10 @@ public class FlatArrowButton
|
|||||||
this.yOffset = yOffset;
|
this.yOffset = yOffset;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected Color deriveHoverBackground( Color hoverBackground ) {
|
||||||
|
return hoverBackground;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Dimension getPreferredSize() {
|
public Dimension getPreferredSize() {
|
||||||
return scale( super.getPreferredSize() );
|
return scale( super.getPreferredSize() );
|
||||||
@@ -132,31 +138,44 @@ public class FlatArrowButton
|
|||||||
|
|
||||||
// paint hover background
|
// paint hover background
|
||||||
if( enabled && isHover() && hoverBackground != null ) {
|
if( enabled && isHover() && hoverBackground != null ) {
|
||||||
g.setColor( hoverBackground );
|
g.setColor( deriveHoverBackground( hoverBackground ) );
|
||||||
g.fillRect( 0, 0, width, height );
|
g.fillRect( 0, 0, width, height );
|
||||||
}
|
}
|
||||||
|
|
||||||
int direction = getDirection();
|
int direction = getDirection();
|
||||||
boolean vert = (direction == NORTH || direction == SOUTH);
|
boolean vert = (direction == NORTH || direction == SOUTH);
|
||||||
|
|
||||||
|
// compute width/height
|
||||||
int w = scale( arrowWidth + (chevron ? 0 : 1) );
|
int w = scale( arrowWidth + (chevron ? 0 : 1) );
|
||||||
int h = scale( (arrowWidth / 2) + (chevron ? 0 : 1) );
|
int h = scale( (arrowWidth / 2) + (chevron ? 0 : 1) );
|
||||||
|
|
||||||
|
// rotate width/height
|
||||||
int rw = vert ? w : h;
|
int rw = vert ? w : h;
|
||||||
int rh = vert ? h : w;
|
int rh = vert ? h : w;
|
||||||
|
|
||||||
|
// chevron lines end 1px outside of width/height
|
||||||
|
if( chevron ) {
|
||||||
|
// add 1px to width/height for position calculation only
|
||||||
|
rw++;
|
||||||
|
rh++;
|
||||||
|
}
|
||||||
|
|
||||||
int x = Math.round( (width - rw) / 2f + scale( (float) xOffset ) );
|
int x = Math.round( (width - rw) / 2f + scale( (float) xOffset ) );
|
||||||
int y = Math.round( (height - rh) / 2f + scale( (float) yOffset ) );
|
int y = Math.round( (height - rh) / 2f + scale( (float) yOffset ) );
|
||||||
|
|
||||||
// optimization for small chevron arrows (e.g. OneTouchButtons in SplitPane)
|
// move arrow for round borders
|
||||||
if( x + rw >= width && x > 0 )
|
Container parent = getParent();
|
||||||
x--;
|
if( vert && parent instanceof JComponent && FlatUIUtils.hasRoundBorder( (JComponent) parent ) )
|
||||||
if( y + rh >= height && y > 0 )
|
x -= scale( parent.getComponentOrientation().isLeftToRight() ? 1 : -1 );
|
||||||
y--;
|
|
||||||
|
|
||||||
// paint arrow
|
// paint arrow
|
||||||
g.setColor( enabled
|
g.setColor( enabled
|
||||||
? (isHover() && hoverForeground != null ? hoverForeground : foreground)
|
? (isHover() && hoverForeground != null ? hoverForeground : foreground)
|
||||||
: disabledForeground );
|
: disabledForeground );
|
||||||
g.translate( x, y );
|
g.translate( x, y );
|
||||||
|
/*debug
|
||||||
|
debugPaint( g2, vert, rw, rh );
|
||||||
|
debug*/
|
||||||
Shape arrowShape = createArrowShape( direction, chevron, w, h );
|
Shape arrowShape = createArrowShape( direction, chevron, w, h );
|
||||||
if( chevron ) {
|
if( chevron ) {
|
||||||
g2.setStroke( new BasicStroke( scale( 1f ) ) );
|
g2.setStroke( new BasicStroke( scale( 1f ) ) );
|
||||||
@@ -177,4 +196,22 @@ public class FlatArrowButton
|
|||||||
default: return new Path2D.Float();
|
default: return new Path2D.Float();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*debug
|
||||||
|
private void debugPaint( Graphics g, boolean vert, int w, int h ) {
|
||||||
|
Color oldColor = g.getColor();
|
||||||
|
g.setColor( Color.red );
|
||||||
|
g.drawRect( 0, 0, w - 1, h - 1 );
|
||||||
|
|
||||||
|
int xy1 = -2;
|
||||||
|
int xy2 = h + 1;
|
||||||
|
for( int i = 0; i < 20; i++ ) {
|
||||||
|
g.drawRect( vert ? 0 : xy1, vert ? xy1 : 0, 0, 0 );
|
||||||
|
g.drawRect( vert ? 0 : xy2, vert ? xy2 : 0, 0, 0 );
|
||||||
|
xy1 -= 2;
|
||||||
|
xy2 += 2;
|
||||||
|
}
|
||||||
|
g.setColor( oldColor );
|
||||||
|
}
|
||||||
|
debug*/
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,6 +36,8 @@ import javax.swing.SwingUtilities;
|
|||||||
import javax.swing.UIManager;
|
import javax.swing.UIManager;
|
||||||
import javax.swing.plaf.basic.BasicBorders;
|
import javax.swing.plaf.basic.BasicBorders;
|
||||||
import javax.swing.text.JTextComponent;
|
import javax.swing.text.JTextComponent;
|
||||||
|
import com.formdev.flatlaf.FlatClientProperties;
|
||||||
|
import com.formdev.flatlaf.util.DerivedColor;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Border for various components (e.g. {@link javax.swing.JTextField}).
|
* Border for various components (e.g. {@link javax.swing.JTextField}).
|
||||||
@@ -48,24 +50,37 @@ import javax.swing.text.JTextComponent;
|
|||||||
* {@link FlatUIUtils#paintParentBackground} to paint the empty space correctly.
|
* {@link FlatUIUtils#paintParentBackground} to paint the empty space correctly.
|
||||||
*
|
*
|
||||||
* @uiDefault Component.focusWidth int
|
* @uiDefault Component.focusWidth int
|
||||||
* @uiDefault Component.innerFocusWidth int
|
* @uiDefault Component.innerFocusWidth int or float
|
||||||
* @uiDefault Component.focusColor Color
|
* @uiDefault Component.focusColor Color
|
||||||
* @uiDefault Component.borderColor Color
|
* @uiDefault Component.borderColor Color
|
||||||
* @uiDefault Component.disabledBorderColor Color
|
* @uiDefault Component.disabledBorderColor Color
|
||||||
* @uiDefault Component.focusedBorderColor Color
|
* @uiDefault Component.focusedBorderColor Color
|
||||||
*
|
*
|
||||||
|
* @uiDefault Component.error.borderColor Color
|
||||||
|
* @uiDefault Component.error.focusedBorderColor Color
|
||||||
|
* @uiDefault Component.warning.borderColor Color
|
||||||
|
* @uiDefault Component.warning.focusedBorderColor Color
|
||||||
|
* @uiDefault Component.custom.borderColor Color
|
||||||
|
*
|
||||||
* @author Karl Tauber
|
* @author Karl Tauber
|
||||||
*/
|
*/
|
||||||
public class FlatBorder
|
public class FlatBorder
|
||||||
extends BasicBorders.MarginBorder
|
extends BasicBorders.MarginBorder
|
||||||
{
|
{
|
||||||
protected final int focusWidth = UIManager.getInt( "Component.focusWidth" );
|
protected final int focusWidth = UIManager.getInt( "Component.focusWidth" );
|
||||||
protected final int innerFocusWidth = UIManager.getInt( "Component.innerFocusWidth" );
|
protected final float innerFocusWidth = FlatUIUtils.getUIFloat( "Component.innerFocusWidth", 0 );
|
||||||
|
protected final float innerOutlineWidth = FlatUIUtils.getUIFloat( "Component.innerOutlineWidth", 0 );
|
||||||
protected final Color focusColor = UIManager.getColor( "Component.focusColor" );
|
protected final Color focusColor = UIManager.getColor( "Component.focusColor" );
|
||||||
protected final Color borderColor = UIManager.getColor( "Component.borderColor" );
|
protected final Color borderColor = UIManager.getColor( "Component.borderColor" );
|
||||||
protected final Color disabledBorderColor = UIManager.getColor( "Component.disabledBorderColor" );
|
protected final Color disabledBorderColor = UIManager.getColor( "Component.disabledBorderColor" );
|
||||||
protected final Color focusedBorderColor = UIManager.getColor( "Component.focusedBorderColor" );
|
protected final Color focusedBorderColor = UIManager.getColor( "Component.focusedBorderColor" );
|
||||||
|
|
||||||
|
protected final Color errorBorderColor = UIManager.getColor( "Component.error.borderColor" );
|
||||||
|
protected final Color errorFocusedBorderColor = UIManager.getColor( "Component.error.focusedBorderColor" );
|
||||||
|
protected final Color warningBorderColor = UIManager.getColor( "Component.warning.borderColor" );
|
||||||
|
protected final Color warningFocusedBorderColor = UIManager.getColor( "Component.warning.focusedBorderColor" );
|
||||||
|
protected final Color customBorderColor = UIManager.getColor( "Component.custom.borderColor" );
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void paintBorder( Component c, Graphics g, int x, int y, int width, int height ) {
|
public void paintBorder( Component c, Graphics g, int x, int y, int width, int height ) {
|
||||||
Graphics2D g2 = (Graphics2D) g.create();
|
Graphics2D g2 = (Graphics2D) g.create();
|
||||||
@@ -73,23 +88,53 @@ public class FlatBorder
|
|||||||
FlatUIUtils.setRenderingHints( g2 );
|
FlatUIUtils.setRenderingHints( g2 );
|
||||||
|
|
||||||
boolean isCellEditor = isTableCellEditor( c );
|
boolean isCellEditor = isTableCellEditor( c );
|
||||||
float focusWidth = isCellEditor ? 0 : getFocusWidth();
|
float focusWidth = isCellEditor ? 0 : scale( (float) getFocusWidth( c ) );
|
||||||
float borderWidth = getBorderWidth( c );
|
float borderWidth = scale( (float) getBorderWidth( c ) );
|
||||||
float arc = isCellEditor ? 0 : getArc();
|
float arc = isCellEditor ? 0 : scale( (float) getArc( c ) );
|
||||||
|
Color outlineColor = getOutlineColor( c );
|
||||||
|
|
||||||
if( isFocused( c ) ) {
|
if( outlineColor != null || isFocused( c ) ) {
|
||||||
g2.setColor( getFocusColor( c ) );
|
float innerFocusWidth = !(c instanceof JScrollPane)
|
||||||
|
? (outlineColor != null ? innerOutlineWidth : this.innerFocusWidth)
|
||||||
|
: 0;
|
||||||
|
|
||||||
|
g2.setColor( (outlineColor != null) ? outlineColor : getFocusColor( c ) );
|
||||||
FlatUIUtils.paintComponentOuterBorder( g2, x, y, width, height, focusWidth,
|
FlatUIUtils.paintComponentOuterBorder( g2, x, y, width, height, focusWidth,
|
||||||
getLineWidth() + scale( (float) innerFocusWidth ), arc );
|
scale( (float) getLineWidth( c ) ) + scale( innerFocusWidth ), arc );
|
||||||
}
|
}
|
||||||
|
|
||||||
g2.setPaint( getBorderColor( c ) );
|
g2.setPaint( (outlineColor != null) ? outlineColor : getBorderColor( c ) );
|
||||||
FlatUIUtils.paintComponentBorder( g2, x, y, width, height, focusWidth, borderWidth, arc );
|
FlatUIUtils.paintComponentBorder( g2, x, y, width, height, focusWidth, borderWidth, arc );
|
||||||
} finally {
|
} finally {
|
||||||
g2.dispose();
|
g2.dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected Color getOutlineColor( Component c ) {
|
||||||
|
if( !(c instanceof JComponent) )
|
||||||
|
return null;
|
||||||
|
|
||||||
|
Object outline = ((JComponent)c).getClientProperty( FlatClientProperties.OUTLINE );
|
||||||
|
if( outline instanceof String ) {
|
||||||
|
switch( (String) outline ) {
|
||||||
|
case FlatClientProperties.OUTLINE_ERROR:
|
||||||
|
return isFocused( c ) ? errorFocusedBorderColor : errorBorderColor;
|
||||||
|
|
||||||
|
case FlatClientProperties.OUTLINE_WARNING:
|
||||||
|
return isFocused( c ) ? warningFocusedBorderColor : warningBorderColor;
|
||||||
|
}
|
||||||
|
} else if( outline instanceof Color ) {
|
||||||
|
Color color = (Color) outline;
|
||||||
|
// use color functions to compute color for unfocused state
|
||||||
|
if( !isFocused( c ) && customBorderColor instanceof DerivedColor )
|
||||||
|
color = ((DerivedColor)customBorderColor).derive( color );
|
||||||
|
return color;
|
||||||
|
} else if( outline instanceof Color[] && ((Color[])outline).length >= 2 )
|
||||||
|
return ((Color[])outline)[isFocused( c ) ? 0 : 1];
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
protected Color getFocusColor( Component c ) {
|
protected Color getFocusColor( Component c ) {
|
||||||
return focusColor;
|
return focusColor;
|
||||||
}
|
}
|
||||||
@@ -117,7 +162,7 @@ public class FlatBorder
|
|||||||
JViewport viewport = ((JScrollPane)c).getViewport();
|
JViewport viewport = ((JScrollPane)c).getViewport();
|
||||||
Component view = (viewport != null) ? viewport.getView() : null;
|
Component view = (viewport != null) ? viewport.getView() : null;
|
||||||
if( view != null ) {
|
if( view != null ) {
|
||||||
if( view.hasFocus() )
|
if( FlatUIUtils.isPermanentFocusOwner( view ) )
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
if( (view instanceof JTable && ((JTable)view).isEditing()) ||
|
if( (view instanceof JTable && ((JTable)view).isEditing()) ||
|
||||||
@@ -131,17 +176,20 @@ public class FlatBorder
|
|||||||
return false;
|
return false;
|
||||||
} else if( c instanceof JComboBox && ((JComboBox<?>)c).isEditable() ) {
|
} else if( c instanceof JComboBox && ((JComboBox<?>)c).isEditable() ) {
|
||||||
Component editorComponent = ((JComboBox<?>)c).getEditor().getEditorComponent();
|
Component editorComponent = ((JComboBox<?>)c).getEditor().getEditorComponent();
|
||||||
return (editorComponent != null) ? editorComponent.hasFocus() : false;
|
return (editorComponent != null) ? FlatUIUtils.isPermanentFocusOwner( editorComponent ) : false;
|
||||||
} else if( c instanceof JSpinner ) {
|
} else if( c instanceof JSpinner ) {
|
||||||
|
if( FlatUIUtils.isPermanentFocusOwner( c ) )
|
||||||
|
return true;
|
||||||
|
|
||||||
JComponent editor = ((JSpinner)c).getEditor();
|
JComponent editor = ((JSpinner)c).getEditor();
|
||||||
if( editor instanceof JSpinner.DefaultEditor ) {
|
if( editor instanceof JSpinner.DefaultEditor ) {
|
||||||
JTextField textField = ((JSpinner.DefaultEditor)editor).getTextField();
|
JTextField textField = ((JSpinner.DefaultEditor)editor).getTextField();
|
||||||
if( textField != null )
|
if( textField != null )
|
||||||
return textField.hasFocus();
|
return FlatUIUtils.isPermanentFocusOwner( textField );
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
} else
|
} else
|
||||||
return c.hasFocus();
|
return FlatUIUtils.isPermanentFocusOwner( c );
|
||||||
}
|
}
|
||||||
|
|
||||||
protected boolean isTableCellEditor( Component c ) {
|
protected boolean isTableCellEditor( Component c ) {
|
||||||
@@ -151,7 +199,8 @@ public class FlatBorder
|
|||||||
@Override
|
@Override
|
||||||
public Insets getBorderInsets( Component c, Insets insets ) {
|
public Insets getBorderInsets( Component c, Insets insets ) {
|
||||||
boolean isCellEditor = isTableCellEditor( c );
|
boolean isCellEditor = isTableCellEditor( c );
|
||||||
float ow = (isCellEditor ? 0 : getFocusWidth()) + getLineWidth();
|
float focusWidth = isCellEditor ? 0 : scale( (float) getFocusWidth( c ) );
|
||||||
|
float ow = focusWidth + scale( (float) getLineWidth( c ) );
|
||||||
|
|
||||||
insets = super.getBorderInsets( c, insets );
|
insets = super.getBorderInsets( c, insets );
|
||||||
insets.top = Math.round( scale( (float) insets.top ) + ow );
|
insets.top = Math.round( scale( (float) insets.top ) + ow );
|
||||||
@@ -161,19 +210,33 @@ public class FlatBorder
|
|||||||
return insets;
|
return insets;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected float getFocusWidth() {
|
/**
|
||||||
return scale( (float) focusWidth );
|
* Returns the (unscaled) thickness of the outer focus border.
|
||||||
|
*/
|
||||||
|
protected int getFocusWidth( Component c ) {
|
||||||
|
return focusWidth;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected float getLineWidth() {
|
/**
|
||||||
return scale( 1f );
|
* Returns the (unscaled) line thickness used to compute the border insets.
|
||||||
|
* This may be different to {@link #getBorderWidth}.
|
||||||
|
*/
|
||||||
|
protected int getLineWidth( Component c ) {
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected float getBorderWidth( Component c ) {
|
/**
|
||||||
return getLineWidth();
|
* Returns the (unscaled) line thickness used to paint the border.
|
||||||
|
* This may be different to {@link #getLineWidth}.
|
||||||
|
*/
|
||||||
|
protected int getBorderWidth( Component c ) {
|
||||||
|
return getLineWidth( c );
|
||||||
}
|
}
|
||||||
|
|
||||||
protected float getArc() {
|
/**
|
||||||
|
* Returns the (unscaled) arc diameter of the border.
|
||||||
|
*/
|
||||||
|
protected int getArc( Component c ) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,16 +16,16 @@
|
|||||||
|
|
||||||
package com.formdev.flatlaf.ui;
|
package com.formdev.flatlaf.ui;
|
||||||
|
|
||||||
import static com.formdev.flatlaf.util.UIScale.scale;
|
|
||||||
import java.awt.Color;
|
import java.awt.Color;
|
||||||
import java.awt.Component;
|
import java.awt.Component;
|
||||||
import java.awt.GradientPaint;
|
import java.awt.GradientPaint;
|
||||||
import java.awt.Graphics;
|
import java.awt.Graphics;
|
||||||
import java.awt.Insets;
|
import java.awt.Insets;
|
||||||
import java.awt.Paint;
|
import java.awt.Paint;
|
||||||
import javax.swing.JButton;
|
import javax.swing.AbstractButton;
|
||||||
import javax.swing.UIManager;
|
import javax.swing.UIManager;
|
||||||
import javax.swing.plaf.UIResource;
|
import javax.swing.plaf.UIResource;
|
||||||
|
import com.formdev.flatlaf.util.UIScale;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Border for {@link javax.swing.JButton}.
|
* Border for {@link javax.swing.JButton}.
|
||||||
@@ -43,6 +43,8 @@ import javax.swing.plaf.UIResource;
|
|||||||
* @uiDefault Button.default.focusedBorderColor Color
|
* @uiDefault Button.default.focusedBorderColor Color
|
||||||
* @uiDefault Button.default.focusColor Color
|
* @uiDefault Button.default.focusColor Color
|
||||||
* @uiDefault Button.default.borderWidth int
|
* @uiDefault Button.default.borderWidth int
|
||||||
|
* @uiDefault Button.toolbar.margin Insets
|
||||||
|
* @uiDefault Button.toolbar.spacingInsets Insets
|
||||||
* @uiDefault Button.arc int
|
* @uiDefault Button.arc int
|
||||||
*
|
*
|
||||||
* @author Karl Tauber
|
* @author Karl Tauber
|
||||||
@@ -61,12 +63,17 @@ public class FlatButtonBorder
|
|||||||
protected final Color defaultFocusedBorderColor = UIManager.getColor( "Button.default.focusedBorderColor" );
|
protected final Color defaultFocusedBorderColor = UIManager.getColor( "Button.default.focusedBorderColor" );
|
||||||
protected final Color defaultFocusColor = UIManager.getColor( "Button.default.focusColor" );
|
protected final Color defaultFocusColor = UIManager.getColor( "Button.default.focusColor" );
|
||||||
protected final int defaultBorderWidth = UIManager.getInt( "Button.default.borderWidth" );
|
protected final int defaultBorderWidth = UIManager.getInt( "Button.default.borderWidth" );
|
||||||
|
protected final Insets toolbarMargin = UIManager.getInsets( "Button.toolbar.margin" );
|
||||||
|
protected final Insets toolbarSpacingInsets = UIManager.getInsets( "Button.toolbar.spacingInsets" );
|
||||||
protected final int arc = UIManager.getInt( "Button.arc" );
|
protected final int arc = UIManager.getInt( "Button.arc" );
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void paintBorder( Component c, Graphics g, int x, int y, int width, int height ) {
|
public void paintBorder( Component c, Graphics g, int x, int y, int width, int height ) {
|
||||||
if( FlatButtonUI.isContentAreaFilled( c ) && !FlatButtonUI.isHelpButton( c ) )
|
if( FlatButtonUI.isContentAreaFilled( c ) &&
|
||||||
super.paintBorder( c, g, x, y, width, height );
|
!FlatButtonUI.isToolBarButton( c ) &&
|
||||||
|
!FlatButtonUI.isHelpButton( c ) &&
|
||||||
|
!FlatToggleButtonUI.isTabButton( c ) )
|
||||||
|
super.paintBorder( c, g, x, y, width, height );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -95,22 +102,42 @@ public class FlatButtonBorder
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Insets getBorderInsets( Component c, Insets insets ) {
|
public Insets getBorderInsets( Component c, Insets insets ) {
|
||||||
insets = super.getBorderInsets( c, insets );
|
if( FlatButtonUI.isToolBarButton( c ) ) {
|
||||||
|
// In toolbars, use button margin only if explicitly set.
|
||||||
|
// Otherwise use toolbar margin specified in UI defaults.
|
||||||
|
Insets margin = (c instanceof AbstractButton)
|
||||||
|
? ((AbstractButton)c).getMargin()
|
||||||
|
: null;
|
||||||
|
|
||||||
// use smaller left and right insets for icon-only buttons (so that they are square)
|
FlatUIUtils.setInsets( insets, UIScale.scale( FlatUIUtils.addInsets( toolbarSpacingInsets,
|
||||||
if( FlatButtonUI.isIconOnlyButton( c ) && ((JButton)c).getMargin() instanceof UIResource )
|
(margin != null && !(margin instanceof UIResource)) ? margin : toolbarMargin ) ) );
|
||||||
insets.left = insets.right = Math.min( insets.top, insets.bottom );
|
} else {
|
||||||
|
insets = super.getBorderInsets( c, insets );
|
||||||
|
|
||||||
|
// use smaller left and right insets for icon-only or single-character buttons (so that they are square)
|
||||||
|
if( FlatButtonUI.isIconOnlyOrSingleCharacterButton( c ) && ((AbstractButton)c).getMargin() instanceof UIResource )
|
||||||
|
insets.left = insets.right = Math.min( insets.top, insets.bottom );
|
||||||
|
}
|
||||||
|
|
||||||
return insets;
|
return insets;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected float getBorderWidth( Component c ) {
|
protected int getFocusWidth( Component c ) {
|
||||||
return FlatButtonUI.isDefaultButton( c ) ? scale( (float) defaultBorderWidth ) : super.getBorderWidth( c );
|
return FlatToggleButtonUI.isTabButton( c ) ? 0 : super.getFocusWidth( c );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected float getArc() {
|
protected int getBorderWidth( Component c ) {
|
||||||
return scale( (float) arc );
|
return FlatButtonUI.isDefaultButton( c ) ? defaultBorderWidth : super.getBorderWidth( c );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected int getArc( Component c ) {
|
||||||
|
switch( FlatButtonUI.getButtonType( c ) ) {
|
||||||
|
case FlatButtonUI.TYPE_SQUARE: return 0;
|
||||||
|
case FlatButtonUI.TYPE_ROUND_RECT: return Short.MAX_VALUE;
|
||||||
|
default: return arc;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,19 +26,22 @@ import java.awt.FontMetrics;
|
|||||||
import java.awt.GradientPaint;
|
import java.awt.GradientPaint;
|
||||||
import java.awt.Graphics;
|
import java.awt.Graphics;
|
||||||
import java.awt.Graphics2D;
|
import java.awt.Graphics2D;
|
||||||
|
import java.awt.Insets;
|
||||||
import java.awt.Rectangle;
|
import java.awt.Rectangle;
|
||||||
import java.awt.geom.RoundRectangle2D;
|
import java.awt.geom.RoundRectangle2D;
|
||||||
|
import java.beans.PropertyChangeEvent;
|
||||||
import javax.swing.AbstractButton;
|
import javax.swing.AbstractButton;
|
||||||
import javax.swing.ButtonModel;
|
import javax.swing.ButtonModel;
|
||||||
import javax.swing.Icon;
|
import javax.swing.Icon;
|
||||||
import javax.swing.JButton;
|
import javax.swing.JButton;
|
||||||
import javax.swing.JComponent;
|
import javax.swing.JComponent;
|
||||||
|
import javax.swing.JToggleButton;
|
||||||
import javax.swing.JToolBar;
|
import javax.swing.JToolBar;
|
||||||
import javax.swing.LookAndFeel;
|
import javax.swing.LookAndFeel;
|
||||||
import javax.swing.UIManager;
|
import javax.swing.UIManager;
|
||||||
import javax.swing.border.Border;
|
|
||||||
import javax.swing.plaf.ComponentUI;
|
import javax.swing.plaf.ComponentUI;
|
||||||
import javax.swing.plaf.UIResource;
|
import javax.swing.plaf.UIResource;
|
||||||
|
import javax.swing.plaf.basic.BasicButtonListener;
|
||||||
import javax.swing.plaf.basic.BasicButtonUI;
|
import javax.swing.plaf.basic.BasicButtonUI;
|
||||||
import com.formdev.flatlaf.FlatLaf;
|
import com.formdev.flatlaf.FlatLaf;
|
||||||
import com.formdev.flatlaf.util.UIScale;
|
import com.formdev.flatlaf.util.UIScale;
|
||||||
@@ -57,8 +60,6 @@ import com.formdev.flatlaf.util.UIScale;
|
|||||||
*
|
*
|
||||||
* <!-- FlatButtonUI -->
|
* <!-- FlatButtonUI -->
|
||||||
*
|
*
|
||||||
* @uiDefault Component.focusWidth int
|
|
||||||
* @uiDefault Button.arc int
|
|
||||||
* @uiDefault Button.minimumWidth int
|
* @uiDefault Button.minimumWidth int
|
||||||
* @uiDefault Button.iconTextGap int
|
* @uiDefault Button.iconTextGap int
|
||||||
* @uiDefault Button.startBackground Color optional; if set, a gradient paint is used and Button.background is ignored
|
* @uiDefault Button.startBackground Color optional; if set, a gradient paint is used and Button.background is ignored
|
||||||
@@ -79,6 +80,7 @@ import com.formdev.flatlaf.util.UIScale;
|
|||||||
* @uiDefault Button.shadowWidth int default is 2
|
* @uiDefault Button.shadowWidth int default is 2
|
||||||
* @uiDefault Button.shadowColor Color optional
|
* @uiDefault Button.shadowColor Color optional
|
||||||
* @uiDefault Button.default.shadowColor Color optional
|
* @uiDefault Button.default.shadowColor Color optional
|
||||||
|
* @uiDefault Button.toolbar.spacingInsets Insets
|
||||||
* @uiDefault Button.toolbar.hoverBackground Color
|
* @uiDefault Button.toolbar.hoverBackground Color
|
||||||
* @uiDefault Button.toolbar.pressedBackground Color
|
* @uiDefault Button.toolbar.pressedBackground Color
|
||||||
*
|
*
|
||||||
@@ -87,8 +89,6 @@ import com.formdev.flatlaf.util.UIScale;
|
|||||||
public class FlatButtonUI
|
public class FlatButtonUI
|
||||||
extends BasicButtonUI
|
extends BasicButtonUI
|
||||||
{
|
{
|
||||||
protected int focusWidth;
|
|
||||||
protected int arc;
|
|
||||||
protected int minimumWidth;
|
protected int minimumWidth;
|
||||||
protected int iconTextGap;
|
protected int iconTextGap;
|
||||||
|
|
||||||
@@ -111,6 +111,7 @@ public class FlatButtonUI
|
|||||||
protected Color shadowColor;
|
protected Color shadowColor;
|
||||||
protected Color defaultShadowColor;
|
protected Color defaultShadowColor;
|
||||||
|
|
||||||
|
protected Insets toolbarSpacingInsets;
|
||||||
protected Color toolbarHoverBackground;
|
protected Color toolbarHoverBackground;
|
||||||
protected Color toolbarPressedBackground;
|
protected Color toolbarPressedBackground;
|
||||||
|
|
||||||
@@ -133,8 +134,6 @@ public class FlatButtonUI
|
|||||||
if( !defaults_initialized ) {
|
if( !defaults_initialized ) {
|
||||||
String prefix = getPropertyPrefix();
|
String prefix = getPropertyPrefix();
|
||||||
|
|
||||||
focusWidth = UIManager.getInt( "Component.focusWidth" );
|
|
||||||
arc = UIManager.getInt( "Button.arc" );
|
|
||||||
minimumWidth = UIManager.getInt( prefix + "minimumWidth" );
|
minimumWidth = UIManager.getInt( prefix + "minimumWidth" );
|
||||||
iconTextGap = FlatUIUtils.getUIInt( prefix + "iconTextGap", 4 );
|
iconTextGap = FlatUIUtils.getUIInt( prefix + "iconTextGap", 4 );
|
||||||
|
|
||||||
@@ -149,6 +148,10 @@ public class FlatButtonUI
|
|||||||
shadowWidth = FlatUIUtils.getUIInt( "Button.shadowWidth", 2 );
|
shadowWidth = FlatUIUtils.getUIInt( "Button.shadowWidth", 2 );
|
||||||
shadowColor = UIManager.getColor( "Button.shadowColor" );
|
shadowColor = UIManager.getColor( "Button.shadowColor" );
|
||||||
defaultShadowColor = UIManager.getColor( "Button.default.shadowColor" );
|
defaultShadowColor = UIManager.getColor( "Button.default.shadowColor" );
|
||||||
|
} else {
|
||||||
|
shadowWidth = 0;
|
||||||
|
shadowColor = null;
|
||||||
|
defaultShadowColor = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
defaultBackground = FlatUIUtils.getUIColor( "Button.default.startBackground", "Button.default.background" );
|
defaultBackground = FlatUIUtils.getUIColor( "Button.default.startBackground", "Button.default.background" );
|
||||||
@@ -159,6 +162,7 @@ public class FlatButtonUI
|
|||||||
defaultPressedBackground = UIManager.getColor( "Button.default.pressedBackground" );
|
defaultPressedBackground = UIManager.getColor( "Button.default.pressedBackground" );
|
||||||
defaultBoldText = UIManager.getBoolean( "Button.default.boldText" );
|
defaultBoldText = UIManager.getBoolean( "Button.default.boldText" );
|
||||||
|
|
||||||
|
toolbarSpacingInsets = UIManager.getInsets( "Button.toolbar.spacingInsets" );
|
||||||
toolbarHoverBackground = UIManager.getColor( prefix + "toolbar.hoverBackground" );
|
toolbarHoverBackground = UIManager.getColor( prefix + "toolbar.hoverBackground" );
|
||||||
toolbarPressedBackground = UIManager.getColor( prefix + "toolbar.pressedBackground" );
|
toolbarPressedBackground = UIManager.getColor( prefix + "toolbar.pressedBackground" );
|
||||||
|
|
||||||
@@ -176,7 +180,7 @@ public class FlatButtonUI
|
|||||||
LookAndFeel.installProperty( b, "opaque", false );
|
LookAndFeel.installProperty( b, "opaque", false );
|
||||||
LookAndFeel.installProperty( b, "iconTextGap", scale( iconTextGap ) );
|
LookAndFeel.installProperty( b, "iconTextGap", scale( iconTextGap ) );
|
||||||
|
|
||||||
MigLayoutVisualPadding.install( b, focusWidth );
|
MigLayoutVisualPadding.install( b );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -187,6 +191,31 @@ public class FlatButtonUI
|
|||||||
defaults_initialized = false;
|
defaults_initialized = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected BasicButtonListener createButtonListener( AbstractButton b ) {
|
||||||
|
return new BasicButtonListener( b ) {
|
||||||
|
@Override
|
||||||
|
public void propertyChange( PropertyChangeEvent e ) {
|
||||||
|
super.propertyChange( e );
|
||||||
|
FlatButtonUI.this.propertyChange( b, e );
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void propertyChange( AbstractButton b, PropertyChangeEvent e ) {
|
||||||
|
switch( e.getPropertyName() ) {
|
||||||
|
case MINIMUM_WIDTH:
|
||||||
|
case MINIMUM_HEIGHT:
|
||||||
|
b.revalidate();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case BUTTON_TYPE:
|
||||||
|
b.revalidate();
|
||||||
|
b.repaint();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static boolean isContentAreaFilled( Component c ) {
|
static boolean isContentAreaFilled( Component c ) {
|
||||||
return !(c instanceof AbstractButton) || ((AbstractButton)c).isContentAreaFilled();
|
return !(c instanceof AbstractButton) || ((AbstractButton)c).isContentAreaFilled();
|
||||||
}
|
}
|
||||||
@@ -195,22 +224,38 @@ public class FlatButtonUI
|
|||||||
return c instanceof JButton && ((JButton)c).isDefaultButton();
|
return c instanceof JButton && ((JButton)c).isDefaultButton();
|
||||||
}
|
}
|
||||||
|
|
||||||
static boolean isIconOnlyButton( Component c ) {
|
/**
|
||||||
if( !(c instanceof JButton) )
|
* Returns true if the button has an icon but no text,
|
||||||
|
* or it it does not have an icon and the text is either "..." or one character.
|
||||||
|
*/
|
||||||
|
static boolean isIconOnlyOrSingleCharacterButton( Component c ) {
|
||||||
|
if( !(c instanceof JButton) && !(c instanceof JToggleButton) )
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
Icon icon = ((JButton)c).getIcon();
|
Icon icon = ((AbstractButton)c).getIcon();
|
||||||
String text = ((JButton)c).getText();
|
String text = ((AbstractButton)c).getText();
|
||||||
return (icon != null && (text == null || text.isEmpty())) ||
|
return (icon != null && (text == null || text.isEmpty())) ||
|
||||||
(icon == null && text != null && ("...".equals( text ) || text.length() == 1));
|
(icon == null && text != null && ("...".equals( text ) || text.length() == 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// same indices as in parameters to clientPropertyChoice()
|
||||||
|
static final int TYPE_OTHER = -1;
|
||||||
|
static final int TYPE_SQUARE = 0;
|
||||||
|
static final int TYPE_ROUND_RECT = 1;
|
||||||
|
|
||||||
|
static int getButtonType( Component c ) {
|
||||||
|
return (c instanceof AbstractButton)
|
||||||
|
? clientPropertyChoice( (AbstractButton) c, BUTTON_TYPE, BUTTON_TYPE_SQUARE, BUTTON_TYPE_ROUND_RECT )
|
||||||
|
: TYPE_OTHER;
|
||||||
|
}
|
||||||
|
|
||||||
static boolean isHelpButton( Component c ) {
|
static boolean isHelpButton( Component c ) {
|
||||||
return c instanceof JButton && clientPropertyEquals( (JButton) c, BUTTON_TYPE, BUTTON_TYPE_HELP );
|
return c instanceof JButton && clientPropertyEquals( (JButton) c, BUTTON_TYPE, BUTTON_TYPE_HELP );
|
||||||
}
|
}
|
||||||
|
|
||||||
static boolean isToolBarButton( JComponent c ) {
|
static boolean isToolBarButton( Component c ) {
|
||||||
return c.getParent() instanceof JToolBar;
|
return c.getParent() instanceof JToolBar ||
|
||||||
|
(c instanceof AbstractButton && clientPropertyEquals( (AbstractButton) c, BUTTON_TYPE, BUTTON_TYPE_TOOLBAR_BUTTON ));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -224,43 +269,63 @@ public class FlatButtonUI
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if( isContentAreaFilled( c ) ) {
|
if( isContentAreaFilled( c ) )
|
||||||
Color background = getBackground( c );
|
paintBackground( g, c );
|
||||||
if( background != null ) {
|
|
||||||
Graphics2D g2 = (Graphics2D) g.create();
|
|
||||||
try {
|
|
||||||
FlatUIUtils.setRenderingHints( g2 );
|
|
||||||
|
|
||||||
Border border = c.getBorder();
|
|
||||||
float focusWidth = (border instanceof FlatBorder) ? scale( (float) this.focusWidth ) : 0;
|
|
||||||
float arc = (border instanceof FlatButtonBorder || isToolBarButton( c )) ? scale( (float) this.arc ) : 0;
|
|
||||||
boolean def = isDefaultButton( c );
|
|
||||||
|
|
||||||
// paint shadow
|
|
||||||
Color shadowColor = def ? defaultShadowColor : this.shadowColor;
|
|
||||||
if( shadowColor != null && shadowWidth > 0 && focusWidth > 0 && !c.hasFocus() && c.isEnabled() ) {
|
|
||||||
g2.setColor( shadowColor );
|
|
||||||
g2.fill( new RoundRectangle2D.Float( focusWidth, focusWidth + UIScale.scale( (float) shadowWidth ),
|
|
||||||
c.getWidth() - focusWidth * 2, c.getHeight() - focusWidth * 2, arc, arc ) );
|
|
||||||
}
|
|
||||||
|
|
||||||
// paint background
|
|
||||||
Color startBg = def ? defaultBackground : startBackground;
|
|
||||||
Color endBg = def ? defaultEndBackground : endBackground;
|
|
||||||
if( background == startBg && endBg != null && !startBg.equals( endBg ) )
|
|
||||||
g2.setPaint( new GradientPaint( 0, 0, startBg, 0, c.getHeight(), endBg ) );
|
|
||||||
else
|
|
||||||
FlatUIUtils.setColor( g2, background, def ? defaultBackground : c.getBackground() );
|
|
||||||
FlatUIUtils.paintComponentBackground( g2, 0, 0, c.getWidth(), c.getHeight(), focusWidth, arc );
|
|
||||||
} finally {
|
|
||||||
g2.dispose();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
paint( g, c );
|
paint( g, c );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void paintBackground( Graphics g, JComponent c ) {
|
||||||
|
Color background = getBackground( c );
|
||||||
|
if( background != null ) {
|
||||||
|
Graphics2D g2 = (Graphics2D) g.create();
|
||||||
|
try {
|
||||||
|
FlatUIUtils.setRenderingHints( g2 );
|
||||||
|
|
||||||
|
boolean isToolBarButton = isToolBarButton( c );
|
||||||
|
float focusWidth = isToolBarButton ? 0 : FlatUIUtils.getBorderFocusWidth( c );
|
||||||
|
float arc = FlatUIUtils.getBorderArc( c );
|
||||||
|
|
||||||
|
boolean def = isDefaultButton( c );
|
||||||
|
|
||||||
|
int x = 0;
|
||||||
|
int y = 0;
|
||||||
|
int width = c.getWidth();
|
||||||
|
int height = c.getHeight();
|
||||||
|
|
||||||
|
if( isToolBarButton ) {
|
||||||
|
Insets spacing = UIScale.scale( toolbarSpacingInsets );
|
||||||
|
x += spacing.left;
|
||||||
|
y += spacing.top;
|
||||||
|
width -= spacing.left + spacing.right;
|
||||||
|
height -= spacing.top + spacing.bottom;
|
||||||
|
}
|
||||||
|
|
||||||
|
// paint shadow
|
||||||
|
Color shadowColor = def ? defaultShadowColor : this.shadowColor;
|
||||||
|
if( !isToolBarButton && shadowColor != null && shadowWidth > 0 && focusWidth > 0 &&
|
||||||
|
!FlatUIUtils.isPermanentFocusOwner( c ) && c.isEnabled() )
|
||||||
|
{
|
||||||
|
g2.setColor( shadowColor );
|
||||||
|
g2.fill( new RoundRectangle2D.Float( focusWidth, focusWidth + UIScale.scale( (float) shadowWidth ),
|
||||||
|
width - focusWidth * 2, height - focusWidth * 2, arc, arc ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
// paint background
|
||||||
|
Color startBg = def ? defaultBackground : startBackground;
|
||||||
|
Color endBg = def ? defaultEndBackground : endBackground;
|
||||||
|
if( background == startBg && endBg != null && !startBg.equals( endBg ) )
|
||||||
|
g2.setPaint( new GradientPaint( 0, 0, startBg, 0, height, endBg ) );
|
||||||
|
else
|
||||||
|
g2.setColor( FlatUIUtils.deriveColor( background, def ? defaultBackground : c.getBackground() ) );
|
||||||
|
|
||||||
|
FlatUIUtils.paintComponentBackground( g2, x, y, width, height, focusWidth, arc );
|
||||||
|
} finally {
|
||||||
|
g2.dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void paintText( Graphics g, AbstractButton b, Rectangle textRect, String text ) {
|
protected void paintText( Graphics g, AbstractButton b, Rectangle textRect, String text ) {
|
||||||
if( isHelpButton( b ) )
|
if( isHelpButton( b ) )
|
||||||
@@ -328,7 +393,7 @@ public class FlatButtonUI
|
|||||||
if( hoverColor != null && b != null && b.getModel().isRollover() )
|
if( hoverColor != null && b != null && b.getModel().isRollover() )
|
||||||
return hoverColor;
|
return hoverColor;
|
||||||
|
|
||||||
if( focusedColor != null && c.hasFocus() )
|
if( focusedColor != null && FlatUIUtils.isPermanentFocusOwner( c ) )
|
||||||
return focusedColor;
|
return focusedColor;
|
||||||
|
|
||||||
return enabledColor;
|
return enabledColor;
|
||||||
@@ -345,13 +410,20 @@ public class FlatButtonUI
|
|||||||
return new Dimension( helpButtonIcon.getIconWidth(), helpButtonIcon.getIconHeight() );
|
return new Dimension( helpButtonIcon.getIconWidth(), helpButtonIcon.getIconHeight() );
|
||||||
|
|
||||||
Dimension prefSize = super.getPreferredSize( c );
|
Dimension prefSize = super.getPreferredSize( c );
|
||||||
|
if( prefSize == null )
|
||||||
|
return null;
|
||||||
|
|
||||||
// make button square if it is a icon-only button
|
// make button square if it is a single-character button
|
||||||
// or apply minimum width, if not in toolbar and not a icon-only button
|
// or apply minimum width, if not in toolbar and not a icon-only or single-character button
|
||||||
if( isIconOnlyButton( c ) )
|
if( isIconOnlyOrSingleCharacterButton( c ) ) {
|
||||||
prefSize.width = Math.max( prefSize.width, prefSize.height );
|
// make only single-character buttons square to allow non-square icon-only buttons
|
||||||
else if( !isToolBarButton( c ) )
|
if( ((AbstractButton)c).getIcon() == null )
|
||||||
prefSize.width = Math.max( prefSize.width, scale( minimumWidth + (focusWidth * 2) ) );
|
prefSize.width = Math.max( prefSize.width, prefSize.height );
|
||||||
|
} else if( !isToolBarButton( c ) && c.getBorder() instanceof FlatButtonBorder ) {
|
||||||
|
float focusWidth = FlatUIUtils.getBorderFocusWidth( c );
|
||||||
|
prefSize.width = Math.max( prefSize.width, scale( FlatUIUtils.minimumWidth( c, minimumWidth ) ) + Math.round( focusWidth * 2 ) );
|
||||||
|
prefSize.height = Math.max( prefSize.height, scale( FlatUIUtils.minimumHeight( c, 0 ) ) + Math.round( focusWidth * 2 ) );
|
||||||
|
}
|
||||||
|
|
||||||
return prefSize;
|
return prefSize;
|
||||||
}
|
}
|
||||||
|
|||||||
128
flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatCaret.java
Normal file
128
flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatCaret.java
Normal file
@@ -0,0 +1,128 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2020 FormDev Software GmbH
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.formdev.flatlaf.ui;
|
||||||
|
|
||||||
|
import static com.formdev.flatlaf.FlatClientProperties.*;
|
||||||
|
import java.awt.EventQueue;
|
||||||
|
import java.awt.event.FocusEvent;
|
||||||
|
import java.awt.event.MouseEvent;
|
||||||
|
import javax.swing.JFormattedTextField;
|
||||||
|
import javax.swing.plaf.UIResource;
|
||||||
|
import javax.swing.text.DefaultCaret;
|
||||||
|
import javax.swing.text.Document;
|
||||||
|
import javax.swing.text.JTextComponent;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Caret that can select all text on focus gained.
|
||||||
|
*
|
||||||
|
* @author Karl Tauber
|
||||||
|
*/
|
||||||
|
class FlatCaret
|
||||||
|
extends DefaultCaret
|
||||||
|
implements UIResource
|
||||||
|
{
|
||||||
|
private final String selectAllOnFocusPolicy;
|
||||||
|
|
||||||
|
private boolean wasFocused;
|
||||||
|
private boolean wasTemporaryLost;
|
||||||
|
private boolean isMousePressed;
|
||||||
|
|
||||||
|
FlatCaret( String selectAllOnFocusPolicy ) {
|
||||||
|
this.selectAllOnFocusPolicy = selectAllOnFocusPolicy;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void install( JTextComponent c ) {
|
||||||
|
super.install( c );
|
||||||
|
|
||||||
|
// the dot and mark are lost when switching LaF
|
||||||
|
// --> move dot to end of text so that all text may be selected when it gains focus
|
||||||
|
Document doc = c.getDocument();
|
||||||
|
if( doc != null && getDot() == 0 && getMark() == 0 ) {
|
||||||
|
int length = doc.getLength();
|
||||||
|
if( length > 0 )
|
||||||
|
setDot( length );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void focusGained( FocusEvent e ) {
|
||||||
|
if( !wasTemporaryLost && !isMousePressed )
|
||||||
|
selectAllOnFocusGained();
|
||||||
|
wasTemporaryLost = false;
|
||||||
|
wasFocused = true;
|
||||||
|
|
||||||
|
super.focusGained( e );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void focusLost( FocusEvent e ) {
|
||||||
|
wasTemporaryLost = e.isTemporary();
|
||||||
|
super.focusLost( e );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void mousePressed( MouseEvent e ) {
|
||||||
|
isMousePressed = true;
|
||||||
|
super.mousePressed( e );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void mouseReleased( MouseEvent e ) {
|
||||||
|
isMousePressed = false;
|
||||||
|
super.mouseReleased( e );
|
||||||
|
}
|
||||||
|
|
||||||
|
private void selectAllOnFocusGained() {
|
||||||
|
JTextComponent c = getComponent();
|
||||||
|
Document doc = c.getDocument();
|
||||||
|
if( doc == null || !c.isEnabled() || !c.isEditable() )
|
||||||
|
return;
|
||||||
|
|
||||||
|
Object selectAllOnFocusPolicy = c.getClientProperty( SELECT_ALL_ON_FOCUS_POLICY );
|
||||||
|
if( selectAllOnFocusPolicy == null )
|
||||||
|
selectAllOnFocusPolicy = this.selectAllOnFocusPolicy;
|
||||||
|
|
||||||
|
if( SELECT_ALL_ON_FOCUS_POLICY_NEVER.equals( selectAllOnFocusPolicy ) )
|
||||||
|
return;
|
||||||
|
|
||||||
|
if( !SELECT_ALL_ON_FOCUS_POLICY_ALWAYS.equals( selectAllOnFocusPolicy ) ) {
|
||||||
|
// policy is "once" (or null or unknown)
|
||||||
|
|
||||||
|
// was already focused?
|
||||||
|
if( wasFocused )
|
||||||
|
return;
|
||||||
|
|
||||||
|
// check whether selection was modified before gaining focus
|
||||||
|
int dot = getDot();
|
||||||
|
int mark = getMark();
|
||||||
|
if( dot != mark || dot != doc.getLength() )
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// select all
|
||||||
|
if( c instanceof JFormattedTextField ) {
|
||||||
|
EventQueue.invokeLater( () -> {
|
||||||
|
setDot( 0 );
|
||||||
|
moveDot( doc.getLength() );
|
||||||
|
} );
|
||||||
|
} else {
|
||||||
|
setDot( 0 );
|
||||||
|
moveDot( doc.getLength() );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -16,9 +16,11 @@
|
|||||||
|
|
||||||
package com.formdev.flatlaf.ui;
|
package com.formdev.flatlaf.ui;
|
||||||
|
|
||||||
import static com.formdev.flatlaf.util.UIScale.scale;
|
import java.awt.Dimension;
|
||||||
import java.beans.PropertyChangeListener;
|
import java.awt.Graphics;
|
||||||
|
import javax.swing.Icon;
|
||||||
import javax.swing.JComponent;
|
import javax.swing.JComponent;
|
||||||
|
import javax.swing.LookAndFeel;
|
||||||
import javax.swing.plaf.ComponentUI;
|
import javax.swing.plaf.ComponentUI;
|
||||||
import javax.swing.plaf.basic.BasicCheckBoxMenuItemUI;
|
import javax.swing.plaf.basic.BasicCheckBoxMenuItemUI;
|
||||||
|
|
||||||
@@ -27,29 +29,34 @@ import javax.swing.plaf.basic.BasicCheckBoxMenuItemUI;
|
|||||||
*
|
*
|
||||||
* <!-- BasicCheckBoxMenuItemUI -->
|
* <!-- BasicCheckBoxMenuItemUI -->
|
||||||
*
|
*
|
||||||
* @uiDefault CheckBoxMenuItem.font Font
|
* @uiDefault CheckBoxMenuItem.font Font
|
||||||
* @uiDefault CheckBoxMenuItem.background Color
|
* @uiDefault CheckBoxMenuItem.background Color
|
||||||
* @uiDefault CheckBoxMenuItem.foreground Color
|
* @uiDefault CheckBoxMenuItem.foreground Color
|
||||||
* @uiDefault CheckBoxMenuItem.disabledForeground Color
|
* @uiDefault CheckBoxMenuItem.disabledForeground Color
|
||||||
* @uiDefault CheckBoxMenuItem.selectionBackground Color
|
* @uiDefault CheckBoxMenuItem.selectionBackground Color
|
||||||
* @uiDefault CheckBoxMenuItem.selectionForeground Color
|
* @uiDefault CheckBoxMenuItem.selectionForeground Color
|
||||||
* @uiDefault CheckBoxMenuItem.acceleratorForeground Color
|
* @uiDefault CheckBoxMenuItem.acceleratorForeground Color
|
||||||
* @uiDefault CheckBoxMenuItem.acceleratorSelectionForeground Color
|
* @uiDefault CheckBoxMenuItem.acceleratorSelectionForeground Color
|
||||||
* @uiDefault MenuItem.acceleratorFont Font defaults to MenuItem.font
|
* @uiDefault MenuItem.acceleratorFont Font defaults to MenuItem.font
|
||||||
* @uiDefault MenuItem.acceleratorDelimiter String
|
* @uiDefault MenuItem.acceleratorDelimiter String
|
||||||
* @uiDefault CheckBoxMenuItem.border Border
|
* @uiDefault CheckBoxMenuItem.border Border
|
||||||
* @uiDefault CheckBoxMenuItem.borderPainted boolean
|
* @uiDefault CheckBoxMenuItem.borderPainted boolean
|
||||||
* @uiDefault CheckBoxMenuItem.margin Insets
|
* @uiDefault CheckBoxMenuItem.margin Insets
|
||||||
* @uiDefault CheckBoxMenuItem.arrowIcon Icon
|
* @uiDefault CheckBoxMenuItem.arrowIcon Icon
|
||||||
* @uiDefault CheckBoxMenuItem.checkIcon Icon
|
* @uiDefault CheckBoxMenuItem.checkIcon Icon
|
||||||
* @uiDefault CheckBoxMenuItem.opaque boolean
|
* @uiDefault CheckBoxMenuItem.opaque boolean
|
||||||
* @uiDefault CheckBoxMenuItem.evenHeight boolean
|
*
|
||||||
|
* <!-- FlatCheckBoxMenuItemUI -->
|
||||||
|
*
|
||||||
|
* @uiDefault MenuItem.iconTextGap int
|
||||||
*
|
*
|
||||||
* @author Karl Tauber
|
* @author Karl Tauber
|
||||||
*/
|
*/
|
||||||
public class FlatCheckBoxMenuItemUI
|
public class FlatCheckBoxMenuItemUI
|
||||||
extends BasicCheckBoxMenuItemUI
|
extends BasicCheckBoxMenuItemUI
|
||||||
{
|
{
|
||||||
|
private FlatMenuItemRenderer renderer;
|
||||||
|
|
||||||
public static ComponentUI createUI( JComponent c ) {
|
public static ComponentUI createUI( JComponent c ) {
|
||||||
return new FlatCheckBoxMenuItemUI();
|
return new FlatCheckBoxMenuItemUI();
|
||||||
}
|
}
|
||||||
@@ -58,20 +65,30 @@ public class FlatCheckBoxMenuItemUI
|
|||||||
protected void installDefaults() {
|
protected void installDefaults() {
|
||||||
super.installDefaults();
|
super.installDefaults();
|
||||||
|
|
||||||
// scale
|
LookAndFeel.installProperty( menuItem, "iconTextGap", FlatUIUtils.getUIInt( "MenuItem.iconTextGap", 4 ) );
|
||||||
defaultTextIconGap = scale( defaultTextIconGap );
|
|
||||||
|
renderer = createRenderer();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Scale defaultTextIconGap again if iconTextGap property has changed.
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
protected PropertyChangeListener createPropertyChangeListener( JComponent c ) {
|
protected void uninstallDefaults() {
|
||||||
PropertyChangeListener superListener = super.createPropertyChangeListener( c );
|
super.uninstallDefaults();
|
||||||
return e -> {
|
|
||||||
superListener.propertyChange( e );
|
renderer = null;
|
||||||
if( e.getPropertyName() == "iconTextGap" )
|
}
|
||||||
defaultTextIconGap = scale( defaultTextIconGap );
|
|
||||||
};
|
protected FlatMenuItemRenderer createRenderer() {
|
||||||
|
return new FlatMenuItemRenderer( menuItem, checkIcon, arrowIcon, acceleratorFont, acceleratorDelimiter );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Dimension getPreferredMenuItemSize( JComponent c, Icon checkIcon, Icon arrowIcon, int defaultTextIconGap ) {
|
||||||
|
return renderer.getPreferredMenuItemSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void paint( Graphics g, JComponent c ) {
|
||||||
|
renderer.paintMenuItem( g, selectionBackground, selectionForeground, disabledForeground,
|
||||||
|
acceleratorForeground, acceleratorSelectionForeground );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,12 +16,9 @@
|
|||||||
|
|
||||||
package com.formdev.flatlaf.ui;
|
package com.formdev.flatlaf.ui;
|
||||||
|
|
||||||
import java.awt.Dimension;
|
|
||||||
import javax.swing.JComponent;
|
import javax.swing.JComponent;
|
||||||
import javax.swing.UIManager;
|
|
||||||
import javax.swing.plaf.ComponentUI;
|
import javax.swing.plaf.ComponentUI;
|
||||||
import javax.swing.plaf.basic.BasicColorChooserUI;
|
import javax.swing.plaf.basic.BasicColorChooserUI;
|
||||||
import com.formdev.flatlaf.util.UIScale;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides the Flat LaF UI delegate for {@link javax.swing.JColorChooser}.
|
* Provides the Flat LaF UI delegate for {@link javax.swing.JColorChooser}.
|
||||||
@@ -43,21 +40,4 @@ public class FlatColorChooserUI
|
|||||||
public static ComponentUI createUI( JComponent c ) {
|
public static ComponentUI createUI( JComponent c ) {
|
||||||
return new FlatColorChooserUI();
|
return new FlatColorChooserUI();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void installUI( JComponent c ) {
|
|
||||||
if( UIScale.getUserScaleFactor() != 1f ) {
|
|
||||||
// temporary scale swatch sizes
|
|
||||||
Dimension swatchSize = UIManager.getDimension( "ColorChooser.swatchesSwatchSize" );
|
|
||||||
Dimension swatchSize2 = UIManager.getDimension( "ColorChooser.swatchesRecentSwatchSize" );
|
|
||||||
UIManager.put( "ColorChooser.swatchesSwatchSize", UIScale.scale( swatchSize ) );
|
|
||||||
UIManager.put( "ColorChooser.swatchesRecentSwatchSize", UIScale.scale( swatchSize2 ) );
|
|
||||||
|
|
||||||
super.installUI( c );
|
|
||||||
|
|
||||||
UIManager.put( "ColorChooser.swatchesSwatchSize", null );
|
|
||||||
UIManager.put( "ColorChooser.swatchesRecentSwatchSize", null );
|
|
||||||
} else
|
|
||||||
super.installUI( c );
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,6 +28,8 @@ import java.awt.Insets;
|
|||||||
import java.awt.LayoutManager;
|
import java.awt.LayoutManager;
|
||||||
import java.awt.Rectangle;
|
import java.awt.Rectangle;
|
||||||
import java.awt.Shape;
|
import java.awt.Shape;
|
||||||
|
import java.awt.event.ActionEvent;
|
||||||
|
import java.awt.event.ActionListener;
|
||||||
import java.awt.event.FocusEvent;
|
import java.awt.event.FocusEvent;
|
||||||
import java.awt.event.FocusListener;
|
import java.awt.event.FocusListener;
|
||||||
import java.awt.event.MouseListener;
|
import java.awt.event.MouseListener;
|
||||||
@@ -35,12 +37,18 @@ import java.awt.geom.Rectangle2D;
|
|||||||
import java.beans.PropertyChangeEvent;
|
import java.beans.PropertyChangeEvent;
|
||||||
import java.beans.PropertyChangeListener;
|
import java.beans.PropertyChangeListener;
|
||||||
import java.lang.ref.WeakReference;
|
import java.lang.ref.WeakReference;
|
||||||
|
import javax.swing.AbstractAction;
|
||||||
import javax.swing.BorderFactory;
|
import javax.swing.BorderFactory;
|
||||||
|
import javax.swing.ComboBoxEditor;
|
||||||
|
import javax.swing.DefaultListCellRenderer;
|
||||||
|
import javax.swing.InputMap;
|
||||||
import javax.swing.JButton;
|
import javax.swing.JButton;
|
||||||
import javax.swing.JComboBox;
|
import javax.swing.JComboBox;
|
||||||
import javax.swing.JComponent;
|
import javax.swing.JComponent;
|
||||||
import javax.swing.JList;
|
import javax.swing.JList;
|
||||||
import javax.swing.JPanel;
|
import javax.swing.JPanel;
|
||||||
|
import javax.swing.JTextField;
|
||||||
|
import javax.swing.KeyStroke;
|
||||||
import javax.swing.ListCellRenderer;
|
import javax.swing.ListCellRenderer;
|
||||||
import javax.swing.LookAndFeel;
|
import javax.swing.LookAndFeel;
|
||||||
import javax.swing.SwingConstants;
|
import javax.swing.SwingConstants;
|
||||||
@@ -53,6 +61,7 @@ import javax.swing.plaf.basic.BasicComboPopup;
|
|||||||
import javax.swing.plaf.basic.ComboPopup;
|
import javax.swing.plaf.basic.ComboPopup;
|
||||||
import javax.swing.text.JTextComponent;
|
import javax.swing.text.JTextComponent;
|
||||||
import com.formdev.flatlaf.FlatClientProperties;
|
import com.formdev.flatlaf.FlatClientProperties;
|
||||||
|
import com.formdev.flatlaf.util.SystemInfo;
|
||||||
import com.formdev.flatlaf.util.UIScale;
|
import com.formdev.flatlaf.util.UIScale;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -69,8 +78,8 @@ import com.formdev.flatlaf.util.UIScale;
|
|||||||
*
|
*
|
||||||
* <!-- FlatComboBoxUI -->
|
* <!-- FlatComboBoxUI -->
|
||||||
*
|
*
|
||||||
* @uiDefault Component.focusWidth int
|
* @uiDefault ComboBox.minimumWidth int
|
||||||
* @uiDefault Component.arc int
|
* @uiDefault ComboBox.editorColumns int
|
||||||
* @uiDefault Component.arrowType String triangle (default) or chevron
|
* @uiDefault Component.arrowType String triangle (default) or chevron
|
||||||
* @uiDefault Component.isIntelliJTheme boolean
|
* @uiDefault Component.isIntelliJTheme boolean
|
||||||
* @uiDefault Component.borderColor Color
|
* @uiDefault Component.borderColor Color
|
||||||
@@ -89,8 +98,8 @@ import com.formdev.flatlaf.util.UIScale;
|
|||||||
public class FlatComboBoxUI
|
public class FlatComboBoxUI
|
||||||
extends BasicComboBoxUI
|
extends BasicComboBoxUI
|
||||||
{
|
{
|
||||||
protected int focusWidth;
|
protected int minimumWidth;
|
||||||
protected int arc;
|
protected int editorColumns;
|
||||||
protected String arrowType;
|
protected String arrowType;
|
||||||
protected boolean isIntelliJTheme;
|
protected boolean isIntelliJTheme;
|
||||||
protected Color borderColor;
|
protected Color borderColor;
|
||||||
@@ -143,8 +152,8 @@ public class FlatComboBoxUI
|
|||||||
|
|
||||||
LookAndFeel.installProperty( comboBox, "opaque", false );
|
LookAndFeel.installProperty( comboBox, "opaque", false );
|
||||||
|
|
||||||
focusWidth = UIManager.getInt( "Component.focusWidth" );
|
minimumWidth = UIManager.getInt( "ComboBox.minimumWidth" );
|
||||||
arc = UIManager.getInt( "Component.arc" );
|
editorColumns = UIManager.getInt( "ComboBox.editorColumns" );
|
||||||
arrowType = UIManager.getString( "Component.arrowType" );
|
arrowType = UIManager.getString( "Component.arrowType" );
|
||||||
isIntelliJTheme = UIManager.getBoolean( "Component.isIntelliJTheme" );
|
isIntelliJTheme = UIManager.getBoolean( "Component.isIntelliJTheme" );
|
||||||
borderColor = UIManager.getColor( "Component.borderColor" );
|
borderColor = UIManager.getColor( "Component.borderColor" );
|
||||||
@@ -163,7 +172,7 @@ public class FlatComboBoxUI
|
|||||||
// scale
|
// scale
|
||||||
padding = UIScale.scale( padding );
|
padding = UIScale.scale( padding );
|
||||||
|
|
||||||
MigLayoutVisualPadding.install( comboBox, focusWidth );
|
MigLayoutVisualPadding.install( comboBox );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -242,6 +251,10 @@ public class FlatComboBoxUI
|
|||||||
editor.applyComponentOrientation( o );
|
editor.applyComponentOrientation( o );
|
||||||
} else if( editor != null && FlatClientProperties.PLACEHOLDER_TEXT.equals( propertyName ) )
|
} else if( editor != null && FlatClientProperties.PLACEHOLDER_TEXT.equals( propertyName ) )
|
||||||
editor.repaint();
|
editor.repaint();
|
||||||
|
else if( FlatClientProperties.COMPONENT_ROUND_RECT.equals( propertyName ) )
|
||||||
|
comboBox.repaint();
|
||||||
|
else if( FlatClientProperties.MINIMUM_WIDTH.equals( propertyName ) )
|
||||||
|
comboBox.revalidate();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -251,18 +264,30 @@ public class FlatComboBoxUI
|
|||||||
return new FlatComboPopup( comboBox );
|
return new FlatComboPopup( comboBox );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ComboBoxEditor createEditor() {
|
||||||
|
ComboBoxEditor comboBoxEditor = super.createEditor();
|
||||||
|
|
||||||
|
Component editor = comboBoxEditor.getEditorComponent();
|
||||||
|
if( editor instanceof JTextField ) {
|
||||||
|
JTextField textField = (JTextField) editor;
|
||||||
|
textField.setColumns( editorColumns );
|
||||||
|
|
||||||
|
// assign a non-null and non-javax.swing.plaf.UIResource border to the text field,
|
||||||
|
// otherwise it is replaced with default text field border when switching LaF
|
||||||
|
// because javax.swing.plaf.basic.BasicComboBoxEditor.BorderlessTextField.setBorder()
|
||||||
|
// uses "border instanceof javax.swing.plaf.basic.BasicComboBoxEditor.UIResource"
|
||||||
|
// instead of "border instanceof javax.swing.plaf.UIResource"
|
||||||
|
textField.setBorder( BorderFactory.createEmptyBorder() );
|
||||||
|
}
|
||||||
|
|
||||||
|
return comboBoxEditor;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void configureEditor() {
|
protected void configureEditor() {
|
||||||
super.configureEditor();
|
super.configureEditor();
|
||||||
|
|
||||||
// assign a non-javax.swing.plaf.UIResource border to the text field,
|
|
||||||
// otherwise it is replaced with default text field border when switching LaF
|
|
||||||
// because javax.swing.plaf.basic.BasicComboBoxEditor.BorderlessTextField.setBorder()
|
|
||||||
// uses "border instanceof javax.swing.plaf.basic.BasicComboBoxEditor.UIResource"
|
|
||||||
// instead of "border instanceof javax.swing.plaf.UIResource"
|
|
||||||
if( editor instanceof JTextComponent )
|
|
||||||
((JTextComponent)editor).setBorder( BorderFactory.createEmptyBorder() );
|
|
||||||
|
|
||||||
// explicitly make non-opaque
|
// explicitly make non-opaque
|
||||||
if( editor instanceof JComponent )
|
if( editor instanceof JComponent )
|
||||||
((JComponent)editor).setOpaque( false );
|
((JComponent)editor).setOpaque( false );
|
||||||
@@ -270,6 +295,19 @@ public class FlatComboBoxUI
|
|||||||
editor.applyComponentOrientation( comboBox.getComponentOrientation() );
|
editor.applyComponentOrientation( comboBox.getComponentOrientation() );
|
||||||
|
|
||||||
updateEditorColors();
|
updateEditorColors();
|
||||||
|
|
||||||
|
// macOS
|
||||||
|
if( SystemInfo.IS_MAC && editor instanceof JTextComponent ) {
|
||||||
|
// delegate actions from editor text field to combobox, which is necessary
|
||||||
|
// because text field on macOS already handle those keys
|
||||||
|
InputMap inputMap = ((JTextComponent)editor).getInputMap();
|
||||||
|
new EditorDelegateAction( inputMap, KeyStroke.getKeyStroke( "UP" ) );
|
||||||
|
new EditorDelegateAction( inputMap, KeyStroke.getKeyStroke( "KP_UP" ) );
|
||||||
|
new EditorDelegateAction( inputMap, KeyStroke.getKeyStroke( "DOWN" ) );
|
||||||
|
new EditorDelegateAction( inputMap, KeyStroke.getKeyStroke( "KP_DOWN" ) );
|
||||||
|
new EditorDelegateAction( inputMap, KeyStroke.getKeyStroke( "HOME" ) );
|
||||||
|
new EditorDelegateAction( inputMap, KeyStroke.getKeyStroke( "END" ) );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateEditorColors() {
|
private void updateEditorColors() {
|
||||||
@@ -298,8 +336,11 @@ public class FlatComboBoxUI
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void update( Graphics g, JComponent c ) {
|
public void update( Graphics g, JComponent c ) {
|
||||||
|
float focusWidth = FlatUIUtils.getBorderFocusWidth( c );
|
||||||
|
float arc = FlatUIUtils.getBorderArc( c );
|
||||||
|
|
||||||
// fill background if opaque to avoid garbage if user sets opaque to true
|
// fill background if opaque to avoid garbage if user sets opaque to true
|
||||||
if( c.isOpaque() && (focusWidth > 0 || arc != 0) )
|
if( c.isOpaque() && (focusWidth > 0 || arc > 0) )
|
||||||
FlatUIUtils.paintParentBackground( g, c );
|
FlatUIUtils.paintParentBackground( g, c );
|
||||||
|
|
||||||
Graphics2D g2 = (Graphics2D) g;
|
Graphics2D g2 = (Graphics2D) g;
|
||||||
@@ -307,8 +348,6 @@ public class FlatComboBoxUI
|
|||||||
|
|
||||||
int width = c.getWidth();
|
int width = c.getWidth();
|
||||||
int height = c.getHeight();
|
int height = c.getHeight();
|
||||||
float focusWidth = (c.getBorder() instanceof FlatBorder) ? scale( (float) this.focusWidth ) : 0;
|
|
||||||
float arc = (c.getBorder() instanceof FlatRoundBorder) ? scale( (float) this.arc ) : 0;
|
|
||||||
int arrowX = arrowButton.getX();
|
int arrowX = arrowButton.getX();
|
||||||
int arrowWidth = arrowButton.getWidth();
|
int arrowWidth = arrowButton.getWidth();
|
||||||
boolean enabled = comboBox.isEnabled();
|
boolean enabled = comboBox.isEnabled();
|
||||||
@@ -348,6 +387,8 @@ public class FlatComboBoxUI
|
|||||||
public void paintCurrentValue( Graphics g, Rectangle bounds, boolean hasFocus ) {
|
public void paintCurrentValue( Graphics g, Rectangle bounds, boolean hasFocus ) {
|
||||||
ListCellRenderer<Object> renderer = comboBox.getRenderer();
|
ListCellRenderer<Object> renderer = comboBox.getRenderer();
|
||||||
uninstallCellPaddingBorder( renderer );
|
uninstallCellPaddingBorder( renderer );
|
||||||
|
if( renderer == null )
|
||||||
|
renderer = new DefaultListCellRenderer();
|
||||||
Component c = renderer.getListCellRendererComponent( listBox, comboBox.getSelectedItem(), -1, false, false );
|
Component c = renderer.getListCellRendererComponent( listBox, comboBox.getSelectedItem(), -1, false, false );
|
||||||
c.setFont( comboBox.getFont() );
|
c.setFont( comboBox.getFont() );
|
||||||
c.applyComponentOrientation( comboBox.getComponentOrientation() );
|
c.applyComponentOrientation( comboBox.getComponentOrientation() );
|
||||||
@@ -372,14 +413,20 @@ public class FlatComboBoxUI
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void paintCurrentValueBackground( Graphics g, Rectangle bounds, boolean hasFocus ) {
|
public void paintCurrentValueBackground( Graphics g, Rectangle bounds, boolean hasFocus ) {
|
||||||
g.setColor( comboBox.isEnabled() ? comboBox.getBackground() : getDisabledBackground( comboBox ) );
|
// not necessary because already painted in update()
|
||||||
g.fillRect( bounds.x, bounds.y, bounds.width, bounds.height );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Color getDisabledBackground( JComponent c ) {
|
private Color getDisabledBackground( JComponent c ) {
|
||||||
return isIntelliJTheme ? FlatUIUtils.getParentBackground( c ) : disabledBackground;
|
return isIntelliJTheme ? FlatUIUtils.getParentBackground( c ) : disabledBackground;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Dimension getMinimumSize( JComponent c ) {
|
||||||
|
Dimension minimumSize = super.getMinimumSize( c );
|
||||||
|
minimumSize.width = Math.max( minimumSize.width, scale( FlatUIUtils.minimumWidth( c, minimumWidth ) ) );
|
||||||
|
return minimumSize;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Dimension getDefaultSize() {
|
protected Dimension getDefaultSize() {
|
||||||
@SuppressWarnings( "unchecked" )
|
@SuppressWarnings( "unchecked" )
|
||||||
@@ -400,6 +447,18 @@ public class FlatComboBoxUI
|
|||||||
|
|
||||||
Dimension displaySize = super.getDisplaySize();
|
Dimension displaySize = super.getDisplaySize();
|
||||||
|
|
||||||
|
// recalculate width without hardcoded 100 under special conditions
|
||||||
|
if( displaySize.width == 100 + padding.left + padding.right &&
|
||||||
|
comboBox.isEditable() &&
|
||||||
|
comboBox.getItemCount() == 0 &&
|
||||||
|
comboBox.getPrototypeDisplayValue() == null )
|
||||||
|
{
|
||||||
|
int width = getDefaultSize().width;
|
||||||
|
width = Math.max( width, editor.getPreferredSize().width );
|
||||||
|
width += padding.left + padding.right;
|
||||||
|
displaySize = new Dimension( width, displaySize.height );
|
||||||
|
}
|
||||||
|
|
||||||
uninstallCellPaddingBorder( renderer );
|
uninstallCellPaddingBorder( renderer );
|
||||||
return displaySize;
|
return displaySize;
|
||||||
}
|
}
|
||||||
@@ -438,12 +497,12 @@ public class FlatComboBoxUI
|
|||||||
//---- class FlatComboPopup -----------------------------------------------
|
//---- class FlatComboPopup -----------------------------------------------
|
||||||
|
|
||||||
@SuppressWarnings( { "rawtypes", "unchecked" } )
|
@SuppressWarnings( { "rawtypes", "unchecked" } )
|
||||||
private class FlatComboPopup
|
protected class FlatComboPopup
|
||||||
extends BasicComboPopup
|
extends BasicComboPopup
|
||||||
{
|
{
|
||||||
private CellPaddingBorder paddingBorder;
|
private CellPaddingBorder paddingBorder;
|
||||||
|
|
||||||
FlatComboPopup( JComboBox combo ) {
|
protected FlatComboPopup( JComboBox combo ) {
|
||||||
super( combo );
|
super( combo );
|
||||||
|
|
||||||
// BasicComboPopup listens to JComboBox.componentOrientation and updates
|
// BasicComboPopup listens to JComboBox.componentOrientation and updates
|
||||||
@@ -458,13 +517,8 @@ public class FlatComboBoxUI
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Rectangle computePopupBounds( int px, int py, int pw, int ph ) {
|
protected Rectangle computePopupBounds( int px, int py, int pw, int ph ) {
|
||||||
// get maximum display size of all items, ignoring prototype value
|
// get maximum display size of all items
|
||||||
Object prototype = comboBox.getPrototypeDisplayValue();
|
|
||||||
if( prototype != null )
|
|
||||||
comboBox.setPrototypeDisplayValue( null );
|
|
||||||
Dimension displaySize = getDisplaySize();
|
Dimension displaySize = getDisplaySize();
|
||||||
if( prototype != null )
|
|
||||||
comboBox.setPrototypeDisplayValue( prototype );
|
|
||||||
|
|
||||||
// make popup wider if necessary
|
// make popup wider if necessary
|
||||||
if( displaySize.width > pw ) {
|
if( displaySize.width > pw ) {
|
||||||
@@ -520,6 +574,8 @@ public class FlatComboBoxUI
|
|||||||
CellPaddingBorder.uninstall( renderer );
|
CellPaddingBorder.uninstall( renderer );
|
||||||
CellPaddingBorder.uninstall( lastRendererComponent );
|
CellPaddingBorder.uninstall( lastRendererComponent );
|
||||||
|
|
||||||
|
if( renderer == null )
|
||||||
|
renderer = new DefaultListCellRenderer();
|
||||||
Component c = renderer.getListCellRendererComponent( list, value, index, isSelected, cellHasFocus );
|
Component c = renderer.getListCellRendererComponent( list, value, index, isSelected, cellHasFocus );
|
||||||
c.applyComponentOrientation( comboBox.getComponentOrientation() );
|
c.applyComponentOrientation( comboBox.getComponentOrientation() );
|
||||||
|
|
||||||
@@ -603,4 +659,31 @@ public class FlatComboBoxUI
|
|||||||
rendererBorder.paintBorder( c, g, x, y, width, height );
|
rendererBorder.paintBorder( c, g, x, y, width, height );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//---- class EditorDelegateAction -----------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delegates actions from editor text field to combobox.
|
||||||
|
*/
|
||||||
|
private class EditorDelegateAction
|
||||||
|
extends AbstractAction
|
||||||
|
{
|
||||||
|
private final KeyStroke keyStroke;
|
||||||
|
|
||||||
|
EditorDelegateAction( InputMap inputMap, KeyStroke keyStroke ) {
|
||||||
|
this.keyStroke = keyStroke;
|
||||||
|
|
||||||
|
// add to input map
|
||||||
|
inputMap.put( keyStroke, this );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void actionPerformed( ActionEvent e ) {
|
||||||
|
ActionListener action = comboBox.getActionForKeyStroke( keyStroke );
|
||||||
|
if( action != null ) {
|
||||||
|
action.actionPerformed( new ActionEvent( comboBox, e.getID(),
|
||||||
|
e.getActionCommand(), e.getWhen(), e.getModifiers() ) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,308 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2020 FormDev Software GmbH
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.formdev.flatlaf.ui;
|
||||||
|
|
||||||
|
import java.awt.Component;
|
||||||
|
import java.awt.Container;
|
||||||
|
import java.awt.Dimension;
|
||||||
|
import java.awt.EventQueue;
|
||||||
|
import java.awt.Graphics2D;
|
||||||
|
import java.awt.Image;
|
||||||
|
import java.awt.Insets;
|
||||||
|
import java.awt.LayoutManager;
|
||||||
|
import java.awt.Point;
|
||||||
|
import java.awt.event.ActionListener;
|
||||||
|
import java.awt.event.MouseEvent;
|
||||||
|
import java.awt.image.BufferedImage;
|
||||||
|
import java.beans.PropertyVetoException;
|
||||||
|
import javax.swing.BorderFactory;
|
||||||
|
import javax.swing.ImageIcon;
|
||||||
|
import javax.swing.JButton;
|
||||||
|
import javax.swing.JComponent;
|
||||||
|
import javax.swing.event.MouseInputAdapter;
|
||||||
|
import javax.swing.event.MouseInputListener;
|
||||||
|
import javax.swing.JLabel;
|
||||||
|
import javax.swing.JLayeredPane;
|
||||||
|
import javax.swing.JRootPane;
|
||||||
|
import javax.swing.JToolTip;
|
||||||
|
import javax.swing.LookAndFeel;
|
||||||
|
import javax.swing.SwingConstants;
|
||||||
|
import javax.swing.SwingUtilities;
|
||||||
|
import javax.swing.UIManager;
|
||||||
|
import javax.swing.plaf.ComponentUI;
|
||||||
|
import javax.swing.plaf.basic.BasicDesktopIconUI;
|
||||||
|
import com.formdev.flatlaf.util.UIScale;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides the Flat LaF UI delegate for {@link javax.swing.JInternalFrame.JDesktopIcon}.
|
||||||
|
*
|
||||||
|
* <!-- BasicDesktopIconUI -->
|
||||||
|
*
|
||||||
|
* @uiDefault DesktopIcon.border Border
|
||||||
|
*
|
||||||
|
* <!-- FlatDesktopIconUI -->
|
||||||
|
*
|
||||||
|
* @uiDefault DesktopIcon.background Color
|
||||||
|
* @uiDefault DesktopIcon.foreground Color
|
||||||
|
* @uiDefault DesktopIcon.iconSize Dimension
|
||||||
|
* @uiDefault DesktopIcon.closeSize Dimension
|
||||||
|
* @uiDefault DesktopIcon.closeIcon Icon
|
||||||
|
*
|
||||||
|
* @author Karl Tauber
|
||||||
|
*/
|
||||||
|
public class FlatDesktopIconUI
|
||||||
|
extends BasicDesktopIconUI
|
||||||
|
{
|
||||||
|
private Dimension iconSize;
|
||||||
|
private Dimension closeSize;
|
||||||
|
|
||||||
|
private JLabel dockIcon;
|
||||||
|
private JButton closeButton;
|
||||||
|
private JToolTip titleTip;
|
||||||
|
private ActionListener closeListener;
|
||||||
|
private MouseInputListener mouseInputListener;
|
||||||
|
|
||||||
|
public static ComponentUI createUI( JComponent c ) {
|
||||||
|
return new FlatDesktopIconUI();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void uninstallUI( JComponent c ) {
|
||||||
|
super.uninstallUI( c );
|
||||||
|
|
||||||
|
dockIcon = null;
|
||||||
|
closeButton = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void installComponents() {
|
||||||
|
dockIcon = new JLabel();
|
||||||
|
dockIcon.setHorizontalAlignment( SwingConstants.CENTER );
|
||||||
|
|
||||||
|
closeButton = new JButton();
|
||||||
|
closeButton.setIcon( UIManager.getIcon( "DesktopIcon.closeIcon" ) );
|
||||||
|
closeButton.setFocusable( false );
|
||||||
|
closeButton.setBorder( BorderFactory.createEmptyBorder() );
|
||||||
|
closeButton.setOpaque( true );
|
||||||
|
closeButton.setBackground( FlatUIUtils.nonUIResource( desktopIcon.getBackground() ) );
|
||||||
|
closeButton.setForeground( FlatUIUtils.nonUIResource( desktopIcon.getForeground() ) );
|
||||||
|
closeButton.setVisible( false );
|
||||||
|
|
||||||
|
desktopIcon.setLayout( new FlatDesktopIconLayout() );
|
||||||
|
desktopIcon.add( closeButton );
|
||||||
|
desktopIcon.add( dockIcon );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void uninstallComponents() {
|
||||||
|
hideTitleTip();
|
||||||
|
|
||||||
|
desktopIcon.remove( dockIcon );
|
||||||
|
desktopIcon.remove( closeButton );
|
||||||
|
desktopIcon.setLayout( null );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void installDefaults() {
|
||||||
|
super.installDefaults();
|
||||||
|
|
||||||
|
LookAndFeel.installColors( desktopIcon, "DesktopIcon.background", "DesktopIcon.foreground" );
|
||||||
|
|
||||||
|
iconSize = UIManager.getDimension( "DesktopIcon.iconSize" );
|
||||||
|
closeSize = UIManager.getDimension( "DesktopIcon.closeSize" );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void installListeners() {
|
||||||
|
super.installListeners();
|
||||||
|
|
||||||
|
closeListener = e -> {
|
||||||
|
if( frame.isClosable() )
|
||||||
|
frame.doDefaultCloseAction();
|
||||||
|
};
|
||||||
|
closeButton.addActionListener( closeListener );
|
||||||
|
closeButton.addMouseListener( mouseInputListener );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void uninstallListeners() {
|
||||||
|
super.uninstallListeners();
|
||||||
|
|
||||||
|
closeButton.removeActionListener( closeListener );
|
||||||
|
closeButton.removeMouseListener( mouseInputListener );
|
||||||
|
closeListener = null;
|
||||||
|
mouseInputListener = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected MouseInputListener createMouseInputListener() {
|
||||||
|
mouseInputListener = new MouseInputAdapter() {
|
||||||
|
@Override
|
||||||
|
public void mouseReleased( MouseEvent e ) {
|
||||||
|
if( frame.isIcon() && desktopIcon.contains( e.getX(), e.getY() ) ) {
|
||||||
|
hideTitleTip();
|
||||||
|
closeButton.setVisible( false );
|
||||||
|
|
||||||
|
try {
|
||||||
|
frame.setIcon( false );
|
||||||
|
} catch( PropertyVetoException ex ) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void mouseEntered( MouseEvent e ) {
|
||||||
|
showTitleTip();
|
||||||
|
if( frame.isClosable() )
|
||||||
|
closeButton.setVisible( true );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void mouseExited( MouseEvent e ) {
|
||||||
|
hideTitleTip();
|
||||||
|
closeButton.setVisible( false );
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return mouseInputListener;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void showTitleTip() {
|
||||||
|
JRootPane rootPane = SwingUtilities.getRootPane( desktopIcon );
|
||||||
|
if( rootPane == null )
|
||||||
|
return;
|
||||||
|
|
||||||
|
if( titleTip == null ) {
|
||||||
|
titleTip = new JToolTip();
|
||||||
|
rootPane.getLayeredPane().add( titleTip, JLayeredPane.POPUP_LAYER );
|
||||||
|
}
|
||||||
|
titleTip.setTipText( frame.getTitle() );
|
||||||
|
titleTip.setSize( titleTip.getPreferredSize() );
|
||||||
|
|
||||||
|
int tx = (desktopIcon.getWidth() - titleTip.getWidth()) / 2;
|
||||||
|
int ty = -(titleTip.getHeight() + UIScale.scale( 4 ));
|
||||||
|
Point pt = SwingUtilities.convertPoint( desktopIcon, tx, ty, titleTip.getParent() );
|
||||||
|
if( pt.x + titleTip.getWidth() > rootPane.getWidth() )
|
||||||
|
pt.x = rootPane.getWidth() - titleTip.getWidth();
|
||||||
|
if( pt.x < 0 )
|
||||||
|
pt.x = 0;
|
||||||
|
titleTip.setLocation( pt );
|
||||||
|
titleTip.repaint();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void hideTitleTip() {
|
||||||
|
if( titleTip == null )
|
||||||
|
return;
|
||||||
|
|
||||||
|
titleTip.setVisible( false );
|
||||||
|
titleTip.getParent().remove( titleTip );
|
||||||
|
titleTip = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Dimension getPreferredSize( JComponent c ) {
|
||||||
|
return UIScale.scale( iconSize );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Dimension getMinimumSize( JComponent c ) {
|
||||||
|
return getPreferredSize( c );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Dimension getMaximumSize( JComponent c ) {
|
||||||
|
return getPreferredSize( c );
|
||||||
|
}
|
||||||
|
|
||||||
|
void updateDockIcon() {
|
||||||
|
// use invoke later to make sure that components are updated when switching LaF
|
||||||
|
EventQueue.invokeLater( () -> {
|
||||||
|
if( dockIcon != null )
|
||||||
|
updateDockIconLater();
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateDockIconLater() {
|
||||||
|
// make sure that frame is not selected
|
||||||
|
if( frame.isSelected() ) {
|
||||||
|
try {
|
||||||
|
frame.setSelected( false );
|
||||||
|
} catch( PropertyVetoException ex ) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// paint internal frame to buffered image
|
||||||
|
int frameWidth = Math.max( frame.getWidth(), 1 );
|
||||||
|
int frameHeight = Math.max( frame.getHeight(), 1 );
|
||||||
|
BufferedImage frameImage = new BufferedImage( frameWidth, frameHeight, BufferedImage.TYPE_INT_ARGB );
|
||||||
|
Graphics2D g = frameImage.createGraphics();
|
||||||
|
try {
|
||||||
|
//TODO fix missing internal frame header when switching LaF
|
||||||
|
frame.paint( g );
|
||||||
|
} finally {
|
||||||
|
g.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
// compute preview size (keep ratio; also works with non-square preview)
|
||||||
|
Insets insets = desktopIcon.getInsets();
|
||||||
|
int previewWidth = UIScale.scale( iconSize.width ) - insets.left - insets.right;
|
||||||
|
int previewHeight = UIScale.scale( iconSize.height ) - insets.top - insets.bottom;
|
||||||
|
float frameRatio = ((float) frameHeight / (float) frameWidth);
|
||||||
|
if( ((float) previewWidth / (float) frameWidth) > ((float) previewHeight / (float) frameHeight) )
|
||||||
|
previewWidth = Math.round( previewHeight / frameRatio );
|
||||||
|
else
|
||||||
|
previewHeight = Math.round( previewWidth * frameRatio );
|
||||||
|
|
||||||
|
// scale preview
|
||||||
|
Image previewImage = frameImage.getScaledInstance( previewWidth, previewHeight, Image.SCALE_SMOOTH );
|
||||||
|
dockIcon.setIcon( new ImageIcon( previewImage ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
//---- class DockIcon -----------------------------------------------------
|
||||||
|
|
||||||
|
private class FlatDesktopIconLayout
|
||||||
|
implements LayoutManager
|
||||||
|
{
|
||||||
|
@Override public void addLayoutComponent( String name, Component comp ) {}
|
||||||
|
@Override public void removeLayoutComponent( Component comp ) {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Dimension preferredLayoutSize( Container parent ) {
|
||||||
|
return dockIcon.getPreferredSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Dimension minimumLayoutSize( Container parent ) {
|
||||||
|
return dockIcon.getMinimumSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void layoutContainer( Container parent ) {
|
||||||
|
Insets insets = parent.getInsets();
|
||||||
|
|
||||||
|
// dock icon
|
||||||
|
dockIcon.setBounds( insets.left, insets.top,
|
||||||
|
parent.getWidth() - insets.left - insets.right,
|
||||||
|
parent.getHeight() - insets.top - insets.bottom );
|
||||||
|
|
||||||
|
// close button in upper right corner
|
||||||
|
Dimension cSize = UIScale.scale( closeSize );
|
||||||
|
closeButton.setBounds( parent.getWidth() - cSize.width, 0, cSize.width, cSize.height );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2020 FormDev Software GmbH
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.formdev.flatlaf.ui;
|
||||||
|
|
||||||
|
import javax.swing.DefaultDesktopManager;
|
||||||
|
import javax.swing.JComponent;
|
||||||
|
import javax.swing.JInternalFrame;
|
||||||
|
import javax.swing.plaf.ComponentUI;
|
||||||
|
import javax.swing.plaf.UIResource;
|
||||||
|
import javax.swing.plaf.basic.BasicDesktopPaneUI;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides the Flat LaF UI delegate for {@link javax.swing.JDesktopPane}.
|
||||||
|
*
|
||||||
|
* <!-- BasicDesktopPaneUI -->
|
||||||
|
*
|
||||||
|
* @uiDefault Desktop.background Color
|
||||||
|
* @uiDefault Desktop.minOnScreenInsets Insets
|
||||||
|
*
|
||||||
|
* @author Karl Tauber
|
||||||
|
*/
|
||||||
|
public class FlatDesktopPaneUI
|
||||||
|
extends BasicDesktopPaneUI
|
||||||
|
{
|
||||||
|
public static ComponentUI createUI( JComponent c ) {
|
||||||
|
return new FlatDesktopPaneUI();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void installDesktopManager() {
|
||||||
|
desktopManager = desktop.getDesktopManager();
|
||||||
|
if( desktopManager == null ) {
|
||||||
|
desktopManager = new FlatDesktopManager();
|
||||||
|
desktop.setDesktopManager( desktopManager );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//---- class FlatDesktopManager -------------------------------------------
|
||||||
|
|
||||||
|
private class FlatDesktopManager
|
||||||
|
extends DefaultDesktopManager
|
||||||
|
implements UIResource
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void iconifyFrame( JInternalFrame f ) {
|
||||||
|
super.iconifyFrame( f );
|
||||||
|
|
||||||
|
((FlatDesktopIconUI)f.getDesktopIcon().getUI()).updateDockIcon();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,223 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2020 FormDev Software GmbH
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.formdev.flatlaf.ui;
|
||||||
|
|
||||||
|
import java.awt.Color;
|
||||||
|
import java.awt.Component;
|
||||||
|
import java.awt.Graphics;
|
||||||
|
import java.awt.Graphics2D;
|
||||||
|
import java.awt.Image;
|
||||||
|
import java.awt.Insets;
|
||||||
|
import java.awt.RadialGradientPaint;
|
||||||
|
import java.awt.image.BufferedImage;
|
||||||
|
import com.formdev.flatlaf.util.HiDPIUtils;
|
||||||
|
import com.formdev.flatlaf.util.UIScale;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Paints a drop shadow border around the component.
|
||||||
|
* Supports 1-sided, 2-side, 3-sided or 4-sided drop shadows.
|
||||||
|
* <p>
|
||||||
|
* The shadow insets allow specifying drop shadow thickness for each side.
|
||||||
|
* A zero or negative value hides the drop shadow on that side.
|
||||||
|
* A negative value can be used to indent the drop shadow on corners.
|
||||||
|
* E.g. -4 on left indents drop shadow at top-left and bottom-left corners by 4 pixels.
|
||||||
|
*
|
||||||
|
* @author Karl Tauber
|
||||||
|
*/
|
||||||
|
public class FlatDropShadowBorder
|
||||||
|
extends FlatEmptyBorder
|
||||||
|
{
|
||||||
|
private final Color shadowColor;
|
||||||
|
private final Insets shadowInsets;
|
||||||
|
private final float shadowOpacity;
|
||||||
|
|
||||||
|
private final int shadowSize;
|
||||||
|
private Image shadowImage;
|
||||||
|
private Color lastShadowColor;
|
||||||
|
private double lastSystemScaleFactor;
|
||||||
|
private float lastUserScaleFactor;
|
||||||
|
|
||||||
|
public FlatDropShadowBorder() {
|
||||||
|
this( null );
|
||||||
|
}
|
||||||
|
|
||||||
|
public FlatDropShadowBorder( Color shadowColor ) {
|
||||||
|
this( shadowColor, 4, 0.5f );
|
||||||
|
}
|
||||||
|
|
||||||
|
public FlatDropShadowBorder( Color shadowColor, int shadowSize, float shadowOpacity ) {
|
||||||
|
this( shadowColor, new Insets( -shadowSize, -shadowSize, shadowSize, shadowSize ), shadowOpacity );
|
||||||
|
}
|
||||||
|
|
||||||
|
public FlatDropShadowBorder( Color shadowColor, Insets shadowInsets, float shadowOpacity ) {
|
||||||
|
super( Math.max( shadowInsets.top, 0 ), Math.max( shadowInsets.left, 0 ),
|
||||||
|
Math.max( shadowInsets.bottom, 0 ), Math.max( shadowInsets.right, 0 ) );
|
||||||
|
this.shadowColor = shadowColor;
|
||||||
|
this.shadowInsets = shadowInsets;
|
||||||
|
this.shadowOpacity = shadowOpacity;
|
||||||
|
|
||||||
|
shadowSize = Math.max(
|
||||||
|
Math.max( shadowInsets.left, shadowInsets.right ),
|
||||||
|
Math.max( shadowInsets.top, shadowInsets.bottom ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void paintBorder( Component c, Graphics g, int x, int y, int width, int height ) {
|
||||||
|
if( shadowSize <= 0 )
|
||||||
|
return;
|
||||||
|
|
||||||
|
HiDPIUtils.paintAtScale1x( (Graphics2D) g, x, y, width, height, this::paintImpl );
|
||||||
|
}
|
||||||
|
|
||||||
|
private void paintImpl( Graphics2D g, int x, int y, int width, int height, double scaleFactor ) {
|
||||||
|
Color shadowColor = (this.shadowColor != null) ? this.shadowColor : g.getColor();
|
||||||
|
int shadowSize = scale( this.shadowSize, scaleFactor );
|
||||||
|
|
||||||
|
// create and cache shadow image
|
||||||
|
float userScaleFactor = UIScale.getUserScaleFactor();
|
||||||
|
if( shadowImage == null ||
|
||||||
|
!shadowColor.equals( lastShadowColor ) ||
|
||||||
|
lastSystemScaleFactor != scaleFactor ||
|
||||||
|
lastUserScaleFactor != userScaleFactor )
|
||||||
|
{
|
||||||
|
shadowImage = createShadowImage( shadowColor, shadowSize, shadowOpacity,
|
||||||
|
(float) (scaleFactor * userScaleFactor) );
|
||||||
|
lastShadowColor = shadowColor;
|
||||||
|
lastSystemScaleFactor = scaleFactor;
|
||||||
|
lastUserScaleFactor = userScaleFactor;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*debug
|
||||||
|
int m = shadowImage.getWidth( null );
|
||||||
|
Color oldColor = g.getColor();
|
||||||
|
g.setColor( Color.lightGray );
|
||||||
|
g.drawRect( x - m - 1, y - m - 1, m + 1, m + 1 );
|
||||||
|
g.setColor( Color.white );
|
||||||
|
g.fillRect( x - m, y - m, m, m );
|
||||||
|
g.drawImage( shadowImage, x - m, y - m, null );
|
||||||
|
g.setColor( oldColor );
|
||||||
|
debug*/
|
||||||
|
|
||||||
|
int left = scale( shadowInsets.left, scaleFactor );
|
||||||
|
int right = scale( shadowInsets.right, scaleFactor );
|
||||||
|
int top = scale( shadowInsets.top, scaleFactor );
|
||||||
|
int bottom = scale( shadowInsets.bottom, scaleFactor );
|
||||||
|
|
||||||
|
// shadow outer coordinates
|
||||||
|
int x1o = x - Math.min( left, 0 );
|
||||||
|
int y1o = y - Math.min( top, 0 );
|
||||||
|
int x2o = x + width + Math.min( right, 0 );
|
||||||
|
int y2o = y + height + Math.min( bottom, 0 );
|
||||||
|
|
||||||
|
// shadow inner coordinates
|
||||||
|
int x1i = x1o + shadowSize;
|
||||||
|
int y1i = y1o + shadowSize;
|
||||||
|
int x2i = x2o - shadowSize;
|
||||||
|
int y2i = y2o - shadowSize;
|
||||||
|
|
||||||
|
int wh = (shadowSize * 2) - 1;
|
||||||
|
int center = shadowSize - 1;
|
||||||
|
|
||||||
|
// left-top edge
|
||||||
|
if( left > 0 || top > 0 ) {
|
||||||
|
g.drawImage( shadowImage, x1o, y1o, x1i, y1i,
|
||||||
|
0, 0, center, center, null );
|
||||||
|
}
|
||||||
|
|
||||||
|
// top shadow
|
||||||
|
if( top > 0 ) {
|
||||||
|
g.drawImage( shadowImage, x1i, y1o, x2i, y1i,
|
||||||
|
center, 0, center + 1, center, null );
|
||||||
|
}
|
||||||
|
|
||||||
|
// right-top edge
|
||||||
|
if( right > 0 || top > 0 ) {
|
||||||
|
g.drawImage( shadowImage, x2i, y1o, x2o, y1i,
|
||||||
|
center, 0, wh, center, null );
|
||||||
|
}
|
||||||
|
|
||||||
|
// left shadow
|
||||||
|
if( left > 0 ) {
|
||||||
|
g.drawImage( shadowImage, x1o, y1i, x1i, y2i,
|
||||||
|
0, center, center, center + 1, null );
|
||||||
|
}
|
||||||
|
|
||||||
|
// right shadow
|
||||||
|
if( right > 0 ) {
|
||||||
|
g.drawImage( shadowImage, x2i, y1i, x2o, y2i,
|
||||||
|
center, center, wh, center + 1, null );
|
||||||
|
}
|
||||||
|
|
||||||
|
// left-bottom edge
|
||||||
|
if( left > 0 || bottom > 0 ) {
|
||||||
|
g.drawImage( shadowImage, x1o, y2i, x1i, y2o,
|
||||||
|
0, center, center, wh, null );
|
||||||
|
}
|
||||||
|
|
||||||
|
// bottom shadow
|
||||||
|
if( bottom > 0 ) {
|
||||||
|
g.drawImage( shadowImage, x1i, y2i, x2i, y2o,
|
||||||
|
center, center, center + 1, wh, null );
|
||||||
|
}
|
||||||
|
|
||||||
|
// right-bottom edge
|
||||||
|
if( right > 0 || bottom > 0 ) {
|
||||||
|
g.drawImage( shadowImage, x2i, y2i, x2o, y2o,
|
||||||
|
center, center, wh, wh, null );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private int scale( int value, double scaleFactor ) {
|
||||||
|
return (int) Math.ceil( UIScale.scale( value ) * scaleFactor );
|
||||||
|
}
|
||||||
|
|
||||||
|
private static BufferedImage createShadowImage( Color shadowColor, int shadowSize,
|
||||||
|
float shadowOpacity, float scaleFactor )
|
||||||
|
{
|
||||||
|
int shadowRGB = shadowColor.getRGB() & 0xffffff;
|
||||||
|
int shadowAlpha = (int) (255 * shadowOpacity);
|
||||||
|
Color startColor = new Color( shadowRGB | ((shadowAlpha & 0xff) << 24), true );
|
||||||
|
Color midColor = new Color( shadowRGB | (((shadowAlpha / 2) & 0xff) << 24), true );
|
||||||
|
Color endColor = new Color( shadowRGB, true );
|
||||||
|
|
||||||
|
/*debug
|
||||||
|
startColor = Color.red;
|
||||||
|
midColor = Color.green;
|
||||||
|
endColor = Color.blue;
|
||||||
|
debug*/
|
||||||
|
|
||||||
|
int wh = (shadowSize * 2) - 1;
|
||||||
|
int center = shadowSize - 1;
|
||||||
|
|
||||||
|
RadialGradientPaint p = new RadialGradientPaint( center, center,
|
||||||
|
shadowSize - (0.75f * scaleFactor),
|
||||||
|
new float[] { 0, 0.35f, 1 },
|
||||||
|
new Color[] { startColor, midColor, endColor } );
|
||||||
|
|
||||||
|
BufferedImage image = new BufferedImage( wh, wh, BufferedImage.TYPE_INT_ARGB );
|
||||||
|
|
||||||
|
Graphics2D g = image.createGraphics();
|
||||||
|
try {
|
||||||
|
g.setPaint( p );
|
||||||
|
g.fillRect( 0, 0, wh, wh );
|
||||||
|
} finally {
|
||||||
|
g.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
return image;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -19,6 +19,7 @@ package com.formdev.flatlaf.ui;
|
|||||||
import static com.formdev.flatlaf.util.UIScale.scale;
|
import static com.formdev.flatlaf.util.UIScale.scale;
|
||||||
import java.awt.Dimension;
|
import java.awt.Dimension;
|
||||||
import java.awt.Graphics;
|
import java.awt.Graphics;
|
||||||
|
import java.beans.PropertyChangeEvent;
|
||||||
import javax.swing.JComponent;
|
import javax.swing.JComponent;
|
||||||
import javax.swing.JEditorPane;
|
import javax.swing.JEditorPane;
|
||||||
import javax.swing.UIManager;
|
import javax.swing.UIManager;
|
||||||
@@ -26,6 +27,7 @@ import javax.swing.plaf.ComponentUI;
|
|||||||
import javax.swing.plaf.UIResource;
|
import javax.swing.plaf.UIResource;
|
||||||
import javax.swing.plaf.basic.BasicEditorPaneUI;
|
import javax.swing.plaf.basic.BasicEditorPaneUI;
|
||||||
import javax.swing.text.JTextComponent;
|
import javax.swing.text.JTextComponent;
|
||||||
|
import com.formdev.flatlaf.FlatClientProperties;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides the Flat LaF UI delegate for {@link javax.swing.JEditorPane}.
|
* Provides the Flat LaF UI delegate for {@link javax.swing.JEditorPane}.
|
||||||
@@ -83,21 +85,36 @@ public class FlatEditorPaneUI
|
|||||||
getComponent().putClientProperty( JEditorPane.HONOR_DISPLAY_PROPERTIES, oldHonorDisplayProperties );
|
getComponent().putClientProperty( JEditorPane.HONOR_DISPLAY_PROPERTIES, oldHonorDisplayProperties );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void propertyChange( PropertyChangeEvent e ) {
|
||||||
|
super.propertyChange( e );
|
||||||
|
propertyChange( getComponent(), e );
|
||||||
|
}
|
||||||
|
|
||||||
|
static void propertyChange( JTextComponent c, PropertyChangeEvent e ) {
|
||||||
|
switch( e.getPropertyName() ) {
|
||||||
|
case FlatClientProperties.MINIMUM_WIDTH:
|
||||||
|
c.revalidate();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Dimension getPreferredSize( JComponent c ) {
|
public Dimension getPreferredSize( JComponent c ) {
|
||||||
return applyMinimumWidth( super.getPreferredSize( c ) );
|
return applyMinimumWidth( c, super.getPreferredSize( c ), minimumWidth );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Dimension getMinimumSize( JComponent c ) {
|
public Dimension getMinimumSize( JComponent c ) {
|
||||||
return applyMinimumWidth( super.getMinimumSize( c ) );
|
return applyMinimumWidth( c, super.getMinimumSize( c ), minimumWidth );
|
||||||
}
|
}
|
||||||
|
|
||||||
private Dimension applyMinimumWidth( Dimension size ) {
|
static Dimension applyMinimumWidth( JComponent c, Dimension size, int minimumWidth ) {
|
||||||
// Assume that text area is in a scroll pane (that displays the border)
|
// Assume that text area is in a scroll pane (that displays the border)
|
||||||
// and subtract 1px border line width.
|
// and subtract 1px border line width.
|
||||||
// Using "(scale( 1 ) * 2)" instead of "scale( 2 )" to deal with rounding
|
// Using "(scale( 1 ) * 2)" instead of "scale( 2 )" to deal with rounding
|
||||||
// issues. E.g. at scale factor 1.5 the first returns 4, but the second 3.
|
// issues. E.g. at scale factor 1.5 the first returns 4, but the second 3.
|
||||||
|
minimumWidth = FlatUIUtils.minimumWidth( c, minimumWidth );
|
||||||
size.width = Math.max( size.width, scale( minimumWidth ) - (scale( 1 ) * 2) );
|
size.width = Math.max( size.width, scale( minimumWidth ) - (scale( 1 ) * 2) );
|
||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -50,9 +50,10 @@ public class FlatEmptyBorder
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Insets getBorderInsets( Component c, Insets insets ) {
|
public Insets getBorderInsets( Component c, Insets insets ) {
|
||||||
insets.left = scale( left );
|
boolean leftToRight = left == right || c.getComponentOrientation().isLeftToRight();
|
||||||
|
insets.left = scale( leftToRight ? left : right );
|
||||||
insets.top = scale( top );
|
insets.top = scale( top );
|
||||||
insets.right = scale( right );
|
insets.right = scale( leftToRight ? right : left );
|
||||||
insets.bottom = scale( bottom );
|
insets.bottom = scale( bottom );
|
||||||
return insets;
|
return insets;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,23 +16,125 @@
|
|||||||
|
|
||||||
package com.formdev.flatlaf.ui;
|
package com.formdev.flatlaf.ui;
|
||||||
|
|
||||||
|
import java.awt.BorderLayout;
|
||||||
|
import java.awt.Component;
|
||||||
import java.awt.Dimension;
|
import java.awt.Dimension;
|
||||||
|
import java.awt.Insets;
|
||||||
|
import java.io.File;
|
||||||
|
import javax.swing.AbstractButton;
|
||||||
|
import javax.swing.Box;
|
||||||
|
import javax.swing.BoxLayout;
|
||||||
|
import javax.swing.Icon;
|
||||||
|
import javax.swing.ImageIcon;
|
||||||
|
import javax.swing.JButton;
|
||||||
import javax.swing.JComponent;
|
import javax.swing.JComponent;
|
||||||
import javax.swing.JFileChooser;
|
import javax.swing.JFileChooser;
|
||||||
|
import javax.swing.JPanel;
|
||||||
|
import javax.swing.JToggleButton;
|
||||||
|
import javax.swing.UIManager;
|
||||||
|
import javax.swing.filechooser.FileView;
|
||||||
import javax.swing.plaf.ComponentUI;
|
import javax.swing.plaf.ComponentUI;
|
||||||
import javax.swing.plaf.metal.MetalFileChooserUI;
|
import javax.swing.plaf.metal.MetalFileChooserUI;
|
||||||
|
import com.formdev.flatlaf.FlatClientProperties;
|
||||||
|
import com.formdev.flatlaf.util.ScaledImageIcon;
|
||||||
import com.formdev.flatlaf.util.UIScale;
|
import com.formdev.flatlaf.util.UIScale;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides the Flat LaF UI delegate for {@link javax.swing.JFileChooser}.
|
* Provides the Flat LaF UI delegate for {@link javax.swing.JFileChooser}.
|
||||||
*
|
*
|
||||||
* TODO document used UI defaults of superclass
|
* <!-- BasicFileChooserUI -->
|
||||||
|
*
|
||||||
|
* @uiDefault FileView.directoryIcon Icon
|
||||||
|
* @uiDefault FileView.fileIcon Icon
|
||||||
|
* @uiDefault FileView.computerIcon Icon
|
||||||
|
* @uiDefault FileView.hardDriveIcon Icon
|
||||||
|
* @uiDefault FileView.floppyDriveIcon Icon
|
||||||
|
*
|
||||||
|
* @uiDefault FileChooser.newFolderIcon Icon
|
||||||
|
* @uiDefault FileChooser.upFolderIcon Icon
|
||||||
|
* @uiDefault FileChooser.homeFolderIcon Icon
|
||||||
|
* @uiDefault FileChooser.detailsViewIcon Icon
|
||||||
|
* @uiDefault FileChooser.listViewIcon Icon
|
||||||
|
* @uiDefault FileChooser.viewMenuIcon Icon
|
||||||
|
*
|
||||||
|
* @uiDefault FileChooser.usesSingleFilePane boolean
|
||||||
|
* @uiDefault FileChooser.readOnly boolean if true, "New Folder" is disabled
|
||||||
|
*
|
||||||
|
* @uiDefault FileChooser.newFolderErrorText String
|
||||||
|
* @uiDefault FileChooser.newFolderErrorSeparator String
|
||||||
|
* @uiDefault FileChooser.newFolderParentDoesntExistTitleText String
|
||||||
|
* @uiDefault FileChooser.newFolderParentDoesntExistText String
|
||||||
|
* @uiDefault FileChooser.fileDescriptionText String
|
||||||
|
* @uiDefault FileChooser.directoryDescriptionText String
|
||||||
|
* @uiDefault FileChooser.saveButtonText String
|
||||||
|
* @uiDefault FileChooser.openButtonText String
|
||||||
|
* @uiDefault FileChooser.saveDialogTitleText String
|
||||||
|
* @uiDefault FileChooser.openDialogTitleText String
|
||||||
|
* @uiDefault FileChooser.cancelButtonText String
|
||||||
|
* @uiDefault FileChooser.updateButtonText String
|
||||||
|
* @uiDefault FileChooser.helpButtonText String
|
||||||
|
* @uiDefault FileChooser.directoryOpenButtonText String
|
||||||
|
*
|
||||||
|
* @uiDefault FileChooser.saveButtonMnemonic String
|
||||||
|
* @uiDefault FileChooser.openButtonMnemonic String
|
||||||
|
* @uiDefault FileChooser.cancelButtonMnemonic String
|
||||||
|
* @uiDefault FileChooser.updateButtonMnemonic String
|
||||||
|
* @uiDefault FileChooser.helpButtonMnemonic String
|
||||||
|
* @uiDefault FileChooser.directoryOpenButtonMnemonic String
|
||||||
|
*
|
||||||
|
* @uiDefault FileChooser.saveButtonToolTipText String
|
||||||
|
* @uiDefault FileChooser.openButtonToolTipText String
|
||||||
|
* @uiDefault FileChooser.cancelButtonToolTipText String
|
||||||
|
* @uiDefault FileChooser.updateButtonToolTipText String
|
||||||
|
* @uiDefault FileChooser.helpButtonToolTipText String
|
||||||
|
* @uiDefault FileChooser.directoryOpenButtonToolTipText String
|
||||||
|
*
|
||||||
|
* @uiDefault FileChooser.acceptAllFileFilterText String
|
||||||
|
*
|
||||||
|
* <!-- MetalFileChooserUI -->
|
||||||
|
*
|
||||||
|
* @uiDefault FileChooser.lookInLabelMnemonic String
|
||||||
|
* @uiDefault FileChooser.lookInLabelText String
|
||||||
|
* @uiDefault FileChooser.saveInLabelText String
|
||||||
|
* @uiDefault FileChooser.fileNameLabelMnemonic String
|
||||||
|
* @uiDefault FileChooser.fileNameLabelText String
|
||||||
|
* @uiDefault FileChooser.folderNameLabelMnemonic String
|
||||||
|
* @uiDefault FileChooser.folderNameLabelText String
|
||||||
|
* @uiDefault FileChooser.filesOfTypeLabelMnemonic String
|
||||||
|
* @uiDefault FileChooser.filesOfTypeLabelText String
|
||||||
|
*
|
||||||
|
* @uiDefault FileChooser.upFolderToolTipText String
|
||||||
|
* @uiDefault FileChooser.upFolderAccessibleName String
|
||||||
|
* @uiDefault FileChooser.homeFolderToolTipText String
|
||||||
|
* @uiDefault FileChooser.homeFolderAccessibleName String
|
||||||
|
* @uiDefault FileChooser.newFolderToolTipText String
|
||||||
|
* @uiDefault FileChooser.newFolderAccessibleName String
|
||||||
|
* @uiDefault FileChooser.listViewButtonToolTipText String
|
||||||
|
* @uiDefault FileChooser.listViewButtonAccessibleName String
|
||||||
|
* @uiDefault FileChooser.detailsViewButtonToolTipText String
|
||||||
|
* @uiDefault FileChooser.detailsViewButtonAccessibleName String
|
||||||
|
*
|
||||||
|
* <!-- FilePane -->
|
||||||
|
*
|
||||||
|
* @uiDefault FileChooser.fileNameHeaderText String
|
||||||
|
* @uiDefault FileChooser.fileSizeHeaderText String
|
||||||
|
* @uiDefault FileChooser.fileTypeHeaderText String
|
||||||
|
* @uiDefault FileChooser.fileDateHeaderText String
|
||||||
|
* @uiDefault FileChooser.fileAttrHeaderText String
|
||||||
|
*
|
||||||
|
* @uiDefault FileChooser.viewMenuLabelText String
|
||||||
|
* @uiDefault FileChooser.refreshActionLabelText String
|
||||||
|
* @uiDefault FileChooser.newFolderActionLabelText String
|
||||||
|
* @uiDefault FileChooser.listViewActionLabelText String
|
||||||
|
* @uiDefault FileChooser.detailsViewActionLabelText String
|
||||||
*
|
*
|
||||||
* @author Karl Tauber
|
* @author Karl Tauber
|
||||||
*/
|
*/
|
||||||
public class FlatFileChooserUI
|
public class FlatFileChooserUI
|
||||||
extends MetalFileChooserUI
|
extends MetalFileChooserUI
|
||||||
{
|
{
|
||||||
|
private final FlatFileView fileView = new FlatFileView();
|
||||||
|
|
||||||
public static ComponentUI createUI( JComponent c ) {
|
public static ComponentUI createUI( JComponent c ) {
|
||||||
return new FlatFileChooserUI( (JFileChooser) c );
|
return new FlatFileChooserUI( (JFileChooser) c );
|
||||||
}
|
}
|
||||||
@@ -41,6 +143,40 @@ public class FlatFileChooserUI
|
|||||||
super( filechooser );
|
super( filechooser );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void installComponents( JFileChooser fc ) {
|
||||||
|
super.installComponents( fc );
|
||||||
|
|
||||||
|
patchUI( fc );
|
||||||
|
}
|
||||||
|
|
||||||
|
private void patchUI( JFileChooser fc ) {
|
||||||
|
// turn top-right buttons into toolbar buttons
|
||||||
|
Component topPanel = fc.getComponent( 0 );
|
||||||
|
if( (topPanel instanceof JPanel) &&
|
||||||
|
(((JPanel)topPanel).getLayout() instanceof BorderLayout) )
|
||||||
|
{
|
||||||
|
Component topButtonPanel = ((JPanel)topPanel).getComponent( 0 );
|
||||||
|
if( (topButtonPanel instanceof JPanel) &&
|
||||||
|
(((JPanel)topButtonPanel).getLayout() instanceof BoxLayout) )
|
||||||
|
{
|
||||||
|
Insets margin = UIManager.getInsets( "Button.margin" );
|
||||||
|
Component[] comps = ((JPanel)topButtonPanel).getComponents();
|
||||||
|
for( int i = comps.length - 1; i >= 0; i-- ) {
|
||||||
|
Component c = comps[i];
|
||||||
|
if( c instanceof JButton || c instanceof JToggleButton ) {
|
||||||
|
AbstractButton b = (AbstractButton)c;
|
||||||
|
b.putClientProperty( FlatClientProperties.BUTTON_TYPE,
|
||||||
|
FlatClientProperties.BUTTON_TYPE_TOOLBAR_BUTTON );
|
||||||
|
b.setMargin( margin );
|
||||||
|
b.setFocusable( false );
|
||||||
|
} else if( c instanceof Box.Filler )
|
||||||
|
((JPanel)topButtonPanel).remove( i );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Dimension getPreferredSize( JComponent c ) {
|
public Dimension getPreferredSize( JComponent c ) {
|
||||||
return UIScale.scale( super.getPreferredSize( c ) );
|
return UIScale.scale( super.getPreferredSize( c ) );
|
||||||
@@ -50,4 +186,50 @@ public class FlatFileChooserUI
|
|||||||
public Dimension getMinimumSize( JComponent c ) {
|
public Dimension getMinimumSize( JComponent c ) {
|
||||||
return UIScale.scale( super.getMinimumSize( c ) );
|
return UIScale.scale( super.getMinimumSize( c ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public FileView getFileView( JFileChooser fc ) {
|
||||||
|
return fileView;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void clearIconCache() {
|
||||||
|
fileView.clearIconCache();
|
||||||
|
}
|
||||||
|
|
||||||
|
//---- class FlatFileView -------------------------------------------------
|
||||||
|
|
||||||
|
private class FlatFileView
|
||||||
|
extends BasicFileView
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public Icon getIcon( File f ) {
|
||||||
|
// get cached icon
|
||||||
|
Icon icon = getCachedIcon( f );
|
||||||
|
if( icon != null )
|
||||||
|
return icon;
|
||||||
|
|
||||||
|
// get system icon
|
||||||
|
if( f != null ) {
|
||||||
|
icon = getFileChooser().getFileSystemView().getSystemIcon( f );
|
||||||
|
|
||||||
|
if( icon != null ) {
|
||||||
|
if( icon instanceof ImageIcon )
|
||||||
|
icon = new ScaledImageIcon( (ImageIcon) icon );
|
||||||
|
cacheIcon( f, icon );
|
||||||
|
return icon;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// get default icon
|
||||||
|
icon = super.getIcon( f );
|
||||||
|
|
||||||
|
if( icon instanceof ImageIcon ) {
|
||||||
|
icon = new ScaledImageIcon( (ImageIcon) icon );
|
||||||
|
cacheIcon( f, icon );
|
||||||
|
}
|
||||||
|
|
||||||
|
return icon;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,10 +39,12 @@ import javax.swing.plaf.ComponentUI;
|
|||||||
*
|
*
|
||||||
* <!-- FlatTextFieldUI -->
|
* <!-- FlatTextFieldUI -->
|
||||||
*
|
*
|
||||||
|
* @uiDefault TextComponent.arc int
|
||||||
* @uiDefault Component.focusWidth int
|
* @uiDefault Component.focusWidth int
|
||||||
* @uiDefault Component.minimumWidth int
|
* @uiDefault Component.minimumWidth int
|
||||||
* @uiDefault Component.isIntelliJTheme boolean
|
* @uiDefault Component.isIntelliJTheme boolean
|
||||||
* @uiDefault FormattedTextField.placeholderForeground Color
|
* @uiDefault FormattedTextField.placeholderForeground Color
|
||||||
|
* @uiDefault TextComponent.selectAllOnFocusPolicy String never, once (default) or always
|
||||||
*
|
*
|
||||||
* @author Karl Tauber
|
* @author Karl Tauber
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -0,0 +1,190 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2020 FormDev Software GmbH
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.formdev.flatlaf.ui;
|
||||||
|
|
||||||
|
import java.awt.BorderLayout;
|
||||||
|
import java.awt.Color;
|
||||||
|
import java.awt.Dimension;
|
||||||
|
import java.awt.Graphics;
|
||||||
|
import java.awt.LayoutManager;
|
||||||
|
import java.beans.PropertyChangeEvent;
|
||||||
|
import java.beans.PropertyChangeListener;
|
||||||
|
import javax.swing.BorderFactory;
|
||||||
|
import javax.swing.BoxLayout;
|
||||||
|
import javax.swing.Icon;
|
||||||
|
import javax.swing.JInternalFrame;
|
||||||
|
import javax.swing.JLabel;
|
||||||
|
import javax.swing.JPanel;
|
||||||
|
import javax.swing.LookAndFeel;
|
||||||
|
import javax.swing.UIManager;
|
||||||
|
import javax.swing.border.Border;
|
||||||
|
import javax.swing.plaf.basic.BasicInternalFrameTitlePane;
|
||||||
|
import com.formdev.flatlaf.util.UIScale;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides the Flat LaF internal frame title bar.
|
||||||
|
*
|
||||||
|
* @author Karl Tauber
|
||||||
|
*/
|
||||||
|
public class FlatInternalFrameTitlePane
|
||||||
|
extends BasicInternalFrameTitlePane
|
||||||
|
{
|
||||||
|
private JLabel titleLabel;
|
||||||
|
private JPanel buttonPanel;
|
||||||
|
|
||||||
|
public FlatInternalFrameTitlePane( JInternalFrame f ) {
|
||||||
|
super( f );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void installDefaults() {
|
||||||
|
super.installDefaults();
|
||||||
|
|
||||||
|
LookAndFeel.installBorder( this, "InternalFrameTitlePane.border" );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected PropertyChangeListener createPropertyChangeListener() {
|
||||||
|
return new FlatPropertyChangeHandler();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected LayoutManager createLayout() {
|
||||||
|
return new BorderLayout( UIScale.scale( 4 ), 0 );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void createButtons() {
|
||||||
|
super.createButtons();
|
||||||
|
|
||||||
|
iconButton.setContentAreaFilled( false );
|
||||||
|
maxButton.setContentAreaFilled( false );
|
||||||
|
closeButton.setContentAreaFilled( false );
|
||||||
|
|
||||||
|
Border emptyBorder = BorderFactory.createEmptyBorder();
|
||||||
|
iconButton.setBorder( emptyBorder );
|
||||||
|
maxButton.setBorder( emptyBorder );
|
||||||
|
closeButton.setBorder( emptyBorder );
|
||||||
|
|
||||||
|
updateButtonsVisibility();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void addSubComponents() {
|
||||||
|
titleLabel = new JLabel( frame.getTitle() );
|
||||||
|
titleLabel.setFont( FlatUIUtils.nonUIResource( getFont() ) );
|
||||||
|
titleLabel.setMinimumSize( new Dimension( UIScale.scale( 32 ), 1 ) );
|
||||||
|
updateFrameIcon();
|
||||||
|
updateColors();
|
||||||
|
|
||||||
|
buttonPanel = new JPanel();
|
||||||
|
buttonPanel.setLayout( new BoxLayout( buttonPanel, BoxLayout.LINE_AXIS ) );
|
||||||
|
buttonPanel.setOpaque( false );
|
||||||
|
|
||||||
|
buttonPanel.add( iconButton );
|
||||||
|
buttonPanel.add( maxButton );
|
||||||
|
buttonPanel.add( closeButton );
|
||||||
|
|
||||||
|
add( titleLabel, BorderLayout.CENTER );
|
||||||
|
add( buttonPanel, BorderLayout.LINE_END );
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateFrameIcon() {
|
||||||
|
Icon frameIcon = frame.getFrameIcon();
|
||||||
|
if( frameIcon == UIManager.getIcon( "InternalFrame.icon" ) )
|
||||||
|
frameIcon = null;
|
||||||
|
titleLabel.setIcon( frameIcon );
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateColors() {
|
||||||
|
Color background = FlatUIUtils.nonUIResource( frame.isSelected() ? selectedTitleColor : notSelectedTitleColor );
|
||||||
|
Color foreground = FlatUIUtils.nonUIResource( frame.isSelected() ? selectedTextColor : notSelectedTextColor );
|
||||||
|
|
||||||
|
titleLabel.setForeground( foreground );
|
||||||
|
iconButton.setBackground( background );
|
||||||
|
iconButton.setForeground( foreground );
|
||||||
|
maxButton.setBackground( background );
|
||||||
|
maxButton.setForeground( foreground );
|
||||||
|
closeButton.setBackground( background );
|
||||||
|
closeButton.setForeground( foreground );
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateButtonsVisibility() {
|
||||||
|
iconButton.setVisible( frame.isIconifiable() );
|
||||||
|
maxButton.setVisible( frame.isMaximizable() );
|
||||||
|
closeButton.setVisible( frame.isClosable() );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Does nothing because FlatLaf internal frames do not have system menus.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected void assembleSystemMenu() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Does nothing because FlatLaf internal frames do not have system menus.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected void showSystemMenu() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void paintComponent( Graphics g ) {
|
||||||
|
paintTitleBackground( g );
|
||||||
|
}
|
||||||
|
|
||||||
|
//---- class FlatPropertyChangeHandler ------------------------------------
|
||||||
|
|
||||||
|
private class FlatPropertyChangeHandler
|
||||||
|
extends PropertyChangeHandler
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void propertyChange( PropertyChangeEvent e ) {
|
||||||
|
switch( e.getPropertyName() ) {
|
||||||
|
case JInternalFrame.TITLE_PROPERTY:
|
||||||
|
titleLabel.setText( frame.getTitle() );
|
||||||
|
break;
|
||||||
|
|
||||||
|
case JInternalFrame.FRAME_ICON_PROPERTY:
|
||||||
|
updateFrameIcon();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case JInternalFrame.IS_SELECTED_PROPERTY:
|
||||||
|
updateColors();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "iconable":
|
||||||
|
case "maximizable":
|
||||||
|
case "closable":
|
||||||
|
updateButtonsVisibility();
|
||||||
|
enableActions();
|
||||||
|
revalidate();
|
||||||
|
repaint();
|
||||||
|
|
||||||
|
// do not invoke super.propertyChange() because this adds/removes the buttons
|
||||||
|
return;
|
||||||
|
|
||||||
|
case "componentOrientation":
|
||||||
|
applyComponentOrientation( frame.getComponentOrientation() );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
super.propertyChange( e );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,180 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2020 FormDev Software GmbH
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.formdev.flatlaf.ui;
|
||||||
|
|
||||||
|
import static com.formdev.flatlaf.util.UIScale.scale;
|
||||||
|
import java.awt.Color;
|
||||||
|
import java.awt.Component;
|
||||||
|
import java.awt.Graphics;
|
||||||
|
import java.awt.Graphics2D;
|
||||||
|
import java.awt.Insets;
|
||||||
|
import javax.swing.JComponent;
|
||||||
|
import javax.swing.JInternalFrame;
|
||||||
|
import javax.swing.LookAndFeel;
|
||||||
|
import javax.swing.UIManager;
|
||||||
|
import javax.swing.plaf.ComponentUI;
|
||||||
|
import javax.swing.plaf.basic.BasicInternalFrameUI;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides the Flat LaF UI delegate for {@link javax.swing.JInternalFrame}.
|
||||||
|
*
|
||||||
|
* <!-- BasicInternalFrameUI -->
|
||||||
|
*
|
||||||
|
* @uiDefault control Color
|
||||||
|
* @uiDefault InternalFrame.icon Icon
|
||||||
|
* @uiDefault InternalFrame.border Border
|
||||||
|
* @uiDefault InternalFrame.layoutTitlePaneAtOrigin boolean
|
||||||
|
*
|
||||||
|
* <!-- BasicInternalFrameTitlePane -->
|
||||||
|
*
|
||||||
|
* @uiDefault InternalFrame.titleFont Font
|
||||||
|
* @uiDefault InternalFrame.icon Icon
|
||||||
|
* @uiDefault InternalFrame.maximizeIcon Icon
|
||||||
|
* @uiDefault InternalFrame.minimizeIcon Icon
|
||||||
|
* @uiDefault InternalFrame.iconifyIcon Icon
|
||||||
|
* @uiDefault InternalFrame.closeIcon Icon
|
||||||
|
* @uiDefault InternalFrame.activeTitleBackground Color
|
||||||
|
* @uiDefault InternalFrame.activeTitleForeground Color
|
||||||
|
* @uiDefault InternalFrame.inactiveTitleBackground Color
|
||||||
|
* @uiDefault InternalFrame.inactiveTitleForeground Color
|
||||||
|
* @uiDefault InternalFrame.closeButtonToolTip String
|
||||||
|
* @uiDefault InternalFrame.iconButtonToolTip String
|
||||||
|
* @uiDefault InternalFrame.restoreButtonToolTip String
|
||||||
|
* @uiDefault InternalFrame.maxButtonToolTip String
|
||||||
|
* @uiDefault InternalFrameTitlePane.closeButtonText String
|
||||||
|
* @uiDefault InternalFrameTitlePane.minimizeButtonText String
|
||||||
|
* @uiDefault InternalFrameTitlePane.restoreButtonText String
|
||||||
|
* @uiDefault InternalFrameTitlePane.maximizeButtonText String
|
||||||
|
* @uiDefault InternalFrameTitlePane.moveButtonText String
|
||||||
|
* @uiDefault InternalFrameTitlePane.sizeButtonText String
|
||||||
|
* @uiDefault InternalFrameTitlePane.closeButton.mnemonic Integer
|
||||||
|
* @uiDefault InternalFrameTitlePane.minimizeButton.mnemonic Integer
|
||||||
|
* @uiDefault InternalFrameTitlePane.restoreButton.mnemonic Integer
|
||||||
|
* @uiDefault InternalFrameTitlePane.maximizeButton.mnemonic Integer
|
||||||
|
* @uiDefault InternalFrameTitlePane.moveButton.mnemonic Integer
|
||||||
|
* @uiDefault InternalFrameTitlePane.sizeButton.mnemonic Integer
|
||||||
|
*
|
||||||
|
* <!-- FlatInternalFrameUI -->
|
||||||
|
*
|
||||||
|
* @uiDefault InternalFrame.activeBorderColor Color
|
||||||
|
* @uiDefault InternalFrame.inactiveBorderColor Color
|
||||||
|
* @uiDefault InternalFrame.borderLineWidth int
|
||||||
|
* @uiDefault InternalFrame.borderMargins Insets
|
||||||
|
*
|
||||||
|
* <!-- FlatInternalFrameTitlePane -->
|
||||||
|
*
|
||||||
|
* @uiDefault InternalFrameTitlePane.border Border
|
||||||
|
*
|
||||||
|
* @author Karl Tauber
|
||||||
|
*/
|
||||||
|
public class FlatInternalFrameUI
|
||||||
|
extends BasicInternalFrameUI
|
||||||
|
{
|
||||||
|
public static ComponentUI createUI( JComponent c ) {
|
||||||
|
return new FlatInternalFrameUI( (JInternalFrame) c );
|
||||||
|
}
|
||||||
|
|
||||||
|
public FlatInternalFrameUI( JInternalFrame b ) {
|
||||||
|
super( b );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void installUI( JComponent c ) {
|
||||||
|
super.installUI( c );
|
||||||
|
|
||||||
|
LookAndFeel.installProperty( frame, "opaque", false );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected JComponent createNorthPane( JInternalFrame w ) {
|
||||||
|
return new FlatInternalFrameTitlePane( w );
|
||||||
|
}
|
||||||
|
|
||||||
|
//---- class FlatInternalFrameBorder --------------------------------------
|
||||||
|
|
||||||
|
public static class FlatInternalFrameBorder
|
||||||
|
extends FlatEmptyBorder
|
||||||
|
{
|
||||||
|
private final Color activeBorderColor = UIManager.getColor( "InternalFrame.activeBorderColor" );
|
||||||
|
private final Color inactiveBorderColor = UIManager.getColor( "InternalFrame.inactiveBorderColor" );
|
||||||
|
private final int borderLineWidth = FlatUIUtils.getUIInt( "InternalFrame.borderLineWidth", 1 );
|
||||||
|
private final boolean dropShadowPainted = UIManager.getBoolean( "InternalFrame.dropShadowPainted" );
|
||||||
|
|
||||||
|
private final FlatDropShadowBorder activeDropShadowBorder = new FlatDropShadowBorder(
|
||||||
|
UIManager.getColor( "InternalFrame.activeDropShadowColor" ),
|
||||||
|
UIManager.getInsets( "InternalFrame.activeDropShadowInsets" ),
|
||||||
|
FlatUIUtils.getUIFloat( "InternalFrame.activeDropShadowOpacity", 0.5f ) );
|
||||||
|
private final FlatDropShadowBorder inactiveDropShadowBorder = new FlatDropShadowBorder(
|
||||||
|
UIManager.getColor( "InternalFrame.inactiveDropShadowColor" ),
|
||||||
|
UIManager.getInsets( "InternalFrame.inactiveDropShadowInsets" ),
|
||||||
|
FlatUIUtils.getUIFloat( "InternalFrame.inactiveDropShadowOpacity", 0.5f ) );
|
||||||
|
|
||||||
|
public FlatInternalFrameBorder() {
|
||||||
|
super( UIManager.getInsets( "InternalFrame.borderMargins" ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Insets getBorderInsets( Component c, Insets insets ) {
|
||||||
|
if( c instanceof JInternalFrame && ((JInternalFrame)c).isMaximum() ) {
|
||||||
|
insets.left = scale( Math.min( borderLineWidth, left ) );
|
||||||
|
insets.top = scale( Math.min( borderLineWidth, top ) );
|
||||||
|
insets.right = scale( Math.min( borderLineWidth, right ) );
|
||||||
|
insets.bottom = scale( Math.min( borderLineWidth, bottom ) );
|
||||||
|
return insets;
|
||||||
|
}
|
||||||
|
|
||||||
|
return super.getBorderInsets( c, insets );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void paintBorder( Component c, Graphics g, int x, int y, int width, int height ) {
|
||||||
|
JInternalFrame f = (JInternalFrame) c;
|
||||||
|
|
||||||
|
Insets insets = getBorderInsets( c );
|
||||||
|
float lineWidth = scale( (float) borderLineWidth );
|
||||||
|
|
||||||
|
float rx = x + insets.left - lineWidth;
|
||||||
|
float ry = y + insets.top - lineWidth;
|
||||||
|
float rwidth = width - insets.left - insets.right + (lineWidth * 2);
|
||||||
|
float rheight = height - insets.top - insets.bottom + (lineWidth * 2);
|
||||||
|
|
||||||
|
Graphics2D g2 = (Graphics2D) g.create();
|
||||||
|
try {
|
||||||
|
FlatUIUtils.setRenderingHints( g2 );
|
||||||
|
g2.setColor( f.isSelected() ? activeBorderColor : inactiveBorderColor );
|
||||||
|
|
||||||
|
// paint drop shadow
|
||||||
|
if( dropShadowPainted ) {
|
||||||
|
FlatDropShadowBorder dropShadowBorder = f.isSelected()
|
||||||
|
? activeDropShadowBorder : inactiveDropShadowBorder;
|
||||||
|
|
||||||
|
Insets dropShadowInsets = dropShadowBorder.getBorderInsets();
|
||||||
|
dropShadowBorder.paintBorder( c, g2,
|
||||||
|
(int) rx - dropShadowInsets.left,
|
||||||
|
(int) ry - dropShadowInsets.top,
|
||||||
|
(int) rwidth + dropShadowInsets.left + dropShadowInsets.right,
|
||||||
|
(int) rheight + dropShadowInsets.top + dropShadowInsets.bottom );
|
||||||
|
}
|
||||||
|
|
||||||
|
// paint border
|
||||||
|
g2.fill( FlatUIUtils.createRectangle( rx, ry, rwidth, rheight, lineWidth ) );
|
||||||
|
} finally {
|
||||||
|
g2.dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -20,12 +20,14 @@ import java.awt.Color;
|
|||||||
import java.awt.FontMetrics;
|
import java.awt.FontMetrics;
|
||||||
import java.awt.Graphics;
|
import java.awt.Graphics;
|
||||||
import java.awt.Rectangle;
|
import java.awt.Rectangle;
|
||||||
|
import java.beans.PropertyChangeEvent;
|
||||||
import javax.swing.Icon;
|
import javax.swing.Icon;
|
||||||
import javax.swing.JComponent;
|
import javax.swing.JComponent;
|
||||||
import javax.swing.JLabel;
|
import javax.swing.JLabel;
|
||||||
import javax.swing.SwingUtilities;
|
import javax.swing.SwingUtilities;
|
||||||
import javax.swing.UIManager;
|
import javax.swing.UIManager;
|
||||||
import javax.swing.plaf.ComponentUI;
|
import javax.swing.plaf.ComponentUI;
|
||||||
|
import javax.swing.plaf.basic.BasicHTML;
|
||||||
import javax.swing.plaf.basic.BasicLabelUI;
|
import javax.swing.plaf.basic.BasicLabelUI;
|
||||||
import com.formdev.flatlaf.FlatLaf;
|
import com.formdev.flatlaf.FlatLaf;
|
||||||
import com.formdev.flatlaf.util.UIScale;
|
import com.formdev.flatlaf.util.UIScale;
|
||||||
@@ -77,6 +79,51 @@ public class FlatLabelUI
|
|||||||
defaults_initialized = false;
|
defaults_initialized = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void installComponents( JLabel c ) {
|
||||||
|
super.installComponents( c );
|
||||||
|
|
||||||
|
// update HTML renderer if necessary
|
||||||
|
updateHTMLRenderer( c, c.getText(), false );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void propertyChange( PropertyChangeEvent e ) {
|
||||||
|
String name = e.getPropertyName();
|
||||||
|
if( name == "text" || name == "font" || name == "foreground" ) {
|
||||||
|
JLabel label = (JLabel) e.getSource();
|
||||||
|
updateHTMLRenderer( label, label.getText(), true );
|
||||||
|
} else
|
||||||
|
super.propertyChange( e );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether text contains HTML headings and adds a special CSS rule to
|
||||||
|
* re-calculate heading font sizes based on current component font size.
|
||||||
|
*/
|
||||||
|
static void updateHTMLRenderer( JComponent c, String text, boolean always ) {
|
||||||
|
if( BasicHTML.isHTMLString( text ) &&
|
||||||
|
c.getClientProperty( "html.disable" ) != Boolean.TRUE &&
|
||||||
|
text.contains( "<h" ) &&
|
||||||
|
(text.contains( "<h1" ) || text.contains( "<h2" ) || text.contains( "<h3" ) ||
|
||||||
|
text.contains( "<h4" ) || text.contains( "<h5" ) || text.contains( "<h6" )) )
|
||||||
|
{
|
||||||
|
int headIndex = text.indexOf( "<head>" );
|
||||||
|
|
||||||
|
String style = "<style>BASE_SIZE " + c.getFont().getSize() + "</style>";
|
||||||
|
if( headIndex < 0 )
|
||||||
|
style = "<head>" + style + "</head>";
|
||||||
|
|
||||||
|
int insertIndex = headIndex >= 0 ? (headIndex + "<head>".length()) : "<html>".length();
|
||||||
|
text = text.substring( 0, insertIndex )
|
||||||
|
+ style
|
||||||
|
+ text.substring( insertIndex );
|
||||||
|
} else if( !always )
|
||||||
|
return; // not necessary to invoke BasicHTML.updateRenderer()
|
||||||
|
|
||||||
|
BasicHTML.updateRenderer( c, text );
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void paintEnabledText( JLabel l, Graphics g, String s, int textX, int textY ) {
|
protected void paintEnabledText( JLabel l, Graphics g, String s, int textX, int textY ) {
|
||||||
int mnemIndex = FlatLaf.isShowMnemonics() ? l.getDisplayedMnemonicIndex() : -1;
|
int mnemIndex = FlatLaf.isShowMnemonics() ? l.getDisplayedMnemonicIndex() : -1;
|
||||||
|
|||||||
@@ -26,9 +26,9 @@ import java.awt.Insets;
|
|||||||
/**
|
/**
|
||||||
* Line border for various components.
|
* Line border for various components.
|
||||||
*
|
*
|
||||||
* Paints a scaled 1px thick line around the component.
|
* Paints a scaled (usually 1px thick) line around the component.
|
||||||
* The line thickness is not included in the border insets.
|
* The line thickness is not added to the border insets.
|
||||||
* The insets should be at least 1,1,1,1.
|
* The insets should be at least have line thickness (usually 1,1,1,1).
|
||||||
*
|
*
|
||||||
* @author Karl Tauber
|
* @author Karl Tauber
|
||||||
*/
|
*/
|
||||||
@@ -36,10 +36,24 @@ public class FlatLineBorder
|
|||||||
extends FlatEmptyBorder
|
extends FlatEmptyBorder
|
||||||
{
|
{
|
||||||
private final Color lineColor;
|
private final Color lineColor;
|
||||||
|
private final float lineThickness;
|
||||||
|
|
||||||
public FlatLineBorder( Insets insets, Color lineColor ) {
|
public FlatLineBorder( Insets insets, Color lineColor ) {
|
||||||
|
this( insets, lineColor, 1f );
|
||||||
|
}
|
||||||
|
|
||||||
|
public FlatLineBorder( Insets insets, Color lineColor, float lineThickness ) {
|
||||||
super( insets );
|
super( insets );
|
||||||
this.lineColor = lineColor;
|
this.lineColor = lineColor;
|
||||||
|
this.lineThickness = lineThickness;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Color getLineColor() {
|
||||||
|
return lineColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float getLineThickness() {
|
||||||
|
return lineThickness;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -48,7 +62,7 @@ public class FlatLineBorder
|
|||||||
try {
|
try {
|
||||||
FlatUIUtils.setRenderingHints( g2 );
|
FlatUIUtils.setRenderingHints( g2 );
|
||||||
g2.setColor( lineColor );
|
g2.setColor( lineColor );
|
||||||
FlatUIUtils.paintComponentBorder( g2, x, y, width, height, 0f, scale( 1f ), 0f );
|
FlatUIUtils.paintComponentBorder( g2, x, y, width, height, 0f, scale( lineThickness ), 0f );
|
||||||
} finally {
|
} finally {
|
||||||
g2.dispose();
|
g2.dispose();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,88 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2019 FormDev Software GmbH
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.formdev.flatlaf.ui;
|
||||||
|
|
||||||
|
import java.awt.Component;
|
||||||
|
import java.awt.Graphics;
|
||||||
|
import javax.swing.JList;
|
||||||
|
import javax.swing.SwingUtilities;
|
||||||
|
import javax.swing.UIManager;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cell border for {@link javax.swing.DefaultListCellRenderer}
|
||||||
|
* (used by {@link javax.swing.JList}).
|
||||||
|
* <p>
|
||||||
|
* Uses separate cell margins from UI defaults to allow easy customizing.
|
||||||
|
*
|
||||||
|
* @author Karl Tauber
|
||||||
|
*/
|
||||||
|
public class FlatListCellBorder
|
||||||
|
extends FlatLineBorder
|
||||||
|
{
|
||||||
|
final boolean showCellFocusIndicator = UIManager.getBoolean( "List.showCellFocusIndicator" );
|
||||||
|
|
||||||
|
protected FlatListCellBorder() {
|
||||||
|
super( UIManager.getInsets( "List.cellMargins" ), UIManager.getColor( "List.cellFocusColor" ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
//---- class Default ------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Border for unselected cell that uses margins, but does not paint focus indicator border.
|
||||||
|
*/
|
||||||
|
public static class Default
|
||||||
|
extends FlatListCellBorder
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void paintBorder( Component c, Graphics g, int x, int y, int width, int height ) {
|
||||||
|
// do not paint focus indicator border
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//---- class Focused ------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Border for focused unselected cell that uses margins and paints focus indicator border.
|
||||||
|
*/
|
||||||
|
public static class Focused
|
||||||
|
extends FlatListCellBorder
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
//---- class Selected -----------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Border for selected cell that uses margins and paints focus indicator border
|
||||||
|
* if enabled (List.showCellFocusIndicator=true) and exactly one item is selected.
|
||||||
|
*/
|
||||||
|
public static class Selected
|
||||||
|
extends FlatListCellBorder
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void paintBorder( Component c, Graphics g, int x, int y, int width, int height ) {
|
||||||
|
if( !showCellFocusIndicator )
|
||||||
|
return;
|
||||||
|
|
||||||
|
// paint focus indicator border only if exactly one item is selected
|
||||||
|
JList<?> list = (JList<?>) SwingUtilities.getAncestorOfClass( JList.class, c );
|
||||||
|
if( list != null && list.getMinSelectionIndex() == list.getMaxSelectionIndex() )
|
||||||
|
return;
|
||||||
|
|
||||||
|
super.paintBorder( c, g, x, y, width, height );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -52,6 +52,12 @@ import javax.swing.plaf.basic.BasicListUI;
|
|||||||
* @uiDefault List.selectionInactiveBackground Color
|
* @uiDefault List.selectionInactiveBackground Color
|
||||||
* @uiDefault List.selectionInactiveForeground Color
|
* @uiDefault List.selectionInactiveForeground Color
|
||||||
*
|
*
|
||||||
|
* <!-- FlatListCellBorder -->
|
||||||
|
*
|
||||||
|
* @uiDefault List.cellMargins Insets
|
||||||
|
* @uiDefault List.cellFocusColor Color
|
||||||
|
* @uiDefault List.showCellFocusIndicator boolean
|
||||||
|
*
|
||||||
* @author Karl Tauber
|
* @author Karl Tauber
|
||||||
*/
|
*/
|
||||||
public class FlatListUI
|
public class FlatListUI
|
||||||
@@ -75,7 +81,7 @@ public class FlatListUI
|
|||||||
selectionInactiveBackground = UIManager.getColor( "List.selectionInactiveBackground" );
|
selectionInactiveBackground = UIManager.getColor( "List.selectionInactiveBackground" );
|
||||||
selectionInactiveForeground = UIManager.getColor( "List.selectionInactiveForeground" );
|
selectionInactiveForeground = UIManager.getColor( "List.selectionInactiveForeground" );
|
||||||
|
|
||||||
toggleSelectionColors( list.hasFocus() );
|
toggleSelectionColors();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -94,13 +100,13 @@ public class FlatListUI
|
|||||||
@Override
|
@Override
|
||||||
public void focusGained( FocusEvent e ) {
|
public void focusGained( FocusEvent e ) {
|
||||||
super.focusGained( e );
|
super.focusGained( e );
|
||||||
toggleSelectionColors( true );
|
toggleSelectionColors();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void focusLost( FocusEvent e ) {
|
public void focusLost( FocusEvent e ) {
|
||||||
super.focusLost( e );
|
super.focusLost( e );
|
||||||
toggleSelectionColors( false );
|
toggleSelectionColors();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -114,8 +120,8 @@ public class FlatListUI
|
|||||||
* already used in applications. Then either the inactive colors are not used,
|
* already used in applications. Then either the inactive colors are not used,
|
||||||
* or the application has to be changed to extend a FlatLaf renderer.
|
* or the application has to be changed to extend a FlatLaf renderer.
|
||||||
*/
|
*/
|
||||||
private void toggleSelectionColors( boolean focused ) {
|
private void toggleSelectionColors() {
|
||||||
if( focused ) {
|
if( FlatUIUtils.isPermanentFocusOwner( list ) ) {
|
||||||
if( list.getSelectionBackground() == selectionInactiveBackground )
|
if( list.getSelectionBackground() == selectionInactiveBackground )
|
||||||
list.setSelectionBackground( selectionBackground );
|
list.setSelectionBackground( selectionBackground );
|
||||||
if( list.getSelectionForeground() == selectionInactiveForeground )
|
if( list.getSelectionForeground() == selectionInactiveForeground )
|
||||||
|
|||||||
@@ -29,13 +29,26 @@ import javax.swing.plaf.basic.BasicBorders;
|
|||||||
public class FlatMarginBorder
|
public class FlatMarginBorder
|
||||||
extends BasicBorders.MarginBorder
|
extends BasicBorders.MarginBorder
|
||||||
{
|
{
|
||||||
|
private final int left, right, top, bottom;
|
||||||
|
|
||||||
|
public FlatMarginBorder() {
|
||||||
|
left = right = top = bottom = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public FlatMarginBorder( Insets insets ) {
|
||||||
|
left = insets.left;
|
||||||
|
top = insets.top;
|
||||||
|
right = insets.right;
|
||||||
|
bottom = insets.bottom;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Insets getBorderInsets( Component c, Insets insets ) {
|
public Insets getBorderInsets( Component c, Insets insets ) {
|
||||||
insets = super.getBorderInsets( c, insets );
|
insets = super.getBorderInsets( c, insets );
|
||||||
insets.top = scale( insets.top );
|
insets.top = scale( insets.top + top );
|
||||||
insets.left = scale( insets.left );
|
insets.left = scale( insets.left + left );
|
||||||
insets.bottom = scale( insets.bottom );
|
insets.bottom = scale( insets.bottom + bottom );
|
||||||
insets.right = scale( insets.right );
|
insets.right = scale( insets.right + right );
|
||||||
return insets;
|
return insets;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,9 +16,20 @@
|
|||||||
|
|
||||||
package com.formdev.flatlaf.ui;
|
package com.formdev.flatlaf.ui;
|
||||||
|
|
||||||
|
import java.awt.event.ActionEvent;
|
||||||
|
import javax.swing.AbstractAction;
|
||||||
|
import javax.swing.ActionMap;
|
||||||
import javax.swing.JComponent;
|
import javax.swing.JComponent;
|
||||||
|
import javax.swing.JMenu;
|
||||||
|
import javax.swing.JMenuBar;
|
||||||
|
import javax.swing.MenuElement;
|
||||||
|
import javax.swing.MenuSelectionManager;
|
||||||
|
import javax.swing.SwingUtilities;
|
||||||
|
import javax.swing.plaf.ActionMapUIResource;
|
||||||
import javax.swing.plaf.ComponentUI;
|
import javax.swing.plaf.ComponentUI;
|
||||||
import javax.swing.plaf.basic.BasicMenuBarUI;
|
import javax.swing.plaf.basic.BasicMenuBarUI;
|
||||||
|
import com.formdev.flatlaf.FlatLaf;
|
||||||
|
import com.formdev.flatlaf.util.SystemInfo;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides the Flat LaF UI delegate for {@link javax.swing.JMenuBar}.
|
* Provides the Flat LaF UI delegate for {@link javax.swing.JMenuBar}.
|
||||||
@@ -38,4 +49,45 @@ public class FlatMenuBarUI
|
|||||||
public static ComponentUI createUI( JComponent c ) {
|
public static ComponentUI createUI( JComponent c ) {
|
||||||
return new FlatMenuBarUI();
|
return new FlatMenuBarUI();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* WARNING: This class is not used on macOS if screen menu bar is enabled.
|
||||||
|
* Do not add any functionality here.
|
||||||
|
*/
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void installKeyboardActions() {
|
||||||
|
super.installKeyboardActions();
|
||||||
|
|
||||||
|
ActionMap map = SwingUtilities.getUIActionMap( menuBar );
|
||||||
|
if( map == null ) {
|
||||||
|
map = new ActionMapUIResource();
|
||||||
|
SwingUtilities.replaceUIActionMap( menuBar, map );
|
||||||
|
}
|
||||||
|
map.put( "takeFocus", new TakeFocus() );
|
||||||
|
}
|
||||||
|
|
||||||
|
//---- class TakeFocus ----------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Activates the menu bar and shows mnemonics.
|
||||||
|
* On Windows, the popup of the first menu is not shown.
|
||||||
|
* On other platforms, the popup of the first menu is shown.
|
||||||
|
*/
|
||||||
|
private static class TakeFocus
|
||||||
|
extends AbstractAction
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void actionPerformed( ActionEvent e ) {
|
||||||
|
JMenuBar menuBar = (JMenuBar) e.getSource();
|
||||||
|
JMenu menu = menuBar.getMenu( 0 );
|
||||||
|
if( menu != null ) {
|
||||||
|
MenuSelectionManager.defaultManager().setSelectedPath( SystemInfo.IS_WINDOWS
|
||||||
|
? new MenuElement[] { menuBar, menu }
|
||||||
|
: new MenuElement[] { menuBar, menu, menu.getPopupMenu() } );
|
||||||
|
|
||||||
|
FlatLaf.showMnemonics( menuBar );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,49 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2020 FormDev Software GmbH
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.formdev.flatlaf.ui;
|
||||||
|
|
||||||
|
import static com.formdev.flatlaf.util.UIScale.scale;
|
||||||
|
import java.awt.Component;
|
||||||
|
import java.awt.Insets;
|
||||||
|
import javax.swing.JMenuBar;
|
||||||
|
import javax.swing.UIManager;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Border for {@link javax.swing.JMenu}, {@link javax.swing.JMenuItem},
|
||||||
|
* {@link javax.swing.JCheckBoxMenuItem} and {@link javax.swing.JRadioButtonMenuItem}.
|
||||||
|
*
|
||||||
|
* @uiDefault MenuBar.itemMargins Insets
|
||||||
|
*
|
||||||
|
* @author Karl Tauber
|
||||||
|
*/
|
||||||
|
public class FlatMenuItemBorder
|
||||||
|
extends FlatMarginBorder
|
||||||
|
{
|
||||||
|
private final Insets menuBarItemMargins = UIManager.getInsets( "MenuBar.itemMargins" );
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Insets getBorderInsets( Component c, Insets insets ) {
|
||||||
|
if( c.getParent() instanceof JMenuBar ) {
|
||||||
|
insets.top = scale( menuBarItemMargins.top );
|
||||||
|
insets.left = scale( menuBarItemMargins.left );
|
||||||
|
insets.bottom = scale( menuBarItemMargins.bottom + 1 );
|
||||||
|
insets.right = scale( menuBarItemMargins.right );
|
||||||
|
return insets;
|
||||||
|
} else
|
||||||
|
return super.getBorderInsets( c, insets );
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,516 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2020 FormDev Software GmbH
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.formdev.flatlaf.ui;
|
||||||
|
|
||||||
|
import static com.formdev.flatlaf.util.UIScale.scale;
|
||||||
|
import java.awt.Color;
|
||||||
|
import java.awt.Component;
|
||||||
|
import java.awt.Dimension;
|
||||||
|
import java.awt.Font;
|
||||||
|
import java.awt.FontMetrics;
|
||||||
|
import java.awt.Graphics;
|
||||||
|
import java.awt.Graphics2D;
|
||||||
|
import java.awt.Insets;
|
||||||
|
import java.awt.Paint;
|
||||||
|
import java.awt.Rectangle;
|
||||||
|
import java.awt.event.InputEvent;
|
||||||
|
import java.awt.event.KeyEvent;
|
||||||
|
import java.text.AttributedCharacterIterator;
|
||||||
|
import javax.swing.Icon;
|
||||||
|
import javax.swing.JMenu;
|
||||||
|
import javax.swing.JMenuItem;
|
||||||
|
import javax.swing.KeyStroke;
|
||||||
|
import javax.swing.SwingUtilities;
|
||||||
|
import javax.swing.UIManager;
|
||||||
|
import javax.swing.plaf.basic.BasicHTML;
|
||||||
|
import javax.swing.text.View;
|
||||||
|
import com.formdev.flatlaf.FlatLaf;
|
||||||
|
import com.formdev.flatlaf.util.Graphics2DProxy;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renderer for menu items.
|
||||||
|
*
|
||||||
|
* @uiDefault MenuItem.minimumWidth int
|
||||||
|
* @uiDefault MenuItem.minimumIconSize Dimension
|
||||||
|
* @uiDefault MenuItem.textAcceleratorGap int
|
||||||
|
* @uiDefault MenuItem.textNoAcceleratorGap int
|
||||||
|
* @uiDefault MenuItem.acceleratorArrowGap int
|
||||||
|
* @uiDefault MenuItem.checkBackground Color
|
||||||
|
* @uiDefault MenuItem.underlineSelectionBackground Color
|
||||||
|
* @uiDefault MenuItem.underlineSelectionCheckBackground Color
|
||||||
|
* @uiDefault MenuItem.underlineSelectionColor Color
|
||||||
|
* @uiDefault MenuItem.underlineSelectionHeight Color
|
||||||
|
*
|
||||||
|
* @author Karl Tauber
|
||||||
|
*/
|
||||||
|
public class FlatMenuItemRenderer
|
||||||
|
{
|
||||||
|
protected final JMenuItem menuItem;
|
||||||
|
protected final Icon checkIcon;
|
||||||
|
protected final Icon arrowIcon;
|
||||||
|
protected final Font acceleratorFont;
|
||||||
|
protected final String acceleratorDelimiter;
|
||||||
|
|
||||||
|
protected final int minimumWidth = UIManager.getInt( "MenuItem.minimumWidth" );
|
||||||
|
protected final Dimension minimumIconSize;
|
||||||
|
protected final int textAcceleratorGap = FlatUIUtils.getUIInt( "MenuItem.textAcceleratorGap", 28 );
|
||||||
|
protected final int textNoAcceleratorGap = FlatUIUtils.getUIInt( "MenuItem.textNoAcceleratorGap", 6 );
|
||||||
|
protected final int acceleratorArrowGap = FlatUIUtils.getUIInt( "MenuItem.acceleratorArrowGap", 2 );
|
||||||
|
|
||||||
|
protected final Color checkBackground = UIManager.getColor( "MenuItem.checkBackground" );
|
||||||
|
protected final Insets checkMargins = UIManager.getInsets( "MenuItem.checkMargins" );
|
||||||
|
|
||||||
|
protected final Color underlineSelectionBackground = UIManager.getColor( "MenuItem.underlineSelectionBackground" );
|
||||||
|
protected final Color underlineSelectionCheckBackground = UIManager.getColor( "MenuItem.underlineSelectionCheckBackground" );
|
||||||
|
protected final Color underlineSelectionColor = UIManager.getColor( "MenuItem.underlineSelectionColor" );
|
||||||
|
protected final int underlineSelectionHeight = UIManager.getInt( "MenuItem.underlineSelectionHeight" );
|
||||||
|
|
||||||
|
protected FlatMenuItemRenderer( JMenuItem menuItem, Icon checkIcon, Icon arrowIcon,
|
||||||
|
Font acceleratorFont, String acceleratorDelimiter )
|
||||||
|
{
|
||||||
|
this.menuItem = menuItem;
|
||||||
|
this.checkIcon = checkIcon;
|
||||||
|
this.arrowIcon = arrowIcon;
|
||||||
|
this.acceleratorFont = acceleratorFont;
|
||||||
|
this.acceleratorDelimiter = acceleratorDelimiter;
|
||||||
|
|
||||||
|
Dimension minimumIconSize = UIManager.getDimension( "MenuItem.minimumIconSize" );
|
||||||
|
this.minimumIconSize = (minimumIconSize != null) ? minimumIconSize : new Dimension( 16, 16 );
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Dimension getPreferredMenuItemSize() {
|
||||||
|
int width = 0;
|
||||||
|
int height = 0;
|
||||||
|
boolean isTopLevelMenu = isTopLevelMenu( menuItem );
|
||||||
|
|
||||||
|
Rectangle viewRect = new Rectangle( 0, 0, Integer.MAX_VALUE, Integer.MAX_VALUE );
|
||||||
|
Rectangle iconRect = new Rectangle();
|
||||||
|
Rectangle textRect = new Rectangle();
|
||||||
|
|
||||||
|
// layout icon and text
|
||||||
|
SwingUtilities.layoutCompoundLabel( menuItem,
|
||||||
|
menuItem.getFontMetrics( menuItem.getFont() ), menuItem.getText(), getIconForLayout(),
|
||||||
|
menuItem.getVerticalAlignment(), menuItem.getHorizontalAlignment(),
|
||||||
|
menuItem.getVerticalTextPosition(), menuItem.getHorizontalTextPosition(),
|
||||||
|
viewRect, iconRect, textRect, scale( menuItem.getIconTextGap() ) );
|
||||||
|
|
||||||
|
// union icon and text rectangles
|
||||||
|
Rectangle labelRect = iconRect.union( textRect );
|
||||||
|
width += labelRect.width;
|
||||||
|
height = Math.max( labelRect.height, height );
|
||||||
|
|
||||||
|
// accelerator size
|
||||||
|
String accelText = getAcceleratorText();
|
||||||
|
if( accelText != null ) {
|
||||||
|
// gap between text and accelerator
|
||||||
|
width += scale( !isTopLevelMenu ? textAcceleratorGap : menuItem.getIconTextGap() );
|
||||||
|
|
||||||
|
FontMetrics accelFm = menuItem.getFontMetrics( acceleratorFont );
|
||||||
|
width += SwingUtilities.computeStringWidth( accelFm, accelText );
|
||||||
|
height = Math.max( accelFm.getHeight(), height );
|
||||||
|
}
|
||||||
|
|
||||||
|
// arrow size
|
||||||
|
if( !isTopLevelMenu && arrowIcon != null ) {
|
||||||
|
// gap between text and arrow
|
||||||
|
if( accelText == null )
|
||||||
|
width += scale( textNoAcceleratorGap );
|
||||||
|
|
||||||
|
// gap between accelerator and arrow
|
||||||
|
width += scale( acceleratorArrowGap );
|
||||||
|
|
||||||
|
width += arrowIcon.getIconWidth();
|
||||||
|
height = Math.max( arrowIcon.getIconHeight(), height );
|
||||||
|
}
|
||||||
|
|
||||||
|
// add insets
|
||||||
|
Insets insets = menuItem.getInsets();
|
||||||
|
width += insets.left + insets.right;
|
||||||
|
height += insets.top + insets.bottom;
|
||||||
|
|
||||||
|
// minimum width
|
||||||
|
if( !isTopLevelMenu ) {
|
||||||
|
int minimumWidth = FlatUIUtils.minimumWidth( menuItem, this.minimumWidth );
|
||||||
|
width = Math.max( width, scale( minimumWidth ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Dimension( width, height );
|
||||||
|
}
|
||||||
|
|
||||||
|
private void layout( Rectangle viewRect, Rectangle iconRect, Rectangle textRect,
|
||||||
|
Rectangle accelRect, Rectangle arrowRect, Rectangle labelRect )
|
||||||
|
{
|
||||||
|
boolean isTopLevelMenu = isTopLevelMenu( menuItem );
|
||||||
|
|
||||||
|
// layout arrow
|
||||||
|
if( !isTopLevelMenu && arrowIcon != null ) {
|
||||||
|
arrowRect.width = arrowIcon.getIconWidth();
|
||||||
|
arrowRect.height = arrowIcon.getIconHeight();
|
||||||
|
} else
|
||||||
|
arrowRect.setSize( 0, 0 );
|
||||||
|
arrowRect.y = viewRect.y + centerOffset( viewRect.height, arrowRect.height );
|
||||||
|
|
||||||
|
// layout accelerator
|
||||||
|
String accelText = getAcceleratorText();
|
||||||
|
if( accelText != null ) {
|
||||||
|
FontMetrics accelFm = menuItem.getFontMetrics( acceleratorFont );
|
||||||
|
accelRect.width = SwingUtilities.computeStringWidth( accelFm, accelText );
|
||||||
|
accelRect.height = accelFm.getHeight();
|
||||||
|
|
||||||
|
accelRect.y = viewRect.y + centerOffset( viewRect.height, accelRect.height );
|
||||||
|
} else
|
||||||
|
accelRect.setBounds( 0, 0, 0, 0 );
|
||||||
|
|
||||||
|
// compute horizontal positions of accelerator and arrow
|
||||||
|
int accelArrowGap = !isTopLevelMenu ? scale( acceleratorArrowGap ) : 0;
|
||||||
|
if( menuItem.getComponentOrientation().isLeftToRight() ) {
|
||||||
|
// left-to-right
|
||||||
|
arrowRect.x = viewRect.x + viewRect.width - arrowRect.width;
|
||||||
|
accelRect.x = arrowRect.x - accelArrowGap - accelRect.width;
|
||||||
|
} else {
|
||||||
|
// right-to-left
|
||||||
|
arrowRect.x = viewRect.x;
|
||||||
|
accelRect.x = arrowRect.x + accelArrowGap + arrowRect.width;
|
||||||
|
}
|
||||||
|
|
||||||
|
// width of accelerator, arrow and gap
|
||||||
|
int accelArrowWidth = accelRect.width + arrowRect.width;
|
||||||
|
if( accelText != null )
|
||||||
|
accelArrowWidth += scale( !isTopLevelMenu ? textAcceleratorGap : menuItem.getIconTextGap() );
|
||||||
|
if( !isTopLevelMenu && arrowIcon != null ) {
|
||||||
|
if( accelText == null )
|
||||||
|
accelArrowWidth += scale( textNoAcceleratorGap );
|
||||||
|
accelArrowWidth += scale( acceleratorArrowGap );
|
||||||
|
}
|
||||||
|
|
||||||
|
// label rectangle is view rectangle subtracted by accelerator, arrow and gap
|
||||||
|
labelRect.setBounds( viewRect );
|
||||||
|
labelRect.width -= accelArrowWidth;
|
||||||
|
if( !menuItem.getComponentOrientation().isLeftToRight() )
|
||||||
|
labelRect.x += accelArrowWidth;
|
||||||
|
|
||||||
|
// layout icon and text
|
||||||
|
SwingUtilities.layoutCompoundLabel( menuItem,
|
||||||
|
menuItem.getFontMetrics( menuItem.getFont() ), menuItem.getText(), getIconForLayout(),
|
||||||
|
menuItem.getVerticalAlignment(), menuItem.getHorizontalAlignment(),
|
||||||
|
menuItem.getVerticalTextPosition(), menuItem.getHorizontalTextPosition(),
|
||||||
|
labelRect, iconRect, textRect, scale( menuItem.getIconTextGap() ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int centerOffset( int wh1, int wh2 ) {
|
||||||
|
return (wh1 / 2) - (wh2 / 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void paintMenuItem( Graphics g, Color selectionBackground, Color selectionForeground,
|
||||||
|
Color disabledForeground, Color acceleratorForeground, Color acceleratorSelectionForeground )
|
||||||
|
{
|
||||||
|
Rectangle viewRect = new Rectangle( menuItem.getWidth(), menuItem.getHeight() );
|
||||||
|
|
||||||
|
// subtract insets
|
||||||
|
Insets insets = menuItem.getInsets();
|
||||||
|
viewRect.x += insets.left;
|
||||||
|
viewRect.y += insets.top;
|
||||||
|
viewRect.width -= (insets.left + insets.right);
|
||||||
|
viewRect.height -= (insets.top + insets.bottom);
|
||||||
|
|
||||||
|
Rectangle iconRect = new Rectangle();
|
||||||
|
Rectangle textRect = new Rectangle();
|
||||||
|
Rectangle accelRect = new Rectangle();
|
||||||
|
Rectangle arrowRect = new Rectangle();
|
||||||
|
Rectangle labelRect = new Rectangle();
|
||||||
|
|
||||||
|
layout( viewRect, iconRect, textRect, accelRect, arrowRect, labelRect );
|
||||||
|
|
||||||
|
/*debug
|
||||||
|
g.setColor( Color.green ); g.drawRect( viewRect.x, viewRect.y, viewRect.width - 1, viewRect.height - 1 );
|
||||||
|
g.setColor( Color.red ); g.drawRect( labelRect.x, labelRect.y, labelRect.width - 1, labelRect.height - 1 );
|
||||||
|
g.setColor( Color.blue ); g.drawRect( iconRect.x, iconRect.y, iconRect.width - 1, iconRect.height - 1 );
|
||||||
|
g.setColor( Color.cyan ); g.drawRect( textRect.x, textRect.y, textRect.width - 1, textRect.height - 1 );
|
||||||
|
g.setColor( Color.magenta ); g.drawRect( accelRect.x, accelRect.y, accelRect.width - 1, accelRect.height - 1 );
|
||||||
|
g.setColor( Color.orange ); g.drawRect( arrowRect.x, arrowRect.y, arrowRect.width - 1, arrowRect.height - 1 );
|
||||||
|
debug*/
|
||||||
|
|
||||||
|
paintBackground( g, selectionBackground );
|
||||||
|
paintIcon( g, iconRect, getIconForPainting() );
|
||||||
|
paintText( g, textRect, menuItem.getText(), selectionForeground, disabledForeground );
|
||||||
|
paintAccelerator( g, accelRect, getAcceleratorText(), acceleratorForeground, acceleratorSelectionForeground, disabledForeground );
|
||||||
|
if( !isTopLevelMenu( menuItem ) )
|
||||||
|
paintArrowIcon( g, arrowRect, arrowIcon );
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void paintBackground( Graphics g, Color selectionBackground ) {
|
||||||
|
boolean armedOrSelected = isArmedOrSelected( menuItem );
|
||||||
|
if( menuItem.isOpaque() || armedOrSelected ) {
|
||||||
|
int width = menuItem.getWidth();
|
||||||
|
int height = menuItem.getHeight();
|
||||||
|
|
||||||
|
// paint background
|
||||||
|
g.setColor( armedOrSelected
|
||||||
|
? (isUnderlineSelection() ? underlineSelectionBackground : selectionBackground)
|
||||||
|
: menuItem.getBackground() );
|
||||||
|
g.fillRect( 0, 0, width, height );
|
||||||
|
|
||||||
|
// paint underline
|
||||||
|
if( armedOrSelected && isUnderlineSelection() ) {
|
||||||
|
int underlineHeight = scale( underlineSelectionHeight );
|
||||||
|
g.setColor( underlineSelectionColor );
|
||||||
|
if( isTopLevelMenu( menuItem ) ) {
|
||||||
|
// paint underline at bottom
|
||||||
|
g.fillRect( 0, height - underlineHeight, width, underlineHeight );
|
||||||
|
} else if( menuItem.getComponentOrientation().isLeftToRight() ) {
|
||||||
|
// paint underline at left side
|
||||||
|
g.fillRect( 0, 0, underlineHeight, height );
|
||||||
|
} else {
|
||||||
|
// paint underline at right side
|
||||||
|
g.fillRect( width - underlineHeight, 0, underlineHeight, height );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void paintIcon( Graphics g, Rectangle iconRect, Icon icon ) {
|
||||||
|
// if checkbox/radiobutton menu item is selected and also has a custom icon,
|
||||||
|
// then use filled icon background to indicate selection (instead of using checkIcon)
|
||||||
|
if( menuItem.isSelected() && checkIcon != null && icon != checkIcon ) {
|
||||||
|
Rectangle r = FlatUIUtils.addInsets( iconRect, scale( checkMargins ) );
|
||||||
|
g.setColor( isUnderlineSelection() ? underlineSelectionCheckBackground : checkBackground );
|
||||||
|
g.fillRect( r.x, r.y, r.width, r.height );
|
||||||
|
}
|
||||||
|
|
||||||
|
paintIcon( g, menuItem, icon, iconRect );
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void paintText( Graphics g, Rectangle textRect, String text, Color selectionForeground, Color disabledForeground ) {
|
||||||
|
View htmlView = (View) menuItem.getClientProperty( BasicHTML.propertyKey );
|
||||||
|
if( htmlView != null ) {
|
||||||
|
paintHTMLText( g, menuItem, textRect, htmlView, isUnderlineSelection() ? null : selectionForeground );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int mnemonicIndex = FlatLaf.isShowMnemonics() ? menuItem.getDisplayedMnemonicIndex() : -1;
|
||||||
|
Color foreground = menuItem.getForeground();
|
||||||
|
|
||||||
|
paintText( g, menuItem, textRect, text, mnemonicIndex, menuItem.getFont(),
|
||||||
|
foreground, isUnderlineSelection() ? foreground : selectionForeground, disabledForeground );
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void paintAccelerator( Graphics g, Rectangle accelRect, String accelText,
|
||||||
|
Color foreground, Color selectionForeground, Color disabledForeground )
|
||||||
|
{
|
||||||
|
paintText( g, menuItem, accelRect, accelText, -1, acceleratorFont,
|
||||||
|
foreground, isUnderlineSelection() ? foreground : selectionForeground, disabledForeground );
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void paintArrowIcon( Graphics g, Rectangle arrowRect, Icon arrowIcon ) {
|
||||||
|
paintIcon( g, menuItem, arrowIcon, arrowRect );
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static void paintIcon( Graphics g, JMenuItem menuItem, Icon icon, Rectangle iconRect ) {
|
||||||
|
if( icon == null )
|
||||||
|
return;
|
||||||
|
|
||||||
|
// center because the real icon may be smaller than dimension in iconRect
|
||||||
|
int x = iconRect.x + centerOffset( iconRect.width, icon.getIconWidth() );
|
||||||
|
int y = iconRect.y + centerOffset( iconRect.height, icon.getIconHeight() );
|
||||||
|
|
||||||
|
// paint
|
||||||
|
icon.paintIcon( menuItem, g, x, y );
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static void paintText( Graphics g, JMenuItem menuItem,
|
||||||
|
Rectangle textRect, String text, int mnemonicIndex, Font font,
|
||||||
|
Color foreground, Color selectionForeground, Color disabledForeground )
|
||||||
|
{
|
||||||
|
if( text == null || text.isEmpty() )
|
||||||
|
return;
|
||||||
|
|
||||||
|
FontMetrics fm = menuItem.getFontMetrics( font );
|
||||||
|
|
||||||
|
Font oldFont = g.getFont();
|
||||||
|
g.setFont( font );
|
||||||
|
g.setColor( !menuItem.isEnabled()
|
||||||
|
? disabledForeground
|
||||||
|
: (isArmedOrSelected( menuItem )
|
||||||
|
? selectionForeground
|
||||||
|
: foreground) );
|
||||||
|
|
||||||
|
FlatUIUtils.drawStringUnderlineCharAt( menuItem, g, text, mnemonicIndex,
|
||||||
|
textRect.x, textRect.y + fm.getAscent() );
|
||||||
|
|
||||||
|
g.setFont( oldFont );
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static void paintHTMLText( Graphics g, JMenuItem menuItem,
|
||||||
|
Rectangle textRect, View htmlView, Color selectionForeground )
|
||||||
|
{
|
||||||
|
if( isArmedOrSelected( menuItem ) && selectionForeground != null )
|
||||||
|
g = new GraphicsProxyWithTextColor( (Graphics2D) g, selectionForeground );
|
||||||
|
|
||||||
|
htmlView.paint( g, textRect );
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static boolean isArmedOrSelected( JMenuItem menuItem ) {
|
||||||
|
return menuItem.isArmed() || (menuItem instanceof JMenu && menuItem.isSelected());
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static boolean isTopLevelMenu( JMenuItem menuItem ) {
|
||||||
|
return menuItem instanceof JMenu && ((JMenu)menuItem).isTopLevelMenu();
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isUnderlineSelection() {
|
||||||
|
return "underline".equals( UIManager.getString( "MenuItem.selectionType" ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
private Icon getIconForPainting() {
|
||||||
|
Icon icon = menuItem.getIcon();
|
||||||
|
|
||||||
|
if( icon == null && checkIcon != null && !isTopLevelMenu( menuItem ) )
|
||||||
|
return checkIcon;
|
||||||
|
|
||||||
|
if( icon == null )
|
||||||
|
return null;
|
||||||
|
|
||||||
|
if( !menuItem.isEnabled() )
|
||||||
|
return menuItem.getDisabledIcon();
|
||||||
|
|
||||||
|
if( menuItem.getModel().isPressed() && menuItem.isArmed() ) {
|
||||||
|
Icon pressedIcon = menuItem.getPressedIcon();
|
||||||
|
if( pressedIcon != null )
|
||||||
|
return pressedIcon;
|
||||||
|
}
|
||||||
|
|
||||||
|
return icon;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Icon getIconForLayout() {
|
||||||
|
Icon icon = menuItem.getIcon();
|
||||||
|
|
||||||
|
if( isTopLevelMenu( menuItem ) )
|
||||||
|
return (icon != null) ? new MinSizeIcon( icon ) : null;
|
||||||
|
|
||||||
|
return new MinSizeIcon( (icon != null) ? icon : checkIcon );
|
||||||
|
}
|
||||||
|
|
||||||
|
private KeyStroke cachedAccelerator;
|
||||||
|
private String cachedAcceleratorText;
|
||||||
|
|
||||||
|
private String getAcceleratorText() {
|
||||||
|
KeyStroke accelerator = menuItem.getAccelerator();
|
||||||
|
if( accelerator == null )
|
||||||
|
return null;
|
||||||
|
|
||||||
|
if( accelerator == cachedAccelerator )
|
||||||
|
return cachedAcceleratorText;
|
||||||
|
|
||||||
|
StringBuilder buf = new StringBuilder();
|
||||||
|
int modifiers = accelerator.getModifiers();
|
||||||
|
if( modifiers != 0 )
|
||||||
|
buf.append( InputEvent.getModifiersExText( modifiers ) ).append( acceleratorDelimiter );
|
||||||
|
|
||||||
|
int keyCode = accelerator.getKeyCode();
|
||||||
|
if( keyCode != 0 )
|
||||||
|
buf.append( KeyEvent.getKeyText( keyCode ) );
|
||||||
|
else
|
||||||
|
buf.append( accelerator.getKeyChar() );
|
||||||
|
|
||||||
|
cachedAccelerator = accelerator;
|
||||||
|
cachedAcceleratorText = buf.toString();
|
||||||
|
|
||||||
|
return cachedAcceleratorText;
|
||||||
|
}
|
||||||
|
|
||||||
|
//---- class MinSizeIcon --------------------------------------------------
|
||||||
|
|
||||||
|
private class MinSizeIcon
|
||||||
|
implements Icon
|
||||||
|
{
|
||||||
|
private final Icon delegate;
|
||||||
|
|
||||||
|
MinSizeIcon( Icon delegate ) {
|
||||||
|
this.delegate = delegate;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getIconWidth() {
|
||||||
|
int iconWidth = (delegate != null) ? delegate.getIconWidth() : 0;
|
||||||
|
return Math.max( iconWidth, scale( minimumIconSize.width ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getIconHeight() {
|
||||||
|
int iconHeight = (delegate != null) ? delegate.getIconHeight() : 0;
|
||||||
|
return Math.max( iconHeight, scale( minimumIconSize.height ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void paintIcon( Component c, Graphics g, int x, int y ) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//---- class GraphicsProxyWithTextColor -----------------------------------
|
||||||
|
|
||||||
|
private static class GraphicsProxyWithTextColor
|
||||||
|
extends Graphics2DProxy
|
||||||
|
{
|
||||||
|
private final Color textColor;
|
||||||
|
|
||||||
|
GraphicsProxyWithTextColor( Graphics2D delegate, Color textColor ) {
|
||||||
|
super( delegate );
|
||||||
|
this.textColor = textColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void drawString( String str, int x, int y ) {
|
||||||
|
Paint oldPaint = getPaint();
|
||||||
|
setPaint( textColor );
|
||||||
|
super.drawString( str, x, y );
|
||||||
|
setPaint( oldPaint );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void drawString( String str, float x, float y ) {
|
||||||
|
Paint oldPaint = getPaint();
|
||||||
|
setPaint( textColor );
|
||||||
|
super.drawString( str, x, y );
|
||||||
|
setPaint( oldPaint );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void drawString( AttributedCharacterIterator iterator, int x, int y ) {
|
||||||
|
Paint oldPaint = getPaint();
|
||||||
|
setPaint( textColor );
|
||||||
|
super.drawString( iterator, x, y );
|
||||||
|
setPaint( oldPaint );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void drawString( AttributedCharacterIterator iterator, float x, float y ) {
|
||||||
|
Paint oldPaint = getPaint();
|
||||||
|
setPaint( textColor );
|
||||||
|
super.drawString( iterator, x, y );
|
||||||
|
setPaint( oldPaint );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void drawChars( char[] data, int offset, int length, int x, int y ) {
|
||||||
|
Paint oldPaint = getPaint();
|
||||||
|
setPaint( textColor );
|
||||||
|
super.drawChars( data, offset, length, x, y );
|
||||||
|
setPaint( oldPaint );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -16,9 +16,11 @@
|
|||||||
|
|
||||||
package com.formdev.flatlaf.ui;
|
package com.formdev.flatlaf.ui;
|
||||||
|
|
||||||
import static com.formdev.flatlaf.util.UIScale.scale;
|
import java.awt.Dimension;
|
||||||
import java.beans.PropertyChangeListener;
|
import java.awt.Graphics;
|
||||||
|
import javax.swing.Icon;
|
||||||
import javax.swing.JComponent;
|
import javax.swing.JComponent;
|
||||||
|
import javax.swing.LookAndFeel;
|
||||||
import javax.swing.plaf.ComponentUI;
|
import javax.swing.plaf.ComponentUI;
|
||||||
import javax.swing.plaf.basic.BasicMenuItemUI;
|
import javax.swing.plaf.basic.BasicMenuItemUI;
|
||||||
|
|
||||||
@@ -27,29 +29,34 @@ import javax.swing.plaf.basic.BasicMenuItemUI;
|
|||||||
*
|
*
|
||||||
* <!-- BasicMenuItemUI -->
|
* <!-- BasicMenuItemUI -->
|
||||||
*
|
*
|
||||||
* @uiDefault MenuItem.font Font
|
* @uiDefault MenuItem.font Font
|
||||||
* @uiDefault MenuItem.background Color
|
* @uiDefault MenuItem.background Color
|
||||||
* @uiDefault MenuItem.foreground Color
|
* @uiDefault MenuItem.foreground Color
|
||||||
* @uiDefault MenuItem.disabledForeground Color
|
* @uiDefault MenuItem.disabledForeground Color
|
||||||
* @uiDefault MenuItem.selectionBackground Color
|
* @uiDefault MenuItem.selectionBackground Color
|
||||||
* @uiDefault MenuItem.selectionForeground Color
|
* @uiDefault MenuItem.selectionForeground Color
|
||||||
* @uiDefault MenuItem.acceleratorForeground Color
|
* @uiDefault MenuItem.acceleratorForeground Color
|
||||||
* @uiDefault MenuItem.acceleratorSelectionForeground Color
|
* @uiDefault MenuItem.acceleratorSelectionForeground Color
|
||||||
* @uiDefault MenuItem.acceleratorFont Font defaults to MenuItem.font
|
* @uiDefault MenuItem.acceleratorFont Font defaults to MenuItem.font
|
||||||
* @uiDefault MenuItem.acceleratorDelimiter String
|
* @uiDefault MenuItem.acceleratorDelimiter String
|
||||||
* @uiDefault MenuItem.border Border
|
* @uiDefault MenuItem.border Border
|
||||||
* @uiDefault MenuItem.borderPainted boolean
|
* @uiDefault MenuItem.borderPainted boolean
|
||||||
* @uiDefault MenuItem.margin Insets
|
* @uiDefault MenuItem.margin Insets
|
||||||
* @uiDefault MenuItem.arrowIcon Icon
|
* @uiDefault MenuItem.arrowIcon Icon
|
||||||
* @uiDefault MenuItem.checkIcon Icon
|
* @uiDefault MenuItem.checkIcon Icon
|
||||||
* @uiDefault MenuItem.opaque boolean
|
* @uiDefault MenuItem.opaque boolean
|
||||||
* @uiDefault MenuItem.evenHeight boolean
|
*
|
||||||
|
* <!-- FlatMenuItemUI -->
|
||||||
|
*
|
||||||
|
* @uiDefault MenuItem.iconTextGap int
|
||||||
*
|
*
|
||||||
* @author Karl Tauber
|
* @author Karl Tauber
|
||||||
*/
|
*/
|
||||||
public class FlatMenuItemUI
|
public class FlatMenuItemUI
|
||||||
extends BasicMenuItemUI
|
extends BasicMenuItemUI
|
||||||
{
|
{
|
||||||
|
private FlatMenuItemRenderer renderer;
|
||||||
|
|
||||||
public static ComponentUI createUI( JComponent c ) {
|
public static ComponentUI createUI( JComponent c ) {
|
||||||
return new FlatMenuItemUI();
|
return new FlatMenuItemUI();
|
||||||
}
|
}
|
||||||
@@ -58,20 +65,30 @@ public class FlatMenuItemUI
|
|||||||
protected void installDefaults() {
|
protected void installDefaults() {
|
||||||
super.installDefaults();
|
super.installDefaults();
|
||||||
|
|
||||||
// scale
|
LookAndFeel.installProperty( menuItem, "iconTextGap", FlatUIUtils.getUIInt( "MenuItem.iconTextGap", 4 ) );
|
||||||
defaultTextIconGap = scale( defaultTextIconGap );
|
|
||||||
|
renderer = createRenderer();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Scale defaultTextIconGap again if iconTextGap property has changed.
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
protected PropertyChangeListener createPropertyChangeListener( JComponent c ) {
|
protected void uninstallDefaults() {
|
||||||
PropertyChangeListener superListener = super.createPropertyChangeListener( c );
|
super.uninstallDefaults();
|
||||||
return e -> {
|
|
||||||
superListener.propertyChange( e );
|
renderer = null;
|
||||||
if( e.getPropertyName() == "iconTextGap" )
|
}
|
||||||
defaultTextIconGap = scale( defaultTextIconGap );
|
|
||||||
};
|
protected FlatMenuItemRenderer createRenderer() {
|
||||||
|
return new FlatMenuItemRenderer( menuItem, checkIcon, arrowIcon, acceleratorFont, acceleratorDelimiter );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Dimension getPreferredMenuItemSize( JComponent c, Icon checkIcon, Icon arrowIcon, int defaultTextIconGap ) {
|
||||||
|
return renderer.getPreferredMenuItemSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void paint( Graphics g, JComponent c ) {
|
||||||
|
renderer.paintMenuItem( g, selectionBackground, selectionForeground, disabledForeground,
|
||||||
|
acceleratorForeground, acceleratorSelectionForeground );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,9 +16,19 @@
|
|||||||
|
|
||||||
package com.formdev.flatlaf.ui;
|
package com.formdev.flatlaf.ui;
|
||||||
|
|
||||||
import static com.formdev.flatlaf.util.UIScale.scale;
|
import java.awt.Color;
|
||||||
import java.beans.PropertyChangeListener;
|
import java.awt.Dimension;
|
||||||
|
import java.awt.Font;
|
||||||
|
import java.awt.Graphics;
|
||||||
|
import java.awt.event.MouseEvent;
|
||||||
|
import javax.swing.ButtonModel;
|
||||||
|
import javax.swing.Icon;
|
||||||
import javax.swing.JComponent;
|
import javax.swing.JComponent;
|
||||||
|
import javax.swing.JMenu;
|
||||||
|
import javax.swing.JMenuItem;
|
||||||
|
import javax.swing.LookAndFeel;
|
||||||
|
import javax.swing.UIManager;
|
||||||
|
import javax.swing.event.MouseInputListener;
|
||||||
import javax.swing.plaf.ComponentUI;
|
import javax.swing.plaf.ComponentUI;
|
||||||
import javax.swing.plaf.basic.BasicMenuUI;
|
import javax.swing.plaf.basic.BasicMenuUI;
|
||||||
|
|
||||||
@@ -27,32 +37,39 @@ import javax.swing.plaf.basic.BasicMenuUI;
|
|||||||
*
|
*
|
||||||
* <!-- BasicMenuUI -->
|
* <!-- BasicMenuUI -->
|
||||||
*
|
*
|
||||||
* @uiDefault Menu.font Font
|
* @uiDefault Menu.font Font
|
||||||
* @uiDefault Menu.background Color
|
* @uiDefault Menu.background Color
|
||||||
* @uiDefault Menu.foreground Color
|
* @uiDefault Menu.foreground Color
|
||||||
* @uiDefault Menu.disabledForeground Color
|
* @uiDefault Menu.disabledForeground Color
|
||||||
* @uiDefault Menu.selectionBackground Color
|
* @uiDefault Menu.selectionBackground Color
|
||||||
* @uiDefault Menu.selectionForeground Color
|
* @uiDefault Menu.selectionForeground Color
|
||||||
* @uiDefault Menu.acceleratorForeground Color
|
* @uiDefault Menu.acceleratorForeground Color
|
||||||
* @uiDefault Menu.acceleratorSelectionForeground Color
|
* @uiDefault Menu.acceleratorSelectionForeground Color
|
||||||
* @uiDefault MenuItem.acceleratorFont Font defaults to MenuItem.font
|
* @uiDefault MenuItem.acceleratorFont Font defaults to MenuItem.font
|
||||||
* @uiDefault MenuItem.acceleratorDelimiter String
|
* @uiDefault MenuItem.acceleratorDelimiter String
|
||||||
* @uiDefault Menu.border Border
|
* @uiDefault Menu.border Border
|
||||||
* @uiDefault Menu.borderPainted boolean
|
* @uiDefault Menu.borderPainted boolean
|
||||||
* @uiDefault Menu.margin Insets
|
* @uiDefault Menu.margin Insets
|
||||||
* @uiDefault Menu.arrowIcon Icon
|
* @uiDefault Menu.arrowIcon Icon
|
||||||
* @uiDefault Menu.checkIcon Icon
|
* @uiDefault Menu.checkIcon Icon
|
||||||
* @uiDefault Menu.opaque boolean
|
* @uiDefault Menu.opaque boolean
|
||||||
* @uiDefault Menu.evenHeight boolean
|
* @uiDefault Menu.crossMenuMnemonic boolean default is false
|
||||||
* @uiDefault Menu.crossMenuMnemonic boolean default is false
|
* @uiDefault Menu.useMenuBarBackgroundForTopLevel boolean default is false
|
||||||
* @uiDefault Menu.useMenuBarBackgroundForTopLevel boolean default is false
|
* @uiDefault MenuBar.background Color used if Menu.useMenuBarBackgroundForTopLevel is true
|
||||||
* @uiDefault MenuBar.background Color used if Menu.useMenuBarBackgroundForTopLevel is true
|
*
|
||||||
|
* <!-- FlatMenuUI -->
|
||||||
|
*
|
||||||
|
* @uiDefault MenuItem.iconTextGap int
|
||||||
|
* @uiDefault MenuBar.hoverBackground Color
|
||||||
*
|
*
|
||||||
* @author Karl Tauber
|
* @author Karl Tauber
|
||||||
*/
|
*/
|
||||||
public class FlatMenuUI
|
public class FlatMenuUI
|
||||||
extends BasicMenuUI
|
extends BasicMenuUI
|
||||||
{
|
{
|
||||||
|
private Color hoverBackground;
|
||||||
|
private FlatMenuItemRenderer renderer;
|
||||||
|
|
||||||
public static ComponentUI createUI( JComponent c ) {
|
public static ComponentUI createUI( JComponent c ) {
|
||||||
return new FlatMenuUI();
|
return new FlatMenuUI();
|
||||||
}
|
}
|
||||||
@@ -61,20 +78,83 @@ public class FlatMenuUI
|
|||||||
protected void installDefaults() {
|
protected void installDefaults() {
|
||||||
super.installDefaults();
|
super.installDefaults();
|
||||||
|
|
||||||
// scale
|
LookAndFeel.installProperty( menuItem, "iconTextGap", FlatUIUtils.getUIInt( "MenuItem.iconTextGap", 4 ) );
|
||||||
defaultTextIconGap = scale( defaultTextIconGap );
|
|
||||||
|
menuItem.setRolloverEnabled( true );
|
||||||
|
|
||||||
|
hoverBackground = UIManager.getColor( "MenuBar.hoverBackground" );
|
||||||
|
renderer = createRenderer();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Scale defaultTextIconGap again if iconTextGap property has changed.
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
protected PropertyChangeListener createPropertyChangeListener( JComponent c ) {
|
protected void uninstallDefaults() {
|
||||||
PropertyChangeListener superListener = super.createPropertyChangeListener( c );
|
super.uninstallDefaults();
|
||||||
return e -> {
|
|
||||||
superListener.propertyChange( e );
|
hoverBackground = null;
|
||||||
if( e.getPropertyName() == "iconTextGap" )
|
renderer = null;
|
||||||
defaultTextIconGap = scale( defaultTextIconGap );
|
}
|
||||||
|
|
||||||
|
protected FlatMenuItemRenderer createRenderer() {
|
||||||
|
return new FlatMenuRenderer( menuItem, checkIcon, arrowIcon, acceleratorFont, acceleratorDelimiter );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected MouseInputListener createMouseInputListener( JComponent c ) {
|
||||||
|
return new BasicMenuUI.MouseInputHandler() {
|
||||||
|
@Override
|
||||||
|
public void mouseEntered( MouseEvent e ) {
|
||||||
|
super.mouseEntered( e );
|
||||||
|
rollover( e, true );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void mouseExited( MouseEvent e ) {
|
||||||
|
super.mouseExited( e );
|
||||||
|
rollover( e, false );
|
||||||
|
}
|
||||||
|
|
||||||
|
private void rollover( MouseEvent e, boolean rollover ) {
|
||||||
|
JMenu menu = (JMenu) e.getSource();
|
||||||
|
if( menu.isTopLevelMenu() && menu.isRolloverEnabled() ) {
|
||||||
|
menu.getModel().setRollover( rollover );
|
||||||
|
menu.repaint();
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Dimension getPreferredMenuItemSize( JComponent c, Icon checkIcon, Icon arrowIcon, int defaultTextIconGap ) {
|
||||||
|
return renderer.getPreferredMenuItemSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void paint( Graphics g, JComponent c ) {
|
||||||
|
renderer.paintMenuItem( g, selectionBackground, selectionForeground, disabledForeground,
|
||||||
|
acceleratorForeground, acceleratorSelectionForeground );
|
||||||
|
}
|
||||||
|
|
||||||
|
//---- class FlatMenuRenderer ---------------------------------------------
|
||||||
|
|
||||||
|
protected class FlatMenuRenderer
|
||||||
|
extends FlatMenuItemRenderer
|
||||||
|
{
|
||||||
|
protected FlatMenuRenderer( JMenuItem menuItem, Icon checkIcon, Icon arrowIcon,
|
||||||
|
Font acceleratorFont, String acceleratorDelimiter )
|
||||||
|
{
|
||||||
|
super( menuItem, checkIcon, arrowIcon, acceleratorFont, acceleratorDelimiter );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void paintBackground( Graphics g, Color selectionBackground ) {
|
||||||
|
ButtonModel model = menuItem.getModel();
|
||||||
|
if( model.isRollover() && !model.isArmed() && !model.isSelected() &&
|
||||||
|
model.isEnabled() && ((JMenu)menuItem).isTopLevelMenu() )
|
||||||
|
{
|
||||||
|
g.setColor( FlatUIUtils.deriveColor( hoverBackground, menuItem.getBackground() ) );
|
||||||
|
g.fillRect( 0, 0, menuItem.getWidth(), menuItem.getHeight() );
|
||||||
|
} else
|
||||||
|
super.paintBackground( g, selectionBackground );
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,20 +35,19 @@ import com.formdev.flatlaf.util.UIScale;
|
|||||||
/**
|
/**
|
||||||
* Provides the Flat LaF UI delegate for {@link javax.swing.JOptionPane}.
|
* Provides the Flat LaF UI delegate for {@link javax.swing.JOptionPane}.
|
||||||
*
|
*
|
||||||
|
* <!-- BasicOptionPaneUI -->
|
||||||
|
*
|
||||||
* @uiDefault OptionPane.font Font unused
|
* @uiDefault OptionPane.font Font unused
|
||||||
* @uiDefault OptionPane.background Color
|
* @uiDefault OptionPane.background Color
|
||||||
* @uiDefault OptionPane.foreground Color unused
|
* @uiDefault OptionPane.foreground Color unused
|
||||||
* @uiDefault OptionPane.border Border
|
* @uiDefault OptionPane.border Border
|
||||||
* @uiDefault OptionPane.messageAreaBorder Border
|
* @uiDefault OptionPane.messageAreaBorder Border
|
||||||
* @uiDefault OptionPane.buttonAreaBorder Border
|
* @uiDefault OptionPane.buttonAreaBorder Border
|
||||||
* @uiDefault OptionPane.messageForeground Color
|
* @uiDefault OptionPane.messageForeground Color optional; defaults to Label.foreground
|
||||||
* @uiDefault OptionPane.messageFont Font
|
* @uiDefault OptionPane.messageFont Font optional; defaults to Label.font
|
||||||
* @uiDefault OptionPane.buttonFont Font
|
* @uiDefault OptionPane.buttonFont Font optional; defaults to Button.font
|
||||||
*
|
*
|
||||||
* @uiDefault OptionPane.minimumSize Dimension
|
* @uiDefault OptionPane.minimumSize Dimension
|
||||||
* @uiDefault OptionPane.maxCharactersPerLine int
|
|
||||||
* @uiDefault OptionPane.iconMessageGap int
|
|
||||||
* @uiDefault OptionPane.messagePadding int
|
|
||||||
* @uiDefault OptionPane.buttonPadding int
|
* @uiDefault OptionPane.buttonPadding int
|
||||||
* @uiDefault OptionPane.buttonMinimumWidth int -1=disabled
|
* @uiDefault OptionPane.buttonMinimumWidth int -1=disabled
|
||||||
* @uiDefault OptionPane.sameSizeButtons boolean if true, gives all buttons same size
|
* @uiDefault OptionPane.sameSizeButtons boolean if true, gives all buttons same size
|
||||||
@@ -61,6 +60,25 @@ import com.formdev.flatlaf.util.UIScale;
|
|||||||
* @uiDefault OptionPane.questionIcon Icon
|
* @uiDefault OptionPane.questionIcon Icon
|
||||||
* @uiDefault OptionPane.warningIcon Icon
|
* @uiDefault OptionPane.warningIcon Icon
|
||||||
*
|
*
|
||||||
|
* @uiDefault OptionPane.okButtonText String
|
||||||
|
* @uiDefault OptionPane.okButtonMnemonic String
|
||||||
|
* @uiDefault OptionPane.okIcon Icon
|
||||||
|
* @uiDefault OptionPane.cancelButtonText String
|
||||||
|
* @uiDefault OptionPane.cancelButtonMnemonic String
|
||||||
|
* @uiDefault OptionPane.cancelIcon Icon
|
||||||
|
* @uiDefault OptionPane.yesButtonText String
|
||||||
|
* @uiDefault OptionPane.yesButtonMnemonic String
|
||||||
|
* @uiDefault OptionPane.yesIcon Icon
|
||||||
|
* @uiDefault OptionPane.noButtonText String
|
||||||
|
* @uiDefault OptionPane.noButtonMnemonic String
|
||||||
|
* @uiDefault OptionPane.noIcon Icon
|
||||||
|
*
|
||||||
|
* <!-- FlatOptionPaneUI -->
|
||||||
|
*
|
||||||
|
* @uiDefault OptionPane.iconMessageGap int
|
||||||
|
* @uiDefault OptionPane.messagePadding int
|
||||||
|
* @uiDefault OptionPane.maxCharactersPerLine int
|
||||||
|
*
|
||||||
* @author Karl Tauber
|
* @author Karl Tauber
|
||||||
*/
|
*/
|
||||||
public class FlatOptionPaneUI
|
public class FlatOptionPaneUI
|
||||||
|
|||||||
@@ -0,0 +1,45 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2020 FormDev Software GmbH
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.formdev.flatlaf.ui;
|
||||||
|
|
||||||
|
import javax.swing.JComponent;
|
||||||
|
import javax.swing.plaf.ComponentUI;
|
||||||
|
import javax.swing.plaf.basic.BasicPanelUI;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides the Flat LaF UI delegate for {@link javax.swing.JPanel}.
|
||||||
|
*
|
||||||
|
* <!-- BasicPanelUI -->
|
||||||
|
*
|
||||||
|
* @uiDefault Panel.font Font unused
|
||||||
|
* @uiDefault Panel.background Color only used if opaque
|
||||||
|
* @uiDefault Panel.foreground Color
|
||||||
|
* @uiDefault Panel.border Border
|
||||||
|
*
|
||||||
|
* @author Karl Tauber
|
||||||
|
*/
|
||||||
|
public class FlatPanelUI
|
||||||
|
extends BasicPanelUI
|
||||||
|
{
|
||||||
|
private static ComponentUI instance;
|
||||||
|
|
||||||
|
public static ComponentUI createUI( JComponent c ) {
|
||||||
|
if( instance == null )
|
||||||
|
instance = new FlatPanelUI();
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -16,19 +16,23 @@
|
|||||||
|
|
||||||
package com.formdev.flatlaf.ui;
|
package com.formdev.flatlaf.ui;
|
||||||
|
|
||||||
import static com.formdev.flatlaf.util.UIScale.scale;
|
|
||||||
import java.awt.Color;
|
import java.awt.Color;
|
||||||
import java.awt.Dimension;
|
import java.awt.Dimension;
|
||||||
import java.awt.Graphics;
|
import java.awt.Graphics;
|
||||||
|
import java.awt.Toolkit;
|
||||||
import java.awt.event.FocusListener;
|
import java.awt.event.FocusListener;
|
||||||
|
import java.awt.event.KeyAdapter;
|
||||||
|
import java.awt.event.KeyEvent;
|
||||||
|
import java.awt.event.KeyListener;
|
||||||
import java.beans.PropertyChangeEvent;
|
import java.beans.PropertyChangeEvent;
|
||||||
|
import javax.swing.Icon;
|
||||||
import javax.swing.JComponent;
|
import javax.swing.JComponent;
|
||||||
import javax.swing.LookAndFeel;
|
import javax.swing.LookAndFeel;
|
||||||
import javax.swing.UIManager;
|
import javax.swing.UIManager;
|
||||||
import javax.swing.plaf.ComponentUI;
|
import javax.swing.plaf.ComponentUI;
|
||||||
import javax.swing.plaf.basic.BasicPasswordFieldUI;
|
import javax.swing.plaf.basic.BasicPasswordFieldUI;
|
||||||
import com.formdev.flatlaf.FlatClientProperties;
|
import javax.swing.text.Caret;
|
||||||
import com.formdev.flatlaf.util.SystemInfo;
|
import javax.swing.text.JTextComponent;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides the Flat LaF UI delegate for {@link javax.swing.JPasswordField}.
|
* Provides the Flat LaF UI delegate for {@link javax.swing.JPasswordField}.
|
||||||
@@ -51,22 +55,24 @@ import com.formdev.flatlaf.util.SystemInfo;
|
|||||||
*
|
*
|
||||||
* <!-- FlatPasswordFieldUI -->
|
* <!-- FlatPasswordFieldUI -->
|
||||||
*
|
*
|
||||||
* @uiDefault Component.focusWidth int
|
|
||||||
* @uiDefault Component.minimumWidth int
|
* @uiDefault Component.minimumWidth int
|
||||||
* @uiDefault Component.isIntelliJTheme boolean
|
* @uiDefault Component.isIntelliJTheme boolean
|
||||||
* @uiDefault PasswordField.placeholderForeground Color
|
* @uiDefault PasswordField.placeholderForeground Color
|
||||||
|
* @uiDefault PasswordField.capsLockIcon Icon
|
||||||
|
* @uiDefault TextComponent.selectAllOnFocusPolicy String never, once (default) or always
|
||||||
*
|
*
|
||||||
* @author Karl Tauber
|
* @author Karl Tauber
|
||||||
*/
|
*/
|
||||||
public class FlatPasswordFieldUI
|
public class FlatPasswordFieldUI
|
||||||
extends BasicPasswordFieldUI
|
extends BasicPasswordFieldUI
|
||||||
{
|
{
|
||||||
protected int focusWidth;
|
|
||||||
protected int minimumWidth;
|
protected int minimumWidth;
|
||||||
protected boolean isIntelliJTheme;
|
protected boolean isIntelliJTheme;
|
||||||
protected Color placeholderForeground;
|
protected Color placeholderForeground;
|
||||||
|
protected Icon capsLockIcon;
|
||||||
|
|
||||||
private FocusListener focusListener;
|
private FocusListener focusListener;
|
||||||
|
private KeyListener capsLockListener;
|
||||||
|
|
||||||
public static ComponentUI createUI( JComponent c ) {
|
public static ComponentUI createUI( JComponent c ) {
|
||||||
return new FlatPasswordFieldUI();
|
return new FlatPasswordFieldUI();
|
||||||
@@ -76,19 +82,15 @@ public class FlatPasswordFieldUI
|
|||||||
protected void installDefaults() {
|
protected void installDefaults() {
|
||||||
super.installDefaults();
|
super.installDefaults();
|
||||||
|
|
||||||
// use other echoChar on Mac because the default is too large in SF font
|
|
||||||
if( SystemInfo.IS_MAC )
|
|
||||||
LookAndFeel.installProperty( getComponent(), "echoChar", '\u2022' );
|
|
||||||
|
|
||||||
String prefix = getPropertyPrefix();
|
String prefix = getPropertyPrefix();
|
||||||
focusWidth = UIManager.getInt( "Component.focusWidth" );
|
|
||||||
minimumWidth = UIManager.getInt( "Component.minimumWidth" );
|
minimumWidth = UIManager.getInt( "Component.minimumWidth" );
|
||||||
isIntelliJTheme = UIManager.getBoolean( "Component.isIntelliJTheme" );
|
isIntelliJTheme = UIManager.getBoolean( "Component.isIntelliJTheme" );
|
||||||
placeholderForeground = UIManager.getColor( prefix + ".placeholderForeground" );
|
placeholderForeground = UIManager.getColor( prefix + ".placeholderForeground" );
|
||||||
|
capsLockIcon = UIManager.getIcon( "PasswordField.capsLockIcon" );
|
||||||
|
|
||||||
LookAndFeel.installProperty( getComponent(), "opaque", focusWidth == 0 );
|
LookAndFeel.installProperty( getComponent(), "opaque", false );
|
||||||
|
|
||||||
MigLayoutVisualPadding.install( getComponent(), focusWidth );
|
MigLayoutVisualPadding.install( getComponent() );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -96,6 +98,7 @@ public class FlatPasswordFieldUI
|
|||||||
super.uninstallDefaults();
|
super.uninstallDefaults();
|
||||||
|
|
||||||
placeholderForeground = null;
|
placeholderForeground = null;
|
||||||
|
capsLockIcon = null;
|
||||||
|
|
||||||
MigLayoutVisualPadding.uninstall( getComponent() );
|
MigLayoutVisualPadding.uninstall( getComponent() );
|
||||||
}
|
}
|
||||||
@@ -105,7 +108,23 @@ public class FlatPasswordFieldUI
|
|||||||
super.installListeners();
|
super.installListeners();
|
||||||
|
|
||||||
focusListener = new FlatUIUtils.RepaintFocusListener( getComponent() );
|
focusListener = new FlatUIUtils.RepaintFocusListener( getComponent() );
|
||||||
|
capsLockListener = new KeyAdapter() {
|
||||||
|
@Override
|
||||||
|
public void keyPressed( KeyEvent e ) {
|
||||||
|
repaint( e );
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public void keyReleased( KeyEvent e ) {
|
||||||
|
repaint( e );
|
||||||
|
}
|
||||||
|
private void repaint( KeyEvent e ) {
|
||||||
|
if( e.getKeyCode() == KeyEvent.VK_CAPS_LOCK )
|
||||||
|
e.getComponent().repaint();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
getComponent().addFocusListener( focusListener );
|
getComponent().addFocusListener( focusListener );
|
||||||
|
getComponent().addKeyListener( capsLockListener );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -113,24 +132,41 @@ public class FlatPasswordFieldUI
|
|||||||
super.uninstallListeners();
|
super.uninstallListeners();
|
||||||
|
|
||||||
getComponent().removeFocusListener( focusListener );
|
getComponent().removeFocusListener( focusListener );
|
||||||
|
getComponent().removeKeyListener( capsLockListener );
|
||||||
focusListener = null;
|
focusListener = null;
|
||||||
|
capsLockListener = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Caret createCaret() {
|
||||||
|
return new FlatCaret( UIManager.getString( "TextComponent.selectAllOnFocusPolicy" ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void propertyChange( PropertyChangeEvent e ) {
|
protected void propertyChange( PropertyChangeEvent e ) {
|
||||||
super.propertyChange( e );
|
super.propertyChange( e );
|
||||||
|
FlatTextFieldUI.propertyChange( getComponent(), e );
|
||||||
if( FlatClientProperties.PLACEHOLDER_TEXT.equals( e.getPropertyName() ) )
|
|
||||||
getComponent().repaint();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void paintSafely( Graphics g ) {
|
protected void paintSafely( Graphics g ) {
|
||||||
FlatTextFieldUI.paintBackground( g, getComponent(), focusWidth, isIntelliJTheme );
|
FlatTextFieldUI.paintBackground( g, getComponent(), isIntelliJTheme );
|
||||||
FlatTextFieldUI.paintPlaceholder( g, getComponent(), placeholderForeground );
|
FlatTextFieldUI.paintPlaceholder( g, getComponent(), placeholderForeground );
|
||||||
|
paintCapsLock( g );
|
||||||
super.paintSafely( g );
|
super.paintSafely( g );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void paintCapsLock( Graphics g ) {
|
||||||
|
JTextComponent c = getComponent();
|
||||||
|
if( !FlatUIUtils.isPermanentFocusOwner( c ) ||
|
||||||
|
!Toolkit.getDefaultToolkit().getLockingKeyState( KeyEvent.VK_CAPS_LOCK ) )
|
||||||
|
return;
|
||||||
|
|
||||||
|
int y = (c.getHeight() - capsLockIcon.getIconHeight()) / 2;
|
||||||
|
int x = c.getWidth() - capsLockIcon.getIconWidth() - y;
|
||||||
|
capsLockIcon.paintIcon( c, g, x, y );
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void paintBackground( Graphics g ) {
|
protected void paintBackground( Graphics g ) {
|
||||||
// background is painted elsewhere
|
// background is painted elsewhere
|
||||||
@@ -138,17 +174,11 @@ public class FlatPasswordFieldUI
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Dimension getPreferredSize( JComponent c ) {
|
public Dimension getPreferredSize( JComponent c ) {
|
||||||
return applyMinimumWidth( super.getPreferredSize( c ), c );
|
return FlatTextFieldUI.applyMinimumWidth( c, super.getPreferredSize( c ), minimumWidth );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Dimension getMinimumSize( JComponent c ) {
|
public Dimension getMinimumSize( JComponent c ) {
|
||||||
return applyMinimumWidth( super.getMinimumSize( c ), c );
|
return FlatTextFieldUI.applyMinimumWidth( c, super.getMinimumSize( c ), minimumWidth );
|
||||||
}
|
|
||||||
|
|
||||||
private Dimension applyMinimumWidth( Dimension size, JComponent c ) {
|
|
||||||
int focusWidth = (c.getBorder() instanceof FlatBorder) ? this.focusWidth : 0;
|
|
||||||
size.width = Math.max( size.width, scale( minimumWidth + (focusWidth * 2) ) );
|
|
||||||
return size;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,375 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2020 FormDev Software GmbH
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.formdev.flatlaf.ui;
|
||||||
|
|
||||||
|
import java.awt.Color;
|
||||||
|
import java.awt.Component;
|
||||||
|
import java.awt.Container;
|
||||||
|
import java.awt.Dimension;
|
||||||
|
import java.awt.Insets;
|
||||||
|
import java.awt.Panel;
|
||||||
|
import java.awt.Point;
|
||||||
|
import java.awt.Rectangle;
|
||||||
|
import java.awt.Window;
|
||||||
|
import java.awt.event.ComponentEvent;
|
||||||
|
import java.awt.event.ComponentListener;
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import javax.swing.JComponent;
|
||||||
|
import javax.swing.JLayeredPane;
|
||||||
|
import javax.swing.JPanel;
|
||||||
|
import javax.swing.Popup;
|
||||||
|
import javax.swing.PopupFactory;
|
||||||
|
import javax.swing.RootPaneContainer;
|
||||||
|
import javax.swing.SwingUtilities;
|
||||||
|
import javax.swing.UIManager;
|
||||||
|
import javax.swing.border.Border;
|
||||||
|
import com.formdev.flatlaf.FlatClientProperties;
|
||||||
|
import com.formdev.flatlaf.util.SystemInfo;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A popup factory that adds drop shadows to popups on Windows.
|
||||||
|
* On macOS and Linux, heavy weight popups (without drop shadow) are produced and the
|
||||||
|
* operating system automatically adds drop shadows.
|
||||||
|
*
|
||||||
|
* @author Karl Tauber
|
||||||
|
*/
|
||||||
|
public class FlatPopupFactory
|
||||||
|
extends PopupFactory
|
||||||
|
{
|
||||||
|
private Method java8getPopupMethod;
|
||||||
|
private Method java9getPopupMethod;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Popup getPopup( Component owner, Component contents, int x, int y )
|
||||||
|
throws IllegalArgumentException
|
||||||
|
{
|
||||||
|
if( !isDropShadowPainted( owner, contents ) )
|
||||||
|
return new NonFlashingPopup( super.getPopup( owner, contents, x, y ), contents );
|
||||||
|
|
||||||
|
// macOS and Linux adds drop shadow to heavy weight popups
|
||||||
|
if( SystemInfo.IS_MAC || SystemInfo.IS_LINUX ) {
|
||||||
|
Popup popup = getHeavyWeightPopup( owner, contents, x, y );
|
||||||
|
if( popup == null )
|
||||||
|
popup = super.getPopup( owner, contents, x, y );
|
||||||
|
return new NonFlashingPopup( popup, contents );
|
||||||
|
}
|
||||||
|
|
||||||
|
// create drop shadow popup
|
||||||
|
return new DropShadowPopup( super.getPopup( owner, contents, x, y ), owner, contents );
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isDropShadowPainted( Component owner, Component contents ) {
|
||||||
|
Boolean b = isDropShadowPainted( owner );
|
||||||
|
if( b != null )
|
||||||
|
return b;
|
||||||
|
|
||||||
|
b = isDropShadowPainted( contents );
|
||||||
|
if( b != null )
|
||||||
|
return b;
|
||||||
|
|
||||||
|
return UIManager.getBoolean( "Popup.dropShadowPainted" );
|
||||||
|
}
|
||||||
|
|
||||||
|
private Boolean isDropShadowPainted( Component c ) {
|
||||||
|
if( !(c instanceof JComponent) )
|
||||||
|
return null;
|
||||||
|
|
||||||
|
Object value = ((JComponent)c).getClientProperty( FlatClientProperties.POPUP_DROP_SHADOW_PAINTED );
|
||||||
|
return (value instanceof Boolean ) ? (Boolean) value : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* There is no API in Java 8 to force creation of heavy weight popups,
|
||||||
|
* but it is possible with reflection. Java 9 provides a new method.
|
||||||
|
*
|
||||||
|
* When changing FlatLaf system requirements to Java 9+,
|
||||||
|
* then this method can be replaced with:
|
||||||
|
* return getPopup( owner, contents, x, y, true );
|
||||||
|
*/
|
||||||
|
private Popup getHeavyWeightPopup( Component owner, Component contents, int x, int y )
|
||||||
|
throws IllegalArgumentException
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
if( SystemInfo.IS_JAVA_9_OR_LATER ) {
|
||||||
|
if( java9getPopupMethod == null ) {
|
||||||
|
java9getPopupMethod = PopupFactory.class.getDeclaredMethod(
|
||||||
|
"getPopup", Component.class, Component.class, int.class, int.class, boolean.class );
|
||||||
|
}
|
||||||
|
return (Popup) java9getPopupMethod.invoke( this, owner, contents, x, y, true );
|
||||||
|
} else {
|
||||||
|
// Java 8
|
||||||
|
if( java8getPopupMethod == null ) {
|
||||||
|
java8getPopupMethod = PopupFactory.class.getDeclaredMethod(
|
||||||
|
"getPopup", Component.class, Component.class, int.class, int.class, int.class );
|
||||||
|
java8getPopupMethod.setAccessible( true );
|
||||||
|
}
|
||||||
|
return (Popup) java8getPopupMethod.invoke( this, owner, contents, x, y, /*HEAVY_WEIGHT_POPUP*/ 2 );
|
||||||
|
}
|
||||||
|
} catch( NoSuchMethodException | SecurityException | IllegalAccessException | InvocationTargetException ex ) {
|
||||||
|
// ignore
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//---- class NonFlashingPopup ---------------------------------------------
|
||||||
|
|
||||||
|
private class NonFlashingPopup
|
||||||
|
extends Popup
|
||||||
|
{
|
||||||
|
private Popup delegate;
|
||||||
|
|
||||||
|
// heavy weight
|
||||||
|
protected Window popupWindow;
|
||||||
|
private Color oldPopupWindowBackground;
|
||||||
|
|
||||||
|
NonFlashingPopup( Popup delegate, Component contents ) {
|
||||||
|
this.delegate = delegate;
|
||||||
|
|
||||||
|
popupWindow = SwingUtilities.windowForComponent( contents );
|
||||||
|
if( popupWindow != null ) {
|
||||||
|
// heavy weight popup
|
||||||
|
|
||||||
|
// fix background flashing which may occur on some platforms
|
||||||
|
// (e.g. macOS and Linux) when using dark theme
|
||||||
|
oldPopupWindowBackground = popupWindow.getBackground();
|
||||||
|
popupWindow.setBackground( contents.getBackground() );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void show() {
|
||||||
|
if( delegate != null )
|
||||||
|
delegate.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void hide() {
|
||||||
|
if( delegate != null ) {
|
||||||
|
delegate.hide();
|
||||||
|
delegate = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( popupWindow != null ) {
|
||||||
|
// restore background so that it can not affect other LaFs (when switching)
|
||||||
|
// because popup windows are cached and reused
|
||||||
|
popupWindow.setBackground( oldPopupWindowBackground );
|
||||||
|
popupWindow = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//---- class DropShadowPopup ----------------------------------------------
|
||||||
|
|
||||||
|
private class DropShadowPopup
|
||||||
|
extends NonFlashingPopup
|
||||||
|
{
|
||||||
|
private final Component owner;
|
||||||
|
|
||||||
|
// light weight
|
||||||
|
private JComponent lightComp;
|
||||||
|
private Border oldBorder;
|
||||||
|
private boolean oldOpaque;
|
||||||
|
|
||||||
|
// medium weight
|
||||||
|
private boolean mediumWeightShown;
|
||||||
|
private Panel mediumWeightPanel;
|
||||||
|
private JPanel dropShadowPanel;
|
||||||
|
private ComponentListener mediumPanelListener;
|
||||||
|
|
||||||
|
// heavy weight
|
||||||
|
private Popup dropShadowDelegate;
|
||||||
|
private Window dropShadowWindow;
|
||||||
|
private Color oldDropShadowWindowBackground;
|
||||||
|
|
||||||
|
DropShadowPopup( Popup delegate, Component owner, Component contents ) {
|
||||||
|
super( delegate, contents );
|
||||||
|
this.owner = owner;
|
||||||
|
|
||||||
|
Dimension size = contents.getPreferredSize();
|
||||||
|
if( size.width <= 0 || size.height <= 0 )
|
||||||
|
return;
|
||||||
|
|
||||||
|
if( popupWindow != null ) {
|
||||||
|
// heavy weight popup
|
||||||
|
|
||||||
|
// Since Java has a problem with sub-pixel text rendering on translucent
|
||||||
|
// windows, we can not make the popup window translucent for the drop shadow.
|
||||||
|
// (see https://bugs.openjdk.java.net/browse/JDK-8215980)
|
||||||
|
// The solution is to create a second translucent window that paints
|
||||||
|
// the drop shadow and is positioned behind the popup window.
|
||||||
|
|
||||||
|
// create panel that paints the drop shadow
|
||||||
|
JPanel dropShadowPanel = new JPanel();
|
||||||
|
dropShadowPanel.setBorder( createDropShadowBorder() );
|
||||||
|
dropShadowPanel.setOpaque( false );
|
||||||
|
|
||||||
|
// set preferred size of drop shadow panel
|
||||||
|
Dimension prefSize = popupWindow.getPreferredSize();
|
||||||
|
Insets insets = dropShadowPanel.getInsets();
|
||||||
|
dropShadowPanel.setPreferredSize( new Dimension(
|
||||||
|
prefSize.width + insets.left + insets.right,
|
||||||
|
prefSize.height + insets.top + insets.bottom ) );
|
||||||
|
|
||||||
|
// create heavy weight popup for drop shadow
|
||||||
|
int x = popupWindow.getX() - insets.left;
|
||||||
|
int y = popupWindow.getY() - insets.top;
|
||||||
|
dropShadowDelegate = getHeavyWeightPopup( owner, dropShadowPanel, x, y );
|
||||||
|
|
||||||
|
// make drop shadow popup window translucent
|
||||||
|
dropShadowWindow = SwingUtilities.windowForComponent( dropShadowPanel );
|
||||||
|
if( dropShadowWindow != null ) {
|
||||||
|
oldDropShadowWindowBackground = dropShadowWindow.getBackground();
|
||||||
|
dropShadowWindow.setBackground( new Color( 0, true ) );
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
mediumWeightPanel = (Panel) SwingUtilities.getAncestorOfClass( Panel.class, contents );
|
||||||
|
if( mediumWeightPanel != null ) {
|
||||||
|
// medium weight popup
|
||||||
|
dropShadowPanel = new JPanel();
|
||||||
|
dropShadowPanel.setBorder( createDropShadowBorder() );
|
||||||
|
dropShadowPanel.setOpaque( false );
|
||||||
|
dropShadowPanel.setSize( FlatUIUtils.addInsets( mediumWeightPanel.getSize(), dropShadowPanel.getInsets() ) );
|
||||||
|
} else {
|
||||||
|
// light weight popup
|
||||||
|
Container p = contents.getParent();
|
||||||
|
if( !(p instanceof JComponent) )
|
||||||
|
return;
|
||||||
|
|
||||||
|
lightComp = (JComponent) p;
|
||||||
|
oldBorder = lightComp.getBorder();
|
||||||
|
oldOpaque = lightComp.isOpaque();
|
||||||
|
lightComp.setBorder( createDropShadowBorder() );
|
||||||
|
lightComp.setOpaque( false );
|
||||||
|
lightComp.setSize( lightComp.getPreferredSize() );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Border createDropShadowBorder() {
|
||||||
|
return new FlatDropShadowBorder(
|
||||||
|
UIManager.getColor( "Popup.dropShadowColor" ),
|
||||||
|
UIManager.getInsets( "Popup.dropShadowInsets" ),
|
||||||
|
FlatUIUtils.getUIFloat( "Popup.dropShadowOpacity", 0.5f ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void show() {
|
||||||
|
if( dropShadowDelegate != null )
|
||||||
|
dropShadowDelegate.show();
|
||||||
|
|
||||||
|
if( mediumWeightPanel != null )
|
||||||
|
showMediumWeightDropShadow();
|
||||||
|
|
||||||
|
super.show();
|
||||||
|
|
||||||
|
// fix location of light weight popup in case it has left or top drop shadow
|
||||||
|
if( lightComp != null ) {
|
||||||
|
Insets insets = lightComp.getInsets();
|
||||||
|
if( insets.left != 0 || insets.top != 0 )
|
||||||
|
lightComp.setLocation( lightComp.getX() - insets.left, lightComp.getY() - insets.top );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void hide() {
|
||||||
|
if( dropShadowDelegate != null ) {
|
||||||
|
dropShadowDelegate.hide();
|
||||||
|
dropShadowDelegate = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( mediumWeightPanel != null ) {
|
||||||
|
hideMediumWeightDropShadow();
|
||||||
|
dropShadowPanel = null;
|
||||||
|
mediumWeightPanel = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
super.hide();
|
||||||
|
|
||||||
|
if( dropShadowWindow != null ) {
|
||||||
|
dropShadowWindow.setBackground( oldDropShadowWindowBackground );
|
||||||
|
dropShadowWindow = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( lightComp != null ) {
|
||||||
|
lightComp.setBorder( oldBorder );
|
||||||
|
lightComp.setOpaque( oldOpaque );
|
||||||
|
lightComp = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void showMediumWeightDropShadow() {
|
||||||
|
if( mediumWeightShown )
|
||||||
|
return;
|
||||||
|
|
||||||
|
mediumWeightShown = true;
|
||||||
|
|
||||||
|
Window window = SwingUtilities.windowForComponent( owner );
|
||||||
|
if( window == null )
|
||||||
|
return;
|
||||||
|
|
||||||
|
if( !(window instanceof RootPaneContainer) )
|
||||||
|
return;
|
||||||
|
|
||||||
|
dropShadowPanel.setVisible( false );
|
||||||
|
|
||||||
|
JLayeredPane layeredPane = ((RootPaneContainer)window).getLayeredPane();
|
||||||
|
layeredPane.add( dropShadowPanel, JLayeredPane.POPUP_LAYER, 0 );
|
||||||
|
|
||||||
|
mediumPanelListener = new ComponentListener() {
|
||||||
|
@Override
|
||||||
|
public void componentShown( ComponentEvent e ) {
|
||||||
|
if( dropShadowPanel != null )
|
||||||
|
dropShadowPanel.setVisible( true );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void componentHidden( ComponentEvent e ) {
|
||||||
|
if( dropShadowPanel != null )
|
||||||
|
dropShadowPanel.setVisible( false );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void componentMoved( ComponentEvent e ) {
|
||||||
|
if( dropShadowPanel != null && mediumWeightPanel != null ) {
|
||||||
|
Point location = mediumWeightPanel.getLocation();
|
||||||
|
Insets insets = dropShadowPanel.getInsets();
|
||||||
|
dropShadowPanel.setLocation( location.x - insets.left, location.y - insets.top );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void componentResized( ComponentEvent e ) {
|
||||||
|
if( dropShadowPanel != null )
|
||||||
|
dropShadowPanel.setSize( FlatUIUtils.addInsets( mediumWeightPanel.getSize(), dropShadowPanel.getInsets() ) );
|
||||||
|
}
|
||||||
|
};
|
||||||
|
mediumWeightPanel.addComponentListener( mediumPanelListener );
|
||||||
|
}
|
||||||
|
|
||||||
|
private void hideMediumWeightDropShadow() {
|
||||||
|
mediumWeightPanel.removeComponentListener( mediumPanelListener );
|
||||||
|
|
||||||
|
Container parent = dropShadowPanel.getParent();
|
||||||
|
if( parent != null ) {
|
||||||
|
Rectangle bounds = dropShadowPanel.getBounds();
|
||||||
|
parent.remove( dropShadowPanel );
|
||||||
|
parent.repaint( bounds.x, bounds.y, bounds.width, bounds.height );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -16,7 +16,12 @@
|
|||||||
|
|
||||||
package com.formdev.flatlaf.ui;
|
package com.formdev.flatlaf.ui;
|
||||||
|
|
||||||
|
import java.awt.Component;
|
||||||
|
import java.awt.Container;
|
||||||
|
import java.awt.Insets;
|
||||||
|
import javax.swing.JScrollPane;
|
||||||
import javax.swing.UIManager;
|
import javax.swing.UIManager;
|
||||||
|
import com.formdev.flatlaf.util.UIScale;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Border for {@link javax.swing.JPopupMenu}.
|
* Border for {@link javax.swing.JPopupMenu}.
|
||||||
@@ -33,4 +38,18 @@ public class FlatPopupMenuBorder
|
|||||||
super( UIManager.getInsets( "PopupMenu.borderInsets" ),
|
super( UIManager.getInsets( "PopupMenu.borderInsets" ),
|
||||||
UIManager.getColor( "PopupMenu.borderColor" ) );
|
UIManager.getColor( "PopupMenu.borderColor" ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Insets getBorderInsets( Component c, Insets insets ) {
|
||||||
|
if( c instanceof Container &&
|
||||||
|
((Container)c).getComponentCount() > 0 &&
|
||||||
|
((Container)c).getComponent( 0 ) instanceof JScrollPane )
|
||||||
|
{
|
||||||
|
// e.g. for combobox popups
|
||||||
|
insets.left = insets.top = insets.right = insets.bottom = UIScale.scale( 1 );
|
||||||
|
return insets;
|
||||||
|
}
|
||||||
|
|
||||||
|
return super.getBorderInsets( c, insets );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
package com.formdev.flatlaf.ui;
|
package com.formdev.flatlaf.ui;
|
||||||
|
|
||||||
|
import static com.formdev.flatlaf.FlatClientProperties.*;
|
||||||
import java.awt.Dimension;
|
import java.awt.Dimension;
|
||||||
import java.awt.FontMetrics;
|
import java.awt.FontMetrics;
|
||||||
import java.awt.Graphics;
|
import java.awt.Graphics;
|
||||||
@@ -23,6 +24,7 @@ import java.awt.Graphics2D;
|
|||||||
import java.awt.Insets;
|
import java.awt.Insets;
|
||||||
import java.awt.geom.Area;
|
import java.awt.geom.Area;
|
||||||
import java.awt.geom.RoundRectangle2D;
|
import java.awt.geom.RoundRectangle2D;
|
||||||
|
import java.beans.PropertyChangeListener;
|
||||||
import javax.swing.JComponent;
|
import javax.swing.JComponent;
|
||||||
import javax.swing.JProgressBar;
|
import javax.swing.JProgressBar;
|
||||||
import javax.swing.LookAndFeel;
|
import javax.swing.LookAndFeel;
|
||||||
@@ -60,6 +62,8 @@ public class FlatProgressBarUI
|
|||||||
protected Dimension horizontalSize;
|
protected Dimension horizontalSize;
|
||||||
protected Dimension verticalSize;
|
protected Dimension verticalSize;
|
||||||
|
|
||||||
|
private PropertyChangeListener propertyChangeListener;
|
||||||
|
|
||||||
public static ComponentUI createUI( JComponent c ) {
|
public static ComponentUI createUI( JComponent c ) {
|
||||||
return new FlatProgressBarUI();
|
return new FlatProgressBarUI();
|
||||||
}
|
}
|
||||||
@@ -75,11 +79,35 @@ public class FlatProgressBarUI
|
|||||||
verticalSize = UIManager.getDimension( "ProgressBar.verticalSize" );
|
verticalSize = UIManager.getDimension( "ProgressBar.verticalSize" );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void installListeners() {
|
||||||
|
super.installListeners();
|
||||||
|
|
||||||
|
propertyChangeListener = e -> {
|
||||||
|
switch( e.getPropertyName() ) {
|
||||||
|
case PROGRESS_BAR_LARGE_HEIGHT:
|
||||||
|
case PROGRESS_BAR_SQUARE:
|
||||||
|
progressBar.revalidate();
|
||||||
|
progressBar.repaint();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
progressBar.addPropertyChangeListener( propertyChangeListener );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void uninstallListeners() {
|
||||||
|
super.uninstallListeners();
|
||||||
|
|
||||||
|
progressBar.removePropertyChangeListener( propertyChangeListener );
|
||||||
|
propertyChangeListener = null;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Dimension getPreferredSize( JComponent c ) {
|
public Dimension getPreferredSize( JComponent c ) {
|
||||||
Dimension size = super.getPreferredSize( c );
|
Dimension size = super.getPreferredSize( c );
|
||||||
|
|
||||||
if( progressBar.isStringPainted() ) {
|
if( progressBar.isStringPainted() || clientPropertyBoolean( c, PROGRESS_BAR_LARGE_HEIGHT, false ) ) {
|
||||||
// recalculate progress height/width to make it smaller
|
// recalculate progress height/width to make it smaller
|
||||||
Insets insets = progressBar.getInsets();
|
Insets insets = progressBar.getInsets();
|
||||||
FontMetrics fm = progressBar.getFontMetrics( progressBar.getFont() );
|
FontMetrics fm = progressBar.getFontMetrics( progressBar.getFont() );
|
||||||
@@ -122,7 +150,9 @@ public class FlatProgressBarUI
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
boolean horizontal = (progressBar.getOrientation() == JProgressBar.HORIZONTAL);
|
boolean horizontal = (progressBar.getOrientation() == JProgressBar.HORIZONTAL);
|
||||||
int arc = Math.min( UIScale.scale( this.arc ), horizontal ? height : width );
|
int arc = clientPropertyBoolean( c, PROGRESS_BAR_SQUARE, false )
|
||||||
|
? 0
|
||||||
|
: Math.min( UIScale.scale( this.arc ), horizontal ? height : width );
|
||||||
|
|
||||||
FlatUIUtils.setRenderingHints( (Graphics2D) g );
|
FlatUIUtils.setRenderingHints( (Graphics2D) g );
|
||||||
|
|
||||||
@@ -163,4 +193,19 @@ public class FlatProgressBarUI
|
|||||||
paintString( g, x, y, width, height, amountFull, insets );
|
paintString( g, x, y, width, height, amountFull, insets );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void setAnimationIndex( int newValue ) {
|
||||||
|
super.setAnimationIndex( newValue );
|
||||||
|
|
||||||
|
// On HiDPI screens at 125%, 150% and 175% scaling, it occurs that antialiased painting
|
||||||
|
// may paint one pixel outside of the clipping area. This results in visual artifacts
|
||||||
|
// in indeterminate mode when the progress moves around.
|
||||||
|
// Unfortunately it is not safe to invoke getBox() from here (may throw NPE),
|
||||||
|
// which makes it impractical to get progress box and repaint increased box.
|
||||||
|
// Only solution is to repaint whole progress bar.
|
||||||
|
double systemScaleFactor = UIScale.getSystemScaleFactor( progressBar.getGraphicsConfiguration() );
|
||||||
|
if( (int) systemScaleFactor != systemScaleFactor )
|
||||||
|
progressBar.repaint();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,9 +16,11 @@
|
|||||||
|
|
||||||
package com.formdev.flatlaf.ui;
|
package com.formdev.flatlaf.ui;
|
||||||
|
|
||||||
import static com.formdev.flatlaf.util.UIScale.scale;
|
import java.awt.Dimension;
|
||||||
import java.beans.PropertyChangeListener;
|
import java.awt.Graphics;
|
||||||
|
import javax.swing.Icon;
|
||||||
import javax.swing.JComponent;
|
import javax.swing.JComponent;
|
||||||
|
import javax.swing.LookAndFeel;
|
||||||
import javax.swing.plaf.ComponentUI;
|
import javax.swing.plaf.ComponentUI;
|
||||||
import javax.swing.plaf.basic.BasicRadioButtonMenuItemUI;
|
import javax.swing.plaf.basic.BasicRadioButtonMenuItemUI;
|
||||||
|
|
||||||
@@ -43,13 +45,18 @@ import javax.swing.plaf.basic.BasicRadioButtonMenuItemUI;
|
|||||||
* @uiDefault RadioButtonMenuItem.arrowIcon Icon
|
* @uiDefault RadioButtonMenuItem.arrowIcon Icon
|
||||||
* @uiDefault RadioButtonMenuItem.checkIcon Icon
|
* @uiDefault RadioButtonMenuItem.checkIcon Icon
|
||||||
* @uiDefault RadioButtonMenuItem.opaque boolean
|
* @uiDefault RadioButtonMenuItem.opaque boolean
|
||||||
* @uiDefault RadioButtonMenuItem.evenHeight boolean
|
*
|
||||||
|
* <!-- FlatRadioButtonMenuItemUI -->
|
||||||
|
*
|
||||||
|
* @uiDefault MenuItem.iconTextGap int
|
||||||
*
|
*
|
||||||
* @author Karl Tauber
|
* @author Karl Tauber
|
||||||
*/
|
*/
|
||||||
public class FlatRadioButtonMenuItemUI
|
public class FlatRadioButtonMenuItemUI
|
||||||
extends BasicRadioButtonMenuItemUI
|
extends BasicRadioButtonMenuItemUI
|
||||||
{
|
{
|
||||||
|
private FlatMenuItemRenderer renderer;
|
||||||
|
|
||||||
public static ComponentUI createUI( JComponent c ) {
|
public static ComponentUI createUI( JComponent c ) {
|
||||||
return new FlatRadioButtonMenuItemUI();
|
return new FlatRadioButtonMenuItemUI();
|
||||||
}
|
}
|
||||||
@@ -58,20 +65,30 @@ public class FlatRadioButtonMenuItemUI
|
|||||||
protected void installDefaults() {
|
protected void installDefaults() {
|
||||||
super.installDefaults();
|
super.installDefaults();
|
||||||
|
|
||||||
// scale
|
LookAndFeel.installProperty( menuItem, "iconTextGap", FlatUIUtils.getUIInt( "MenuItem.iconTextGap", 4 ) );
|
||||||
defaultTextIconGap = scale( defaultTextIconGap );
|
|
||||||
|
renderer = createRenderer();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Scale defaultTextIconGap again if iconTextGap property has changed.
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
protected PropertyChangeListener createPropertyChangeListener( JComponent c ) {
|
protected void uninstallDefaults() {
|
||||||
PropertyChangeListener superListener = super.createPropertyChangeListener( c );
|
super.uninstallDefaults();
|
||||||
return e -> {
|
|
||||||
superListener.propertyChange( e );
|
renderer = null;
|
||||||
if( e.getPropertyName() == "iconTextGap" )
|
}
|
||||||
defaultTextIconGap = scale( defaultTextIconGap );
|
|
||||||
};
|
protected FlatMenuItemRenderer createRenderer() {
|
||||||
|
return new FlatMenuItemRenderer( menuItem, checkIcon, arrowIcon, acceleratorFont, acceleratorDelimiter );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Dimension getPreferredMenuItemSize( JComponent c, Icon checkIcon, Icon arrowIcon, int defaultTextIconGap ) {
|
||||||
|
return renderer.getPreferredMenuItemSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void paint( Graphics g, JComponent c ) {
|
||||||
|
renderer.paintMenuItem( g, selectionBackground, selectionForeground, disabledForeground,
|
||||||
|
acceleratorForeground, acceleratorSelectionForeground );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,7 +23,6 @@ import java.awt.Graphics;
|
|||||||
import java.awt.Insets;
|
import java.awt.Insets;
|
||||||
import java.awt.Rectangle;
|
import java.awt.Rectangle;
|
||||||
import javax.swing.AbstractButton;
|
import javax.swing.AbstractButton;
|
||||||
import javax.swing.CellRendererPane;
|
|
||||||
import javax.swing.JComponent;
|
import javax.swing.JComponent;
|
||||||
import javax.swing.LookAndFeel;
|
import javax.swing.LookAndFeel;
|
||||||
import javax.swing.UIManager;
|
import javax.swing.UIManager;
|
||||||
@@ -101,6 +100,8 @@ public class FlatRadioButtonUI
|
|||||||
@Override
|
@Override
|
||||||
public Dimension getPreferredSize( JComponent c ) {
|
public Dimension getPreferredSize( JComponent c ) {
|
||||||
Dimension size = super.getPreferredSize( c );
|
Dimension size = super.getPreferredSize( c );
|
||||||
|
if( size == null )
|
||||||
|
return null;
|
||||||
|
|
||||||
// small insets fix
|
// small insets fix
|
||||||
int focusWidth = getIconFocusWidth( c );
|
int focusWidth = getIconFocusWidth( c );
|
||||||
@@ -118,11 +119,12 @@ public class FlatRadioButtonUI
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void paint( Graphics g, JComponent c ) {
|
public void paint( Graphics g, JComponent c ) {
|
||||||
// fill background even if opaque if
|
// fill background even if not opaque if
|
||||||
// - used as cell renderer (because of selection background)
|
// - contentAreaFilled is true and
|
||||||
// - if background was explicitly set to a non-UIResource color
|
// - if background was explicitly set to a non-UIResource color
|
||||||
if( !c.isOpaque() &&
|
if( !c.isOpaque() &&
|
||||||
(c.getParent() instanceof CellRendererPane || !(c.getBackground() instanceof UIResource)) )
|
((AbstractButton)c).isContentAreaFilled() &&
|
||||||
|
!(c.getBackground() instanceof UIResource) )
|
||||||
{
|
{
|
||||||
g.setColor( c.getBackground() );
|
g.setColor( c.getBackground() );
|
||||||
g.fillRect( 0, 0, c.getWidth(), c.getHeight() );
|
g.fillRect( 0, 0, c.getWidth(), c.getHeight() );
|
||||||
|
|||||||
@@ -0,0 +1,62 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2020 FormDev Software GmbH
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.formdev.flatlaf.ui;
|
||||||
|
|
||||||
|
import java.awt.Color;
|
||||||
|
import java.awt.Container;
|
||||||
|
import javax.swing.JComponent;
|
||||||
|
import javax.swing.JDialog;
|
||||||
|
import javax.swing.JFrame;
|
||||||
|
import javax.swing.JRootPane;
|
||||||
|
import javax.swing.UIManager;
|
||||||
|
import javax.swing.plaf.ComponentUI;
|
||||||
|
import javax.swing.plaf.UIResource;
|
||||||
|
import javax.swing.plaf.basic.BasicRootPaneUI;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides the Flat LaF UI delegate for {@link javax.swing.JRootPane}.
|
||||||
|
*
|
||||||
|
* @author Karl Tauber
|
||||||
|
*/
|
||||||
|
public class FlatRootPaneUI
|
||||||
|
extends BasicRootPaneUI
|
||||||
|
{
|
||||||
|
private static ComponentUI instance;
|
||||||
|
|
||||||
|
public static ComponentUI createUI( JComponent c ) {
|
||||||
|
if( instance == null )
|
||||||
|
instance = new FlatRootPaneUI();
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void installDefaults( JRootPane c ) {
|
||||||
|
super.installDefaults( c );
|
||||||
|
|
||||||
|
// Update background color of JFrame or JDialog parent to avoid bad border
|
||||||
|
// on HiDPI screens when switching from light to dark Laf.
|
||||||
|
// The background of JFrame is initialized in JFrame.frameInit() and
|
||||||
|
// the background of JDialog in JDialog.dialogInit(),
|
||||||
|
// but it was not updated when switching Laf.
|
||||||
|
Container parent = c.getParent();
|
||||||
|
if( parent instanceof JFrame || parent instanceof JDialog ) {
|
||||||
|
Color background = parent.getBackground();
|
||||||
|
if( background == null || background instanceof UIResource )
|
||||||
|
parent.setBackground( UIManager.getColor( "control" ) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
package com.formdev.flatlaf.ui;
|
package com.formdev.flatlaf.ui;
|
||||||
|
|
||||||
import static com.formdev.flatlaf.util.UIScale.scale;
|
import java.awt.Component;
|
||||||
import javax.swing.UIManager;
|
import javax.swing.UIManager;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -32,7 +32,7 @@ public class FlatRoundBorder
|
|||||||
protected final int arc = UIManager.getInt( "Component.arc" );
|
protected final int arc = UIManager.getInt( "Component.arc" );
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected float getArc() {
|
protected int getArc( Component c ) {
|
||||||
return scale( (float) arc );
|
return FlatUIUtils.isRoundRect( c ) ? Short.MAX_VALUE : arc;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,15 +19,20 @@ package com.formdev.flatlaf.ui;
|
|||||||
import java.awt.Color;
|
import java.awt.Color;
|
||||||
import java.awt.Dimension;
|
import java.awt.Dimension;
|
||||||
import java.awt.Graphics;
|
import java.awt.Graphics;
|
||||||
|
import java.awt.Graphics2D;
|
||||||
|
import java.awt.Insets;
|
||||||
import java.awt.Rectangle;
|
import java.awt.Rectangle;
|
||||||
import java.awt.event.MouseAdapter;
|
import java.awt.event.MouseAdapter;
|
||||||
import java.awt.event.MouseEvent;
|
import java.awt.event.MouseEvent;
|
||||||
import java.beans.PropertyChangeEvent;
|
import java.beans.PropertyChangeEvent;
|
||||||
import java.beans.PropertyChangeListener;
|
import java.beans.PropertyChangeListener;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
import javax.swing.InputMap;
|
||||||
import javax.swing.JButton;
|
import javax.swing.JButton;
|
||||||
import javax.swing.JComponent;
|
import javax.swing.JComponent;
|
||||||
|
import javax.swing.JScrollBar;
|
||||||
import javax.swing.JScrollPane;
|
import javax.swing.JScrollPane;
|
||||||
|
import javax.swing.SwingUtilities;
|
||||||
import javax.swing.UIManager;
|
import javax.swing.UIManager;
|
||||||
import javax.swing.plaf.ComponentUI;
|
import javax.swing.plaf.ComponentUI;
|
||||||
import javax.swing.plaf.basic.BasicScrollBarUI;
|
import javax.swing.plaf.basic.BasicScrollBarUI;
|
||||||
@@ -50,8 +55,13 @@ import com.formdev.flatlaf.util.UIScale;
|
|||||||
*
|
*
|
||||||
* <!-- FlatScrollBarUI -->
|
* <!-- FlatScrollBarUI -->
|
||||||
*
|
*
|
||||||
|
* @uiDefault ScrollBar.trackInsets Insets
|
||||||
|
* @uiDefault ScrollBar.thumbInsets Insets
|
||||||
|
* @uiDefault ScrollBar.trackArc int
|
||||||
|
* @uiDefault ScrollBar.thumbArc int
|
||||||
* @uiDefault ScrollBar.hoverTrackColor Color
|
* @uiDefault ScrollBar.hoverTrackColor Color
|
||||||
* @uiDefault ScrollBar.hoverThumbColor Color
|
* @uiDefault ScrollBar.hoverThumbColor Color
|
||||||
|
* @uiDefault ScrollBar.hoverThumbWithTrack boolean
|
||||||
* @uiDefault Component.arrowType String triangle (default) or chevron
|
* @uiDefault Component.arrowType String triangle (default) or chevron
|
||||||
* @uiDefault ScrollBar.showButtons boolean
|
* @uiDefault ScrollBar.showButtons boolean
|
||||||
* @uiDefault ScrollBar.buttonArrowColor Color
|
* @uiDefault ScrollBar.buttonArrowColor Color
|
||||||
@@ -62,8 +72,13 @@ import com.formdev.flatlaf.util.UIScale;
|
|||||||
public class FlatScrollBarUI
|
public class FlatScrollBarUI
|
||||||
extends BasicScrollBarUI
|
extends BasicScrollBarUI
|
||||||
{
|
{
|
||||||
|
protected Insets trackInsets;
|
||||||
|
protected Insets thumbInsets;
|
||||||
|
protected int trackArc;
|
||||||
|
protected int thumbArc;
|
||||||
protected Color hoverTrackColor;
|
protected Color hoverTrackColor;
|
||||||
protected Color hoverThumbColor;
|
protected Color hoverThumbColor;
|
||||||
|
protected boolean hoverThumbWithTrack;
|
||||||
|
|
||||||
protected boolean showButtons;
|
protected boolean showButtons;
|
||||||
protected String arrowType;
|
protected String arrowType;
|
||||||
@@ -71,8 +86,8 @@ public class FlatScrollBarUI
|
|||||||
protected Color buttonDisabledArrowColor;
|
protected Color buttonDisabledArrowColor;
|
||||||
|
|
||||||
private MouseAdapter hoverListener;
|
private MouseAdapter hoverListener;
|
||||||
private boolean hoverTrack;
|
protected boolean hoverTrack;
|
||||||
private boolean hoverThumb;
|
protected boolean hoverThumb;
|
||||||
|
|
||||||
public static ComponentUI createUI( JComponent c ) {
|
public static ComponentUI createUI( JComponent c ) {
|
||||||
return new FlatScrollBarUI();
|
return new FlatScrollBarUI();
|
||||||
@@ -100,8 +115,13 @@ public class FlatScrollBarUI
|
|||||||
protected void installDefaults() {
|
protected void installDefaults() {
|
||||||
super.installDefaults();
|
super.installDefaults();
|
||||||
|
|
||||||
|
trackInsets = UIManager.getInsets( "ScrollBar.trackInsets" );
|
||||||
|
thumbInsets = UIManager.getInsets( "ScrollBar.thumbInsets" );
|
||||||
|
trackArc = UIManager.getInt( "ScrollBar.trackArc" );
|
||||||
|
thumbArc = UIManager.getInt( "ScrollBar.thumbArc" );
|
||||||
hoverTrackColor = UIManager.getColor( "ScrollBar.hoverTrackColor" );
|
hoverTrackColor = UIManager.getColor( "ScrollBar.hoverTrackColor" );
|
||||||
hoverThumbColor = UIManager.getColor( "ScrollBar.hoverThumbColor" );
|
hoverThumbColor = UIManager.getColor( "ScrollBar.hoverThumbColor" );
|
||||||
|
hoverThumbWithTrack = UIManager.getBoolean( "ScrollBar.hoverThumbWithTrack" );
|
||||||
|
|
||||||
showButtons = UIManager.getBoolean( "ScrollBar.showButtons" );
|
showButtons = UIManager.getBoolean( "ScrollBar.showButtons" );
|
||||||
arrowType = UIManager.getString( "Component.arrowType" );
|
arrowType = UIManager.getString( "Component.arrowType" );
|
||||||
@@ -113,6 +133,8 @@ public class FlatScrollBarUI
|
|||||||
protected void uninstallDefaults() {
|
protected void uninstallDefaults() {
|
||||||
super.uninstallDefaults();
|
super.uninstallDefaults();
|
||||||
|
|
||||||
|
trackInsets = null;
|
||||||
|
thumbInsets = null;
|
||||||
hoverTrackColor = null;
|
hoverTrackColor = null;
|
||||||
hoverThumbColor = null;
|
hoverThumbColor = null;
|
||||||
|
|
||||||
@@ -127,9 +149,24 @@ public class FlatScrollBarUI
|
|||||||
public void propertyChange( PropertyChangeEvent e ) {
|
public void propertyChange( PropertyChangeEvent e ) {
|
||||||
super.propertyChange( e );
|
super.propertyChange( e );
|
||||||
|
|
||||||
if( FlatClientProperties.SCROLL_BAR_SHOW_BUTTONS.equals( e.getPropertyName() ) ) {
|
switch( e.getPropertyName() ) {
|
||||||
scrollbar.revalidate();
|
case FlatClientProperties.SCROLL_BAR_SHOW_BUTTONS:
|
||||||
scrollbar.repaint();
|
scrollbar.revalidate();
|
||||||
|
scrollbar.repaint();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "componentOrientation":
|
||||||
|
// this is missing in BasicScrollBarUI.Handler.propertyChange()
|
||||||
|
InputMap inputMap = (InputMap) UIManager.get( "ScrollBar.ancestorInputMap" );
|
||||||
|
if( !scrollbar.getComponentOrientation().isLeftToRight() ) {
|
||||||
|
InputMap rtlInputMap = (InputMap) UIManager.get( "ScrollBar.ancestorInputMap.RightToLeft" );
|
||||||
|
if( rtlInputMap != null ) {
|
||||||
|
rtlInputMap.setParent( inputMap );
|
||||||
|
inputMap = rtlInputMap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SwingUtilities.replaceUIInputMap( scrollbar, JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, inputMap );
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -154,6 +191,11 @@ public class FlatScrollBarUI
|
|||||||
FlatArrowButton button = new FlatArrowButton( orientation,
|
FlatArrowButton button = new FlatArrowButton( orientation,
|
||||||
arrowType, buttonArrowColor, buttonDisabledArrowColor, null, hoverTrackColor )
|
arrowType, buttonArrowColor, buttonDisabledArrowColor, null, hoverTrackColor )
|
||||||
{
|
{
|
||||||
|
@Override
|
||||||
|
protected Color deriveHoverBackground( Color hoverBackground ) {
|
||||||
|
return getTrackColor( scrollbar, true ) ;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Dimension getPreferredSize() {
|
public Dimension getPreferredSize() {
|
||||||
if( isShowButtons() ) {
|
if( isShowButtons() ) {
|
||||||
@@ -186,6 +228,45 @@ public class FlatScrollBarUI
|
|||||||
return (showButtons != null) ? Objects.equals( showButtons, true ) : this.showButtons;
|
return (showButtons != null) ? Objects.equals( showButtons, true ) : this.showButtons;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void paint( Graphics g, JComponent c ) {
|
||||||
|
FlatUIUtils.setRenderingHints( (Graphics2D) g );
|
||||||
|
super.paint( g, c );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void paintTrack( Graphics g, JComponent c, Rectangle trackBounds ) {
|
||||||
|
g.setColor( getTrackColor( c, hoverTrack ) );
|
||||||
|
paintTrackOrThumb( g, c, trackBounds, trackInsets, trackArc );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void paintThumb( Graphics g, JComponent c, Rectangle thumbBounds ) {
|
||||||
|
if( thumbBounds.isEmpty() || !scrollbar.isEnabled() )
|
||||||
|
return;
|
||||||
|
|
||||||
|
g.setColor( getThumbColor( c, hoverThumb ) );
|
||||||
|
paintTrackOrThumb( g, c, thumbBounds, thumbInsets, thumbArc );
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void paintTrackOrThumb( Graphics g, JComponent c, Rectangle bounds, Insets insets, int arc ) {
|
||||||
|
// rotate insets for horizontal orientation because they are given for vertical orientation
|
||||||
|
if( scrollbar.getOrientation() == JScrollBar.HORIZONTAL )
|
||||||
|
insets = new Insets( insets.right, insets.top, insets.left, insets.bottom );
|
||||||
|
|
||||||
|
// subtract insets from bounds
|
||||||
|
bounds = FlatUIUtils.subtractInsets( bounds, UIScale.scale( insets ) );
|
||||||
|
|
||||||
|
if( arc <= 0 ) {
|
||||||
|
// paint rectangle
|
||||||
|
g.fillRect( bounds.x, bounds.y, bounds.width, bounds.height );
|
||||||
|
} else {
|
||||||
|
// paint round rectangle
|
||||||
|
arc = Math.min( UIScale.scale( arc ), Math.min( bounds.width, bounds.height ) );
|
||||||
|
g.fillRoundRect( bounds.x, bounds.y, bounds.width, bounds.height, arc, arc );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void paintDecreaseHighlight( Graphics g ) {
|
protected void paintDecreaseHighlight( Graphics g ) {
|
||||||
// do not paint
|
// do not paint
|
||||||
@@ -196,19 +277,15 @@ public class FlatScrollBarUI
|
|||||||
// do not paint
|
// do not paint
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
protected Color getTrackColor( JComponent c, boolean hover ) {
|
||||||
protected void paintTrack( Graphics g, JComponent c, Rectangle trackBounds ) {
|
Color trackColor = FlatUIUtils.deriveColor( this.trackColor, c.getBackground() );
|
||||||
g.setColor( hoverTrack ? hoverTrackColor : trackColor );
|
return hover ? FlatUIUtils.deriveColor( hoverTrackColor, trackColor ) : trackColor;
|
||||||
g.fillRect( trackBounds.x, trackBounds.y, trackBounds.width, trackBounds.height );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
protected Color getThumbColor( JComponent c, boolean hover ) {
|
||||||
protected void paintThumb( Graphics g, JComponent c, Rectangle thumbBounds ) {
|
Color trackColor = FlatUIUtils.deriveColor( this.trackColor, c.getBackground() );
|
||||||
if( thumbBounds.isEmpty() || !scrollbar.isEnabled() )
|
Color thumbColor = FlatUIUtils.deriveColor( this.thumbColor, trackColor );
|
||||||
return;
|
return hover ? FlatUIUtils.deriveColor( hoverThumbColor, thumbColor ) : thumbColor;
|
||||||
|
|
||||||
g.setColor( hoverThumb ? hoverThumbColor : thumbColor );
|
|
||||||
g.fillRect( thumbBounds.x, thumbBounds.y, thumbBounds.width, thumbBounds.height );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -259,7 +336,7 @@ public class FlatScrollBarUI
|
|||||||
boolean inThumb = getThumbBounds().contains( x, y );
|
boolean inThumb = getThumbBounds().contains( x, y );
|
||||||
if( inTrack != hoverTrack || inThumb != hoverThumb ) {
|
if( inTrack != hoverTrack || inThumb != hoverThumb ) {
|
||||||
hoverTrack = inTrack;
|
hoverTrack = inTrack;
|
||||||
hoverThumb = inThumb;
|
hoverThumb = inThumb || (hoverThumbWithTrack && inTrack);
|
||||||
repaint();
|
repaint();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,22 +14,37 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Smooth scrolling code partly based on code from IntelliJ IDEA Community Edition,
|
||||||
|
* which is licensed under the Apache 2.0 license. Copyright 2000-2016 JetBrains s.r.o.
|
||||||
|
* See: https://github.com/JetBrains/intellij-community/blob/31e1b5a8e43219b9571951bab6457cfb3012e3ef/platform/platform-api/src/com/intellij/ui/components/SmoothScrollPane.java#L141-L185
|
||||||
|
*
|
||||||
|
*/
|
||||||
package com.formdev.flatlaf.ui;
|
package com.formdev.flatlaf.ui;
|
||||||
|
|
||||||
import java.awt.Component;
|
import java.awt.Component;
|
||||||
import java.awt.Graphics;
|
import java.awt.Graphics;
|
||||||
import java.awt.Insets;
|
import java.awt.Insets;
|
||||||
|
import java.awt.Rectangle;
|
||||||
import java.awt.event.ContainerEvent;
|
import java.awt.event.ContainerEvent;
|
||||||
import java.awt.event.ContainerListener;
|
import java.awt.event.ContainerListener;
|
||||||
import java.awt.event.FocusEvent;
|
import java.awt.event.FocusEvent;
|
||||||
import java.awt.event.FocusListener;
|
import java.awt.event.FocusListener;
|
||||||
|
import java.awt.event.MouseWheelEvent;
|
||||||
|
import java.awt.event.MouseWheelListener;
|
||||||
import java.beans.PropertyChangeEvent;
|
import java.beans.PropertyChangeEvent;
|
||||||
import java.beans.PropertyChangeListener;
|
import java.beans.PropertyChangeListener;
|
||||||
|
import javax.swing.BorderFactory;
|
||||||
|
import javax.swing.JButton;
|
||||||
import javax.swing.JComponent;
|
import javax.swing.JComponent;
|
||||||
import javax.swing.JScrollBar;
|
import javax.swing.JScrollBar;
|
||||||
import javax.swing.JScrollPane;
|
import javax.swing.JScrollPane;
|
||||||
|
import javax.swing.JTable;
|
||||||
import javax.swing.JViewport;
|
import javax.swing.JViewport;
|
||||||
import javax.swing.LookAndFeel;
|
import javax.swing.LookAndFeel;
|
||||||
|
import javax.swing.ScrollPaneConstants;
|
||||||
|
import javax.swing.Scrollable;
|
||||||
|
import javax.swing.SwingConstants;
|
||||||
import javax.swing.UIManager;
|
import javax.swing.UIManager;
|
||||||
import javax.swing.plaf.ComponentUI;
|
import javax.swing.plaf.ComponentUI;
|
||||||
import javax.swing.plaf.basic.BasicScrollPaneUI;
|
import javax.swing.plaf.basic.BasicScrollPaneUI;
|
||||||
@@ -46,6 +61,10 @@ import com.formdev.flatlaf.FlatClientProperties;
|
|||||||
* @uiDefault ScrollPane.border Border
|
* @uiDefault ScrollPane.border Border
|
||||||
* @uiDefault ScrollPane.viewportBorder Border
|
* @uiDefault ScrollPane.viewportBorder Border
|
||||||
*
|
*
|
||||||
|
* <!-- FlatScrollPaneUI -->
|
||||||
|
*
|
||||||
|
* @uiDefault ScrollPane.smoothScrolling boolean
|
||||||
|
*
|
||||||
* @author Karl Tauber
|
* @author Karl Tauber
|
||||||
*/
|
*/
|
||||||
public class FlatScrollPaneUI
|
public class FlatScrollPaneUI
|
||||||
@@ -64,7 +83,7 @@ public class FlatScrollPaneUI
|
|||||||
int focusWidth = UIManager.getInt( "Component.focusWidth" );
|
int focusWidth = UIManager.getInt( "Component.focusWidth" );
|
||||||
LookAndFeel.installProperty( c, "opaque", focusWidth == 0 );
|
LookAndFeel.installProperty( c, "opaque", focusWidth == 0 );
|
||||||
|
|
||||||
MigLayoutVisualPadding.install( scrollpane, focusWidth );
|
MigLayoutVisualPadding.install( scrollpane );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -90,6 +109,130 @@ public class FlatScrollPaneUI
|
|||||||
handler = null;
|
handler = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected MouseWheelListener createMouseWheelListener() {
|
||||||
|
return new BasicScrollPaneUI.MouseWheelHandler() {
|
||||||
|
@Override
|
||||||
|
public void mouseWheelMoved( MouseWheelEvent e ) {
|
||||||
|
// Note: Getting UI value "ScrollPane.smoothScrolling" here to allow
|
||||||
|
// applications to turn smooth scrolling on or off at any time
|
||||||
|
// (e.g. in application options dialog).
|
||||||
|
if( UIManager.getBoolean( "ScrollPane.smoothScrolling" ) &&
|
||||||
|
scrollpane.isWheelScrollingEnabled() &&
|
||||||
|
e.getScrollType() == MouseWheelEvent.WHEEL_UNIT_SCROLL &&
|
||||||
|
e.getPreciseWheelRotation() != 0 &&
|
||||||
|
e.getPreciseWheelRotation() != e.getWheelRotation() )
|
||||||
|
{
|
||||||
|
mouseWheelMovedSmooth( e );
|
||||||
|
} else
|
||||||
|
super.mouseWheelMoved( e );
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final double EPSILON = 1e-5d;
|
||||||
|
|
||||||
|
private void mouseWheelMovedSmooth( MouseWheelEvent e ) {
|
||||||
|
// return if there is no viewport
|
||||||
|
JViewport viewport = scrollpane.getViewport();
|
||||||
|
if( viewport == null )
|
||||||
|
return;
|
||||||
|
|
||||||
|
// find scrollbar to scroll
|
||||||
|
JScrollBar scrollbar = scrollpane.getVerticalScrollBar();
|
||||||
|
if( scrollbar == null || !scrollbar.isVisible() || e.isShiftDown() ) {
|
||||||
|
scrollbar = scrollpane.getHorizontalScrollBar();
|
||||||
|
if( scrollbar == null || !scrollbar.isVisible() )
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// consume event
|
||||||
|
e.consume();
|
||||||
|
|
||||||
|
// get precise wheel rotation
|
||||||
|
double rotation = e.getPreciseWheelRotation();
|
||||||
|
|
||||||
|
// get unit and block increment
|
||||||
|
int unitIncrement;
|
||||||
|
int blockIncrement;
|
||||||
|
int orientation = scrollbar.getOrientation();
|
||||||
|
Component view = viewport.getView();
|
||||||
|
if( view instanceof Scrollable ) {
|
||||||
|
Scrollable scrollable = (Scrollable) view;
|
||||||
|
|
||||||
|
// Use (0, 0) view position to obtain constant unit increment of first item
|
||||||
|
// (which might otherwise be variable on smaller-than-unit scrolling).
|
||||||
|
Rectangle visibleRect = new Rectangle( viewport.getViewSize() );
|
||||||
|
unitIncrement = scrollable.getScrollableUnitIncrement( visibleRect, orientation, 1 );
|
||||||
|
blockIncrement = scrollable.getScrollableBlockIncrement( visibleRect, orientation, 1 );
|
||||||
|
|
||||||
|
if( unitIncrement > 0 ) {
|
||||||
|
// For the case that the first item (e.g. in a list) is larger
|
||||||
|
// than the other items, get the unit increment of the second item
|
||||||
|
// and use the smaller one.
|
||||||
|
if( orientation == SwingConstants.VERTICAL ) {
|
||||||
|
visibleRect.y += unitIncrement;
|
||||||
|
visibleRect.height -= unitIncrement;
|
||||||
|
} else {
|
||||||
|
visibleRect.x += unitIncrement;
|
||||||
|
visibleRect.width -= unitIncrement;
|
||||||
|
}
|
||||||
|
int unitIncrement2 = scrollable.getScrollableUnitIncrement( visibleRect, orientation, 1 );
|
||||||
|
if( unitIncrement2 > 0 )
|
||||||
|
unitIncrement = Math.min( unitIncrement, unitIncrement2 );
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
int direction = rotation < 0 ? -1 : 1;
|
||||||
|
unitIncrement = scrollbar.getUnitIncrement( direction );
|
||||||
|
blockIncrement = scrollbar.getBlockIncrement( direction );
|
||||||
|
}
|
||||||
|
|
||||||
|
// limit scroll amount (number of units to scroll) for small viewports
|
||||||
|
// (e.g. vertical scrolling in file chooser)
|
||||||
|
int scrollAmount = e.getScrollAmount();
|
||||||
|
int viewportWH = (orientation == SwingConstants.VERTICAL)
|
||||||
|
? viewport.getHeight()
|
||||||
|
: viewport.getWidth();
|
||||||
|
if( unitIncrement * scrollAmount > viewportWH )
|
||||||
|
scrollAmount = Math.max( viewportWH / unitIncrement, 1 );
|
||||||
|
|
||||||
|
// compute relative delta
|
||||||
|
double delta = rotation * scrollAmount * unitIncrement;
|
||||||
|
boolean adjustDelta = Math.abs( rotation ) < (1.0 + EPSILON);
|
||||||
|
double adjustedDelta = adjustDelta
|
||||||
|
? Math.max( -blockIncrement, Math.min( delta, blockIncrement ) )
|
||||||
|
: delta;
|
||||||
|
|
||||||
|
// compute new value
|
||||||
|
int value = scrollbar.getValue();
|
||||||
|
double minDelta = scrollbar.getMinimum() - value;
|
||||||
|
double maxDelta = scrollbar.getMaximum() - scrollbar.getModel().getExtent() - value;
|
||||||
|
double boundedDelta = Math.max( minDelta, Math.min( adjustedDelta, maxDelta ) );
|
||||||
|
int newValue = value + (int) Math.round( boundedDelta );
|
||||||
|
|
||||||
|
// set new value
|
||||||
|
if( newValue != value )
|
||||||
|
scrollbar.setValue( newValue );
|
||||||
|
|
||||||
|
/*debug
|
||||||
|
System.out.println( String.format( "%4d %9f / %4d %4d / %12f %5s %12f / %4d %4d %4d / %12f %12f %12f / %4d",
|
||||||
|
e.getWheelRotation(),
|
||||||
|
e.getPreciseWheelRotation(),
|
||||||
|
unitIncrement,
|
||||||
|
blockIncrement,
|
||||||
|
delta,
|
||||||
|
adjustDelta,
|
||||||
|
adjustedDelta,
|
||||||
|
value,
|
||||||
|
scrollbar.getMinimum(),
|
||||||
|
scrollbar.getMaximum(),
|
||||||
|
minDelta,
|
||||||
|
maxDelta,
|
||||||
|
boundedDelta,
|
||||||
|
newValue ) );
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected PropertyChangeListener createPropertyChangeListener() {
|
protected PropertyChangeListener createPropertyChangeListener() {
|
||||||
return new BasicScrollPaneUI.PropertyChangeHandler() {
|
return new BasicScrollPaneUI.PropertyChangeHandler() {
|
||||||
@@ -97,17 +240,35 @@ public class FlatScrollPaneUI
|
|||||||
public void propertyChange( PropertyChangeEvent e ) {
|
public void propertyChange( PropertyChangeEvent e ) {
|
||||||
super.propertyChange( e );
|
super.propertyChange( e );
|
||||||
|
|
||||||
if( FlatClientProperties.SCROLL_BAR_SHOW_BUTTONS.equals( e.getPropertyName() ) ) {
|
switch( e.getPropertyName() ) {
|
||||||
JScrollBar vsb = scrollpane.getVerticalScrollBar();
|
case FlatClientProperties.SCROLL_BAR_SHOW_BUTTONS:
|
||||||
JScrollBar hsb = scrollpane.getHorizontalScrollBar();
|
JScrollBar vsb = scrollpane.getVerticalScrollBar();
|
||||||
if( vsb != null ) {
|
JScrollBar hsb = scrollpane.getHorizontalScrollBar();
|
||||||
vsb.revalidate();
|
if( vsb != null ) {
|
||||||
vsb.repaint();
|
vsb.revalidate();
|
||||||
}
|
vsb.repaint();
|
||||||
if( hsb != null ) {
|
}
|
||||||
hsb.revalidate();
|
if( hsb != null ) {
|
||||||
hsb.repaint();
|
hsb.revalidate();
|
||||||
}
|
hsb.repaint();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ScrollPaneConstants.LOWER_LEFT_CORNER:
|
||||||
|
case ScrollPaneConstants.LOWER_RIGHT_CORNER:
|
||||||
|
case ScrollPaneConstants.UPPER_LEFT_CORNER:
|
||||||
|
case ScrollPaneConstants.UPPER_RIGHT_CORNER:
|
||||||
|
// remove border from buttons added to corners
|
||||||
|
Object corner = e.getNewValue();
|
||||||
|
if( corner instanceof JButton &&
|
||||||
|
((JButton)corner).getBorder() instanceof FlatButtonBorder &&
|
||||||
|
scrollpane.getViewport() != null &&
|
||||||
|
scrollpane.getViewport().getView() instanceof JTable )
|
||||||
|
{
|
||||||
|
((JButton)corner).setBorder( BorderFactory.createEmptyBorder() );
|
||||||
|
((JButton)corner).setFocusable( false );
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -201,7 +201,7 @@ public class FlatSliderUI
|
|||||||
}
|
}
|
||||||
|
|
||||||
if( coloredTrack != null ) {
|
if( coloredTrack != null ) {
|
||||||
FlatUIUtils.setColor( g, slider.hasFocus() ? focusColor : (hover ? hoverColor : thumbColor), thumbColor );
|
g.setColor( FlatUIUtils.deriveColor( FlatUIUtils.isPermanentFocusOwner( slider ) ? focusColor : (hover ? hoverColor : thumbColor), thumbColor ) );
|
||||||
((Graphics2D)g).fill( coloredTrack );
|
((Graphics2D)g).fill( coloredTrack );
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -211,10 +211,10 @@ public class FlatSliderUI
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void paintThumb( Graphics g ) {
|
public void paintThumb( Graphics g ) {
|
||||||
FlatUIUtils.setColor( g, slider.isEnabled()
|
g.setColor( FlatUIUtils.deriveColor( slider.isEnabled()
|
||||||
? (slider.hasFocus() ? focusColor : (hover ? hoverColor : thumbColor))
|
? (FlatUIUtils.isPermanentFocusOwner( slider ) ? focusColor : (hover ? hoverColor : thumbColor))
|
||||||
: disabledForeground,
|
: disabledForeground,
|
||||||
thumbColor );
|
thumbColor ) );
|
||||||
|
|
||||||
if( isRoundThumb() )
|
if( isRoundThumb() )
|
||||||
g.fillOval( thumbRect.x, thumbRect.y, thumbRect.width, thumbRect.height );
|
g.fillOval( thumbRect.x, thumbRect.y, thumbRect.width, thumbRect.height );
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ import javax.swing.SwingConstants;
|
|||||||
import javax.swing.UIManager;
|
import javax.swing.UIManager;
|
||||||
import javax.swing.plaf.ComponentUI;
|
import javax.swing.plaf.ComponentUI;
|
||||||
import javax.swing.plaf.basic.BasicSpinnerUI;
|
import javax.swing.plaf.basic.BasicSpinnerUI;
|
||||||
|
import com.formdev.flatlaf.FlatClientProperties;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides the Flat LaF UI delegate for {@link javax.swing.JSpinner}.
|
* Provides the Flat LaF UI delegate for {@link javax.swing.JSpinner}.
|
||||||
@@ -56,8 +57,6 @@ import javax.swing.plaf.basic.BasicSpinnerUI;
|
|||||||
*
|
*
|
||||||
* <!-- FlatSpinnerUI -->
|
* <!-- FlatSpinnerUI -->
|
||||||
*
|
*
|
||||||
* @uiDefault Component.focusWidth int
|
|
||||||
* @uiDefault Component.arc int
|
|
||||||
* @uiDefault Component.minimumWidth int
|
* @uiDefault Component.minimumWidth int
|
||||||
* @uiDefault Component.arrowType String triangle (default) or chevron
|
* @uiDefault Component.arrowType String triangle (default) or chevron
|
||||||
* @uiDefault Component.isIntelliJTheme boolean
|
* @uiDefault Component.isIntelliJTheme boolean
|
||||||
@@ -78,8 +77,6 @@ public class FlatSpinnerUI
|
|||||||
{
|
{
|
||||||
private Handler handler;
|
private Handler handler;
|
||||||
|
|
||||||
protected int focusWidth;
|
|
||||||
protected int arc;
|
|
||||||
protected int minimumWidth;
|
protected int minimumWidth;
|
||||||
protected String arrowType;
|
protected String arrowType;
|
||||||
protected boolean isIntelliJTheme;
|
protected boolean isIntelliJTheme;
|
||||||
@@ -103,8 +100,6 @@ public class FlatSpinnerUI
|
|||||||
|
|
||||||
LookAndFeel.installProperty( spinner, "opaque", false );
|
LookAndFeel.installProperty( spinner, "opaque", false );
|
||||||
|
|
||||||
focusWidth = UIManager.getInt( "Component.focusWidth" );
|
|
||||||
arc = UIManager.getInt( "Component.arc" );
|
|
||||||
minimumWidth = UIManager.getInt( "Component.minimumWidth" );
|
minimumWidth = UIManager.getInt( "Component.minimumWidth" );
|
||||||
arrowType = UIManager.getString( "Component.arrowType" );
|
arrowType = UIManager.getString( "Component.arrowType" );
|
||||||
isIntelliJTheme = UIManager.getBoolean( "Component.isIntelliJTheme" );
|
isIntelliJTheme = UIManager.getBoolean( "Component.isIntelliJTheme" );
|
||||||
@@ -121,7 +116,7 @@ public class FlatSpinnerUI
|
|||||||
// scale
|
// scale
|
||||||
padding = scale( padding );
|
padding = scale( padding );
|
||||||
|
|
||||||
MigLayoutVisualPadding.install( spinner, focusWidth );
|
MigLayoutVisualPadding.install( spinner );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -146,6 +141,7 @@ public class FlatSpinnerUI
|
|||||||
super.installListeners();
|
super.installListeners();
|
||||||
|
|
||||||
addEditorFocusListener( spinner.getEditor() );
|
addEditorFocusListener( spinner.getEditor() );
|
||||||
|
spinner.addFocusListener( getHandler() );
|
||||||
spinner.addPropertyChangeListener( getHandler() );
|
spinner.addPropertyChangeListener( getHandler() );
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -154,6 +150,7 @@ public class FlatSpinnerUI
|
|||||||
super.uninstallListeners();
|
super.uninstallListeners();
|
||||||
|
|
||||||
removeEditorFocusListener( spinner.getEditor() );
|
removeEditorFocusListener( spinner.getEditor() );
|
||||||
|
spinner.removeFocusListener( getHandler() );
|
||||||
spinner.removePropertyChangeListener( getHandler() );
|
spinner.removePropertyChangeListener( getHandler() );
|
||||||
|
|
||||||
handler = null;
|
handler = null;
|
||||||
@@ -211,7 +208,7 @@ public class FlatSpinnerUI
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private JTextField getEditorTextField( JComponent editor ) {
|
private static JTextField getEditorTextField( JComponent editor ) {
|
||||||
return editor instanceof JSpinner.DefaultEditor
|
return editor instanceof JSpinner.DefaultEditor
|
||||||
? ((JSpinner.DefaultEditor)editor).getTextField()
|
? ((JSpinner.DefaultEditor)editor).getTextField()
|
||||||
: null;
|
: null;
|
||||||
@@ -246,8 +243,11 @@ public class FlatSpinnerUI
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void update( Graphics g, JComponent c ) {
|
public void update( Graphics g, JComponent c ) {
|
||||||
|
float focusWidth = FlatUIUtils.getBorderFocusWidth( c );
|
||||||
|
float arc = FlatUIUtils.getBorderArc( c );
|
||||||
|
|
||||||
// fill background if opaque to avoid garbage if user sets opaque to true
|
// fill background if opaque to avoid garbage if user sets opaque to true
|
||||||
if( c.isOpaque() && (focusWidth > 0 || arc != 0) )
|
if( c.isOpaque() && (focusWidth > 0 || arc > 0) )
|
||||||
FlatUIUtils.paintParentBackground( g, c );
|
FlatUIUtils.paintParentBackground( g, c );
|
||||||
|
|
||||||
Graphics2D g2 = (Graphics2D) g;
|
Graphics2D g2 = (Graphics2D) g;
|
||||||
@@ -255,8 +255,6 @@ public class FlatSpinnerUI
|
|||||||
|
|
||||||
int width = c.getWidth();
|
int width = c.getWidth();
|
||||||
int height = c.getHeight();
|
int height = c.getHeight();
|
||||||
float focusWidth = (c.getBorder() instanceof FlatBorder) ? scale( (float) this.focusWidth ) : 0;
|
|
||||||
float arc = (c.getBorder() instanceof FlatRoundBorder) ? scale( (float) this.arc ) : 0;
|
|
||||||
Component nextButton = getHandler().nextButton;
|
Component nextButton = getHandler().nextButton;
|
||||||
int arrowX = nextButton.getX();
|
int arrowX = nextButton.getX();
|
||||||
int arrowWidth = nextButton.getWidth();
|
int arrowWidth = nextButton.getWidth();
|
||||||
@@ -326,9 +324,11 @@ public class FlatSpinnerUI
|
|||||||
Dimension editorSize = (editor != null) ? editor.getPreferredSize() : new Dimension( 0, 0 );
|
Dimension editorSize = (editor != null) ? editor.getPreferredSize() : new Dimension( 0, 0 );
|
||||||
|
|
||||||
// the arrows width is the same as the inner height so that the arrows area is square
|
// the arrows width is the same as the inner height so that the arrows area is square
|
||||||
|
int minimumWidth = FlatUIUtils.minimumWidth( spinner, FlatSpinnerUI.this.minimumWidth );
|
||||||
int innerHeight = editorSize.height + padding.top + padding.bottom;
|
int innerHeight = editorSize.height + padding.top + padding.bottom;
|
||||||
|
float focusWidth = FlatUIUtils.getBorderFocusWidth( spinner );
|
||||||
return new Dimension(
|
return new Dimension(
|
||||||
Math.max( insets.left + insets.right + editorSize.width + padding.left + padding.right + innerHeight, scale( minimumWidth + (focusWidth * 2) ) ),
|
Math.max( insets.left + insets.right + editorSize.width + padding.left + padding.right + innerHeight, scale( minimumWidth ) + Math.round( focusWidth * 2 ) ),
|
||||||
insets.top + insets.bottom + innerHeight );
|
insets.top + insets.bottom + innerHeight );
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -367,12 +367,14 @@ public class FlatSpinnerUI
|
|||||||
if( editor != null )
|
if( editor != null )
|
||||||
editor.setBounds( FlatUIUtils.subtractInsets( editorRect, padding ) );
|
editor.setBounds( FlatUIUtils.subtractInsets( editorRect, padding ) );
|
||||||
|
|
||||||
int nextHeight = Math.round( buttonsRect.height / 2f );
|
int nextHeight = (buttonsRect.height / 2) + (buttonsRect.height % 2); // round up
|
||||||
if( nextButton != null )
|
if( nextButton != null )
|
||||||
nextButton.setBounds( buttonsRect.x, buttonsRect.y, buttonsRect.width, nextHeight );
|
nextButton.setBounds( buttonsRect.x, buttonsRect.y, buttonsRect.width, nextHeight );
|
||||||
if( previousButton != null ) {
|
if( previousButton != null ) {
|
||||||
previousButton.setBounds( buttonsRect.x, buttonsRect.y + nextHeight,
|
// for precise layout of arrows, the "previous" button has the same height
|
||||||
buttonsRect.width, buttonsRect.height - nextHeight );
|
// as the "next" button and may overlap on uneven buttonsRect.height
|
||||||
|
int previousY = buttonsRect.y + buttonsRect.height - nextHeight;
|
||||||
|
previousButton.setBounds( buttonsRect.x, previousY, buttonsRect.width, nextHeight );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -381,6 +383,13 @@ public class FlatSpinnerUI
|
|||||||
@Override
|
@Override
|
||||||
public void focusGained( FocusEvent e ) {
|
public void focusGained( FocusEvent e ) {
|
||||||
spinner.repaint();
|
spinner.repaint();
|
||||||
|
|
||||||
|
// if spinner gained focus, transfer it to the editor text field
|
||||||
|
if( e.getComponent() == spinner ) {
|
||||||
|
JTextField textField = getEditorTextField( spinner.getEditor() );
|
||||||
|
if( textField != null )
|
||||||
|
textField.requestFocusInWindow();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -397,6 +406,14 @@ public class FlatSpinnerUI
|
|||||||
case "enabled":
|
case "enabled":
|
||||||
updateEditorColors();
|
updateEditorColors();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case FlatClientProperties.COMPONENT_ROUND_RECT:
|
||||||
|
spinner.repaint();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FlatClientProperties.MINIMUM_WIDTH:
|
||||||
|
spinner.revalidate();
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,15 +26,21 @@ import java.awt.FontMetrics;
|
|||||||
import java.awt.Graphics;
|
import java.awt.Graphics;
|
||||||
import java.awt.Graphics2D;
|
import java.awt.Graphics2D;
|
||||||
import java.awt.Insets;
|
import java.awt.Insets;
|
||||||
|
import java.awt.KeyboardFocusManager;
|
||||||
import java.awt.Rectangle;
|
import java.awt.Rectangle;
|
||||||
import java.awt.Shape;
|
import java.awt.Shape;
|
||||||
|
import java.awt.event.InputEvent;
|
||||||
|
import java.awt.event.KeyEvent;
|
||||||
import java.awt.geom.Path2D;
|
import java.awt.geom.Path2D;
|
||||||
import java.awt.geom.Rectangle2D;
|
import java.awt.geom.Rectangle2D;
|
||||||
import java.beans.PropertyChangeEvent;
|
import java.beans.PropertyChangeEvent;
|
||||||
import java.beans.PropertyChangeListener;
|
import java.beans.PropertyChangeListener;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Set;
|
||||||
import javax.swing.JButton;
|
import javax.swing.JButton;
|
||||||
import javax.swing.JComponent;
|
import javax.swing.JComponent;
|
||||||
import javax.swing.JTabbedPane;
|
import javax.swing.JTabbedPane;
|
||||||
|
import javax.swing.KeyStroke;
|
||||||
import javax.swing.UIManager;
|
import javax.swing.UIManager;
|
||||||
import javax.swing.plaf.ComponentUI;
|
import javax.swing.plaf.ComponentUI;
|
||||||
import javax.swing.plaf.UIResource;
|
import javax.swing.plaf.UIResource;
|
||||||
@@ -46,13 +52,29 @@ import com.formdev.flatlaf.util.UIScale;
|
|||||||
/**
|
/**
|
||||||
* Provides the Flat LaF UI delegate for {@link javax.swing.JTabbedPane}.
|
* Provides the Flat LaF UI delegate for {@link javax.swing.JTabbedPane}.
|
||||||
*
|
*
|
||||||
|
* @clientProperty JTabbedPane.showTabSeparators boolean
|
||||||
* @clientProperty JTabbedPane.hasFullBorder boolean
|
* @clientProperty JTabbedPane.hasFullBorder boolean
|
||||||
*
|
*
|
||||||
* @uiDefault Component.arrowType String triangle (default) or chevron
|
* <!-- BasicTabbedPaneUI -->
|
||||||
|
*
|
||||||
* @uiDefault TabbedPane.font Font
|
* @uiDefault TabbedPane.font Font
|
||||||
* @uiDefault TabbedPane.background Color
|
* @uiDefault TabbedPane.background Color
|
||||||
* @uiDefault TabbedPane.foreground Color
|
* @uiDefault TabbedPane.foreground Color
|
||||||
* @uiDefault TabbedPane.shadow Color used for scroll arrows and cropped line
|
* @uiDefault TabbedPane.shadow Color used for scroll arrows and cropped line
|
||||||
|
* @uiDefault TabbedPane.textIconGap int
|
||||||
|
* @uiDefault TabbedPane.tabInsets Insets
|
||||||
|
* @uiDefault TabbedPane.selectedTabPadInsets Insets
|
||||||
|
* @uiDefault TabbedPane.tabAreaInsets Insets
|
||||||
|
* @uiDefault TabbedPane.tabsOverlapBorder boolean
|
||||||
|
* @uiDefault TabbedPane.tabRunOverlay int
|
||||||
|
* @uiDefault TabbedPane.tabsOpaque boolean
|
||||||
|
* @uiDefault TabbedPane.contentOpaque boolean unused
|
||||||
|
* @uiDefault TabbedPane.opaque boolean
|
||||||
|
* @uiDefault TabbedPane.selectionFollowsFocus boolean default is true
|
||||||
|
*
|
||||||
|
* <!-- FlatTabbedPaneUI -->
|
||||||
|
*
|
||||||
|
* @uiDefault Component.arrowType String triangle (default) or chevron
|
||||||
* @uiDefault TabbedPane.disabledForeground Color
|
* @uiDefault TabbedPane.disabledForeground Color
|
||||||
* @uiDefault TabbedPane.selectedBackground Color optional
|
* @uiDefault TabbedPane.selectedBackground Color optional
|
||||||
* @uiDefault TabbedPane.selectedForeground Color
|
* @uiDefault TabbedPane.selectedForeground Color
|
||||||
@@ -62,13 +84,11 @@ import com.formdev.flatlaf.util.UIScale;
|
|||||||
* @uiDefault TabbedPane.focusColor Color
|
* @uiDefault TabbedPane.focusColor Color
|
||||||
* @uiDefault TabbedPane.tabSeparatorColor Color optional; defaults to TabbedPane.contentAreaColor
|
* @uiDefault TabbedPane.tabSeparatorColor Color optional; defaults to TabbedPane.contentAreaColor
|
||||||
* @uiDefault TabbedPane.contentAreaColor Color
|
* @uiDefault TabbedPane.contentAreaColor Color
|
||||||
* @uiDefault TabbedPane.textIconGap int
|
|
||||||
* @uiDefault TabbedPane.tabInsets Insets
|
|
||||||
* @uiDefault TabbedPane.tabAreaInsets Insets
|
|
||||||
* @uiDefault TabbedPane.tabHeight int
|
* @uiDefault TabbedPane.tabHeight int
|
||||||
* @uiDefault TabbedPane.tabSelectionHeight int
|
* @uiDefault TabbedPane.tabSelectionHeight int
|
||||||
* @uiDefault TabbedPane.contentSeparatorHeight int
|
* @uiDefault TabbedPane.contentSeparatorHeight int
|
||||||
* @uiDefault TabbedPane.showTabSeparators boolean
|
* @uiDefault TabbedPane.showTabSeparators boolean
|
||||||
|
* @uiDefault TabbedPane.tabSeparatorsFullHeight boolean
|
||||||
* @uiDefault TabbedPane.hasFullBorder boolean
|
* @uiDefault TabbedPane.hasFullBorder boolean
|
||||||
*
|
*
|
||||||
* @author Karl Tauber
|
* @author Karl Tauber
|
||||||
@@ -76,6 +96,9 @@ import com.formdev.flatlaf.util.UIScale;
|
|||||||
public class FlatTabbedPaneUI
|
public class FlatTabbedPaneUI
|
||||||
extends BasicTabbedPaneUI
|
extends BasicTabbedPaneUI
|
||||||
{
|
{
|
||||||
|
private static Set<KeyStroke> focusForwardTraversalKeys;
|
||||||
|
private static Set<KeyStroke> focusBackwardTraversalKeys;
|
||||||
|
|
||||||
protected Color disabledForeground;
|
protected Color disabledForeground;
|
||||||
protected Color selectedBackground;
|
protected Color selectedBackground;
|
||||||
protected Color selectedForeground;
|
protected Color selectedForeground;
|
||||||
@@ -90,6 +113,7 @@ public class FlatTabbedPaneUI
|
|||||||
protected int tabSelectionHeight;
|
protected int tabSelectionHeight;
|
||||||
protected int contentSeparatorHeight;
|
protected int contentSeparatorHeight;
|
||||||
protected boolean showTabSeparators;
|
protected boolean showTabSeparators;
|
||||||
|
protected boolean tabSeparatorsFullHeight;
|
||||||
protected boolean hasFullBorder;
|
protected boolean hasFullBorder;
|
||||||
protected boolean tabsOverlapBorder;
|
protected boolean tabsOverlapBorder;
|
||||||
|
|
||||||
@@ -115,6 +139,7 @@ public class FlatTabbedPaneUI
|
|||||||
tabSelectionHeight = UIManager.getInt( "TabbedPane.tabSelectionHeight" );
|
tabSelectionHeight = UIManager.getInt( "TabbedPane.tabSelectionHeight" );
|
||||||
contentSeparatorHeight = UIManager.getInt( "TabbedPane.contentSeparatorHeight" );
|
contentSeparatorHeight = UIManager.getInt( "TabbedPane.contentSeparatorHeight" );
|
||||||
showTabSeparators = UIManager.getBoolean( "TabbedPane.showTabSeparators" );
|
showTabSeparators = UIManager.getBoolean( "TabbedPane.showTabSeparators" );
|
||||||
|
tabSeparatorsFullHeight = UIManager.getBoolean( "TabbedPane.tabSeparatorsFullHeight" );
|
||||||
hasFullBorder = UIManager.getBoolean( "TabbedPane.hasFullBorder" );
|
hasFullBorder = UIManager.getBoolean( "TabbedPane.hasFullBorder" );
|
||||||
tabsOverlapBorder = UIManager.getBoolean( "TabbedPane.tabsOverlapBorder" );
|
tabsOverlapBorder = UIManager.getBoolean( "TabbedPane.tabsOverlapBorder" );
|
||||||
|
|
||||||
@@ -126,11 +151,27 @@ public class FlatTabbedPaneUI
|
|||||||
tabHeight = scale( tabHeight );
|
tabHeight = scale( tabHeight );
|
||||||
tabSelectionHeight = scale( tabSelectionHeight );
|
tabSelectionHeight = scale( tabSelectionHeight );
|
||||||
|
|
||||||
|
// replace focus forward/backward traversal keys with TAB/Shift+TAB because
|
||||||
|
// the default also includes Ctrl+TAB/Ctrl+Shift+TAB, which we need to switch tabs
|
||||||
|
if( focusForwardTraversalKeys == null ) {
|
||||||
|
focusForwardTraversalKeys = Collections.singleton( KeyStroke.getKeyStroke( KeyEvent.VK_TAB, 0 ) );
|
||||||
|
focusBackwardTraversalKeys = Collections.singleton( KeyStroke.getKeyStroke( KeyEvent.VK_TAB, InputEvent.SHIFT_MASK ) );
|
||||||
|
}
|
||||||
|
// Ideally we should use `LookAndFeel.installProperty( tabPane, "focusTraversalKeysForward", keys )` here
|
||||||
|
// instead of `tabPane.setFocusTraversalKeys()`, but WindowsTabbedPaneUI also uses later method
|
||||||
|
// and switching from Windows LaF to FlatLaf would not replace the keys and Ctrl+TAB would not work.
|
||||||
|
tabPane.setFocusTraversalKeys( KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS, focusForwardTraversalKeys );
|
||||||
|
tabPane.setFocusTraversalKeys( KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS, focusBackwardTraversalKeys );
|
||||||
|
|
||||||
MigLayoutVisualPadding.install( tabPane, null );
|
MigLayoutVisualPadding.install( tabPane, null );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void uninstallDefaults() {
|
protected void uninstallDefaults() {
|
||||||
|
// restore focus forward/backward traversal keys
|
||||||
|
tabPane.setFocusTraversalKeys( KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS, null );
|
||||||
|
tabPane.setFocusTraversalKeys( KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS, null );
|
||||||
|
|
||||||
super.uninstallDefaults();
|
super.uninstallDefaults();
|
||||||
|
|
||||||
disabledForeground = null;
|
disabledForeground = null;
|
||||||
@@ -156,6 +197,7 @@ public class FlatTabbedPaneUI
|
|||||||
switch( e.getPropertyName() ) {
|
switch( e.getPropertyName() ) {
|
||||||
case TABBED_PANE_SHOW_TAB_SEPARATORS:
|
case TABBED_PANE_SHOW_TAB_SEPARATORS:
|
||||||
case TABBED_PANE_HAS_FULL_BORDER:
|
case TABBED_PANE_HAS_FULL_BORDER:
|
||||||
|
case TABBED_PANE_TAB_HEIGHT:
|
||||||
tabPane.revalidate();
|
tabPane.revalidate();
|
||||||
tabPane.repaint();
|
tabPane.repaint();
|
||||||
break;
|
break;
|
||||||
@@ -168,7 +210,7 @@ public class FlatTabbedPaneUI
|
|||||||
protected JButton createScrollButton( int direction ) {
|
protected JButton createScrollButton( int direction ) {
|
||||||
// this method is invoked before installDefaults(), so we can not use color fields here
|
// this method is invoked before installDefaults(), so we can not use color fields here
|
||||||
return new FlatArrowButton( direction, UIManager.getString( "Component.arrowType" ),
|
return new FlatArrowButton( direction, UIManager.getString( "Component.arrowType" ),
|
||||||
UIManager.getColor( "TabbedPane.shadow" ),
|
UIManager.getColor( "TabbedPane.foreground" ),
|
||||||
UIManager.getColor( "TabbedPane.disabledForeground" ), null,
|
UIManager.getColor( "TabbedPane.disabledForeground" ), null,
|
||||||
UIManager.getColor( "TabbedPane.hoverColor" ) );
|
UIManager.getColor( "TabbedPane.hoverColor" ) );
|
||||||
}
|
}
|
||||||
@@ -202,6 +244,7 @@ public class FlatTabbedPaneUI
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected int calculateTabHeight( int tabPlacement, int tabIndex, int fontHeight ) {
|
protected int calculateTabHeight( int tabPlacement, int tabIndex, int fontHeight ) {
|
||||||
|
int tabHeight = clientPropertyInt( tabPane, TABBED_PANE_TAB_HEIGHT, this.tabHeight );
|
||||||
return Math.max( tabHeight, super.calculateTabHeight( tabPlacement, tabIndex, fontHeight ) - 2 /* was added by superclass */ );
|
return Math.max( tabHeight, super.calculateTabHeight( tabPlacement, tabIndex, fontHeight ) - 2 /* was added by superclass */ );
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -275,7 +318,7 @@ public class FlatTabbedPaneUI
|
|||||||
boolean enabled = tabPane.isEnabled();
|
boolean enabled = tabPane.isEnabled();
|
||||||
g.setColor( enabled && tabPane.isEnabledAt( tabIndex ) && getRolloverTab() == tabIndex
|
g.setColor( enabled && tabPane.isEnabledAt( tabIndex ) && getRolloverTab() == tabIndex
|
||||||
? hoverColor
|
? hoverColor
|
||||||
: (enabled && isSelected && tabPane.hasFocus()
|
: (enabled && isSelected && FlatUIUtils.isPermanentFocusOwner( tabPane )
|
||||||
? focusColor
|
? focusColor
|
||||||
: (selectedBackground != null && enabled && isSelected
|
: (selectedBackground != null && enabled && isSelected
|
||||||
? selectedBackground
|
? selectedBackground
|
||||||
@@ -292,7 +335,7 @@ public class FlatTabbedPaneUI
|
|||||||
!isLastInRun( tabIndex ) )
|
!isLastInRun( tabIndex ) )
|
||||||
{
|
{
|
||||||
float sepWidth = UIScale.scale( 1f );
|
float sepWidth = UIScale.scale( 1f );
|
||||||
float offset = UIScale.scale( 5f );
|
float offset = tabSeparatorsFullHeight ? 0 : UIScale.scale( 5f );
|
||||||
|
|
||||||
g.setColor( (tabSeparatorColor != null) ? tabSeparatorColor : contentAreaColor );
|
g.setColor( (tabSeparatorColor != null) ? tabSeparatorColor : contentAreaColor );
|
||||||
if( tabPlacement == LEFT || tabPlacement == RIGHT ) {
|
if( tabPlacement == LEFT || tabPlacement == RIGHT ) {
|
||||||
|
|||||||
@@ -0,0 +1,115 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2019 FormDev Software GmbH
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.formdev.flatlaf.ui;
|
||||||
|
|
||||||
|
import java.awt.Component;
|
||||||
|
import java.awt.Graphics;
|
||||||
|
import javax.swing.JTable;
|
||||||
|
import javax.swing.SwingUtilities;
|
||||||
|
import javax.swing.UIManager;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cell border for {@link javax.swing.table.DefaultTableCellRenderer}
|
||||||
|
* (used by {@link javax.swing.JTable}).
|
||||||
|
* <p>
|
||||||
|
* Uses separate cell margins from UI defaults to allow easy customizing.
|
||||||
|
*
|
||||||
|
* @author Karl Tauber
|
||||||
|
*/
|
||||||
|
public class FlatTableCellBorder
|
||||||
|
extends FlatLineBorder
|
||||||
|
{
|
||||||
|
final boolean showCellFocusIndicator = UIManager.getBoolean( "Table.showCellFocusIndicator" );
|
||||||
|
|
||||||
|
protected FlatTableCellBorder() {
|
||||||
|
super( UIManager.getInsets( "Table.cellMargins" ), UIManager.getColor( "Table.cellFocusColor" ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
//---- class Default ------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Border for unselected cell that uses margins, but does not paint focus indicator border.
|
||||||
|
*/
|
||||||
|
public static class Default
|
||||||
|
extends FlatTableCellBorder
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void paintBorder( Component c, Graphics g, int x, int y, int width, int height ) {
|
||||||
|
// do not paint focus indicator border
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//---- class Focused ------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Border for focused unselected cell that uses margins and paints focus indicator border.
|
||||||
|
*/
|
||||||
|
public static class Focused
|
||||||
|
extends FlatTableCellBorder
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
//---- class Selected -----------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Border for selected cell that uses margins and paints focus indicator border
|
||||||
|
* if enabled (Table.showCellFocusIndicator=true) or at least one selected cell is editable.
|
||||||
|
*/
|
||||||
|
public static class Selected
|
||||||
|
extends FlatTableCellBorder
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void paintBorder( Component c, Graphics g, int x, int y, int width, int height ) {
|
||||||
|
if( !showCellFocusIndicator ) {
|
||||||
|
JTable table = (JTable) SwingUtilities.getAncestorOfClass( JTable.class, c );
|
||||||
|
if( table != null && !isSelectionEditable( table ) )
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
super.paintBorder( c, g, x, y, width, height );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether at least one selected cell is editable.
|
||||||
|
*/
|
||||||
|
private boolean isSelectionEditable( JTable table ) {
|
||||||
|
if( table.getRowSelectionAllowed() ) {
|
||||||
|
int columnCount = table.getColumnCount();
|
||||||
|
int[] selectedRows = table.getSelectedRows();
|
||||||
|
for( int selectedRow : selectedRows ) {
|
||||||
|
for( int column = 0; column < columnCount; column++ ) {
|
||||||
|
if( table.isCellEditable( selectedRow, column ) )
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if( table.getColumnSelectionAllowed() ) {
|
||||||
|
int rowCount = table.getRowCount();
|
||||||
|
int[] selectedColumns = table.getSelectedColumns();
|
||||||
|
for( int selectedColumn : selectedColumns ) {
|
||||||
|
for( int row = 0; row < rowCount; row++ ) {
|
||||||
|
if( table.isCellEditable( row, selectedColumn ) )
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -17,18 +17,27 @@
|
|||||||
package com.formdev.flatlaf.ui;
|
package com.formdev.flatlaf.ui;
|
||||||
|
|
||||||
import java.awt.Color;
|
import java.awt.Color;
|
||||||
|
import java.awt.Component;
|
||||||
import java.awt.Container;
|
import java.awt.Container;
|
||||||
import java.awt.Dimension;
|
import java.awt.Dimension;
|
||||||
import java.awt.Graphics;
|
import java.awt.Graphics;
|
||||||
import java.awt.Graphics2D;
|
import java.awt.Graphics2D;
|
||||||
|
import java.awt.Insets;
|
||||||
import java.awt.Rectangle;
|
import java.awt.Rectangle;
|
||||||
import java.awt.geom.Rectangle2D;
|
import java.awt.geom.Rectangle2D;
|
||||||
|
import java.util.Objects;
|
||||||
|
import javax.swing.Icon;
|
||||||
import javax.swing.JComponent;
|
import javax.swing.JComponent;
|
||||||
|
import javax.swing.JLabel;
|
||||||
import javax.swing.JScrollPane;
|
import javax.swing.JScrollPane;
|
||||||
import javax.swing.JTable;
|
import javax.swing.JTable;
|
||||||
|
import javax.swing.SwingConstants;
|
||||||
import javax.swing.UIManager;
|
import javax.swing.UIManager;
|
||||||
|
import javax.swing.border.Border;
|
||||||
import javax.swing.plaf.ComponentUI;
|
import javax.swing.plaf.ComponentUI;
|
||||||
|
import javax.swing.plaf.UIResource;
|
||||||
import javax.swing.plaf.basic.BasicTableHeaderUI;
|
import javax.swing.plaf.basic.BasicTableHeaderUI;
|
||||||
|
import javax.swing.table.TableCellRenderer;
|
||||||
import javax.swing.table.TableColumn;
|
import javax.swing.table.TableColumn;
|
||||||
import javax.swing.table.TableColumnModel;
|
import javax.swing.table.TableColumnModel;
|
||||||
import com.formdev.flatlaf.util.UIScale;
|
import com.formdev.flatlaf.util.UIScale;
|
||||||
@@ -47,6 +56,7 @@ import com.formdev.flatlaf.util.UIScale;
|
|||||||
* @uiDefault TableHeader.separatorColor Color
|
* @uiDefault TableHeader.separatorColor Color
|
||||||
* @uiDefault TableHeader.bottomSeparatorColor Color
|
* @uiDefault TableHeader.bottomSeparatorColor Color
|
||||||
* @uiDefault TableHeader.height int
|
* @uiDefault TableHeader.height int
|
||||||
|
* @uiDefault TableHeader.sortIconPosition String right (default), left, top or bottom
|
||||||
*
|
*
|
||||||
* @author Karl Tauber
|
* @author Karl Tauber
|
||||||
*/
|
*/
|
||||||
@@ -56,6 +66,7 @@ public class FlatTableHeaderUI
|
|||||||
protected Color separatorColor;
|
protected Color separatorColor;
|
||||||
protected Color bottomSeparatorColor;
|
protected Color bottomSeparatorColor;
|
||||||
protected int height;
|
protected int height;
|
||||||
|
protected int sortIconPosition;
|
||||||
|
|
||||||
public static ComponentUI createUI( JComponent c ) {
|
public static ComponentUI createUI( JComponent c ) {
|
||||||
return new FlatTableHeaderUI();
|
return new FlatTableHeaderUI();
|
||||||
@@ -68,12 +79,33 @@ public class FlatTableHeaderUI
|
|||||||
separatorColor = UIManager.getColor( "TableHeader.separatorColor" );
|
separatorColor = UIManager.getColor( "TableHeader.separatorColor" );
|
||||||
bottomSeparatorColor = UIManager.getColor( "TableHeader.bottomSeparatorColor" );
|
bottomSeparatorColor = UIManager.getColor( "TableHeader.bottomSeparatorColor" );
|
||||||
height = UIManager.getInt( "TableHeader.height" );
|
height = UIManager.getInt( "TableHeader.height" );
|
||||||
|
switch( Objects.toString( UIManager.getString( "TableHeader.sortIconPosition" ), "right" ) ) {
|
||||||
|
default:
|
||||||
|
case "right": sortIconPosition = SwingConstants.RIGHT; break;
|
||||||
|
case "left": sortIconPosition = SwingConstants.LEFT; break;
|
||||||
|
case "top": sortIconPosition = SwingConstants.TOP; break;
|
||||||
|
case "bottom": sortIconPosition = SwingConstants.BOTTOM; break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// use own renderer if necessary
|
||||||
|
if( sortIconPosition != SwingConstants.RIGHT ) {
|
||||||
|
TableCellRenderer defaultRenderer = header.getDefaultRenderer();
|
||||||
|
if( defaultRenderer instanceof UIResource )
|
||||||
|
header.setDefaultRenderer( new FlatTableCellHeaderRenderer( defaultRenderer ) );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void uninstallDefaults() {
|
protected void uninstallDefaults() {
|
||||||
super.uninstallDefaults();
|
super.uninstallDefaults();
|
||||||
|
|
||||||
|
// restore default renderer
|
||||||
|
TableCellRenderer defaultRenderer = header.getDefaultRenderer();
|
||||||
|
if( defaultRenderer instanceof FlatTableCellHeaderRenderer ) {
|
||||||
|
((FlatTableCellHeaderRenderer)defaultRenderer).reset();
|
||||||
|
header.setDefaultRenderer( ((FlatTableCellHeaderRenderer)defaultRenderer).delegate );
|
||||||
|
}
|
||||||
|
|
||||||
separatorColor = null;
|
separatorColor = null;
|
||||||
bottomSeparatorColor = null;
|
bottomSeparatorColor = null;
|
||||||
}
|
}
|
||||||
@@ -81,10 +113,14 @@ public class FlatTableHeaderUI
|
|||||||
@Override
|
@Override
|
||||||
public void paint( Graphics g, JComponent c ) {
|
public void paint( Graphics g, JComponent c ) {
|
||||||
// do not paint borders if JTableHeader.setDefaultRenderer() was used
|
// do not paint borders if JTableHeader.setDefaultRenderer() was used
|
||||||
String rendererClassName = header.getDefaultRenderer().getClass().getName();
|
TableCellRenderer defaultRenderer = header.getDefaultRenderer();
|
||||||
boolean paintBorders =
|
boolean paintBorders = isSystemDefaultRenderer( defaultRenderer );
|
||||||
rendererClassName.equals( "sun.swing.table.DefaultTableCellHeaderRenderer" ) ||
|
if( !paintBorders && header.getColumnModel().getColumnCount() > 0 ) {
|
||||||
rendererClassName.equals( "sun.swing.FilePane$AlignableTableHeaderRenderer" );
|
// check whether the renderer delegates to the system default renderer
|
||||||
|
Component rendererComponent = defaultRenderer.getTableCellRendererComponent(
|
||||||
|
header.getTable(), "", false, false, -1, 0 );
|
||||||
|
paintBorders = isSystemDefaultRenderer( rendererComponent );
|
||||||
|
}
|
||||||
|
|
||||||
if( paintBorders )
|
if( paintBorders )
|
||||||
paintColumnBorders( g, c );
|
paintColumnBorders( g, c );
|
||||||
@@ -95,6 +131,12 @@ public class FlatTableHeaderUI
|
|||||||
paintDraggedColumnBorders( g, c );
|
paintDraggedColumnBorders( g, c );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean isSystemDefaultRenderer( Object headerRenderer ) {
|
||||||
|
String rendererClassName = headerRenderer.getClass().getName();
|
||||||
|
return rendererClassName.equals( "sun.swing.table.DefaultTableCellHeaderRenderer" ) ||
|
||||||
|
rendererClassName.equals( "sun.swing.FilePane$AlignableTableHeaderRenderer" );
|
||||||
|
}
|
||||||
|
|
||||||
private void paintColumnBorders( Graphics g, JComponent c ) {
|
private void paintColumnBorders( Graphics g, JComponent c ) {
|
||||||
int width = c.getWidth();
|
int width = c.getWidth();
|
||||||
int height = c.getHeight();
|
int height = c.getHeight();
|
||||||
@@ -203,4 +245,83 @@ public class FlatTableHeaderUI
|
|||||||
parent = parent.getParent();
|
parent = parent.getParent();
|
||||||
return (parent instanceof JScrollPane) ? (JScrollPane) parent : null;
|
return (parent instanceof JScrollPane) ? (JScrollPane) parent : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//---- class FlatTableCellHeaderRenderer ----------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A delegating header renderer that is only used to paint sort arrows at
|
||||||
|
* top, bottom or left position.
|
||||||
|
*/
|
||||||
|
private class FlatTableCellHeaderRenderer
|
||||||
|
implements TableCellRenderer, Border, UIResource
|
||||||
|
{
|
||||||
|
private final TableCellRenderer delegate;
|
||||||
|
|
||||||
|
private int oldHorizontalTextPosition = -1;
|
||||||
|
private Border origBorder;
|
||||||
|
private Icon sortIcon;
|
||||||
|
|
||||||
|
FlatTableCellHeaderRenderer( TableCellRenderer delegate ) {
|
||||||
|
this.delegate = delegate;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Component getTableCellRendererComponent( JTable table, Object value, boolean isSelected,
|
||||||
|
boolean hasFocus, int row, int column )
|
||||||
|
{
|
||||||
|
Component c = delegate.getTableCellRendererComponent( table, value, isSelected, hasFocus, row, column );
|
||||||
|
if( !(c instanceof JLabel) )
|
||||||
|
return c;
|
||||||
|
|
||||||
|
JLabel l = (JLabel) c;
|
||||||
|
|
||||||
|
if( sortIconPosition == SwingConstants.LEFT ) {
|
||||||
|
if( oldHorizontalTextPosition < 0 )
|
||||||
|
oldHorizontalTextPosition = l.getHorizontalTextPosition();
|
||||||
|
l.setHorizontalTextPosition( SwingConstants.RIGHT );
|
||||||
|
} else {
|
||||||
|
// top or bottom
|
||||||
|
sortIcon = l.getIcon();
|
||||||
|
origBorder = l.getBorder();
|
||||||
|
l.setIcon( null );
|
||||||
|
l.setBorder( this );
|
||||||
|
}
|
||||||
|
|
||||||
|
return l;
|
||||||
|
}
|
||||||
|
|
||||||
|
void reset() {
|
||||||
|
if( sortIconPosition == SwingConstants.LEFT && oldHorizontalTextPosition >= 0 ) {
|
||||||
|
Component c = getTableCellRendererComponent( header.getTable(), "", false, false, -1, 0 );
|
||||||
|
if( c instanceof JLabel && ((JLabel)c).getHorizontalTextPosition() == SwingConstants.RIGHT )
|
||||||
|
((JLabel)c).setHorizontalTextPosition( oldHorizontalTextPosition );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void paintBorder( Component c, Graphics g, int x, int y, int width, int height ) {
|
||||||
|
if( origBorder != null )
|
||||||
|
origBorder.paintBorder( c, g, x, y, width, height );
|
||||||
|
|
||||||
|
if( sortIcon != null ) {
|
||||||
|
int xi = x + ((width - sortIcon.getIconWidth()) / 2);
|
||||||
|
int yi = (sortIconPosition == SwingConstants.TOP)
|
||||||
|
? y + UIScale.scale( 1 )
|
||||||
|
: y + height - sortIcon.getIconHeight()
|
||||||
|
- 1 // for gap
|
||||||
|
- (int) (1 * UIScale.getUserScaleFactor()); // for bottom border
|
||||||
|
sortIcon.paintIcon( c, g, xi, yi );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Insets getBorderInsets( Component c ) {
|
||||||
|
return (origBorder != null) ? origBorder.getBorderInsets( c ) : new Insets( 0, 0, 0, 0 );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isBorderOpaque() {
|
||||||
|
return (origBorder != null) ? origBorder.isBorderOpaque() : false;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,13 +17,16 @@
|
|||||||
package com.formdev.flatlaf.ui;
|
package com.formdev.flatlaf.ui;
|
||||||
|
|
||||||
import java.awt.Color;
|
import java.awt.Color;
|
||||||
|
import java.awt.Dimension;
|
||||||
import java.awt.event.FocusEvent;
|
import java.awt.event.FocusEvent;
|
||||||
import java.awt.event.FocusListener;
|
import java.awt.event.FocusListener;
|
||||||
|
import javax.swing.JCheckBox;
|
||||||
import javax.swing.JComponent;
|
import javax.swing.JComponent;
|
||||||
import javax.swing.LookAndFeel;
|
import javax.swing.LookAndFeel;
|
||||||
import javax.swing.UIManager;
|
import javax.swing.UIManager;
|
||||||
import javax.swing.plaf.ComponentUI;
|
import javax.swing.plaf.ComponentUI;
|
||||||
import javax.swing.plaf.basic.BasicTableUI;
|
import javax.swing.plaf.basic.BasicTableUI;
|
||||||
|
import javax.swing.table.TableCellRenderer;
|
||||||
import com.formdev.flatlaf.util.UIScale;
|
import com.formdev.flatlaf.util.UIScale;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -54,38 +57,89 @@ import com.formdev.flatlaf.util.UIScale;
|
|||||||
*
|
*
|
||||||
* <!-- FlatTableUI -->
|
* <!-- FlatTableUI -->
|
||||||
*
|
*
|
||||||
* @uiDefault Table.rowHeight int
|
* @uiDefault Table.rowHeight int
|
||||||
* @uiDefault Table.selectionInactiveBackground Color
|
* @uiDefault Table.showHorizontalLines boolean
|
||||||
* @uiDefault Table.selectionInactiveForeground Color
|
* @uiDefault Table.showVerticalLines boolean
|
||||||
|
* @uiDefault Table.intercellSpacing Dimension
|
||||||
|
* @uiDefault Table.selectionInactiveBackground Color
|
||||||
|
* @uiDefault Table.selectionInactiveForeground Color
|
||||||
|
*
|
||||||
|
* <!-- FlatTableCellBorder -->
|
||||||
|
*
|
||||||
|
* @uiDefault Table.cellMargins Insets
|
||||||
|
* @uiDefault Table.cellFocusColor Color
|
||||||
|
* @uiDefault Table.showCellFocusIndicator boolean
|
||||||
*
|
*
|
||||||
* @author Karl Tauber
|
* @author Karl Tauber
|
||||||
*/
|
*/
|
||||||
public class FlatTableUI
|
public class FlatTableUI
|
||||||
extends BasicTableUI
|
extends BasicTableUI
|
||||||
{
|
{
|
||||||
|
protected boolean showHorizontalLines;
|
||||||
|
protected boolean showVerticalLines;
|
||||||
|
protected Dimension intercellSpacing;
|
||||||
|
|
||||||
protected Color selectionBackground;
|
protected Color selectionBackground;
|
||||||
protected Color selectionForeground;
|
protected Color selectionForeground;
|
||||||
protected Color selectionInactiveBackground;
|
protected Color selectionInactiveBackground;
|
||||||
protected Color selectionInactiveForeground;
|
protected Color selectionInactiveForeground;
|
||||||
|
|
||||||
|
private boolean oldShowHorizontalLines;
|
||||||
|
private boolean oldShowVerticalLines;
|
||||||
|
private Dimension oldIntercellSpacing;
|
||||||
|
|
||||||
public static ComponentUI createUI( JComponent c ) {
|
public static ComponentUI createUI( JComponent c ) {
|
||||||
return new FlatTableUI();
|
return new FlatTableUI();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void installUI( JComponent c ) {
|
||||||
|
super.installUI( c );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void uninstallUI( JComponent c ) {
|
||||||
|
super.uninstallUI( c );
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void installDefaults() {
|
protected void installDefaults() {
|
||||||
super.installDefaults();
|
super.installDefaults();
|
||||||
|
|
||||||
|
showHorizontalLines = UIManager.getBoolean( "Table.showHorizontalLines" );
|
||||||
|
showVerticalLines = UIManager.getBoolean( "Table.showVerticalLines" );
|
||||||
|
intercellSpacing = UIManager.getDimension( "Table.intercellSpacing" );
|
||||||
|
|
||||||
selectionBackground = UIManager.getColor( "Table.selectionBackground" );
|
selectionBackground = UIManager.getColor( "Table.selectionBackground" );
|
||||||
selectionForeground = UIManager.getColor( "Table.selectionForeground" );
|
selectionForeground = UIManager.getColor( "Table.selectionForeground" );
|
||||||
selectionInactiveBackground = UIManager.getColor( "Table.selectionInactiveBackground" );
|
selectionInactiveBackground = UIManager.getColor( "Table.selectionInactiveBackground" );
|
||||||
selectionInactiveForeground = UIManager.getColor( "Table.selectionInactiveForeground" );
|
selectionInactiveForeground = UIManager.getColor( "Table.selectionInactiveForeground" );
|
||||||
|
|
||||||
toggleSelectionColors( table.hasFocus() );
|
toggleSelectionColors();
|
||||||
|
|
||||||
int rowHeight = FlatUIUtils.getUIInt( "Table.rowHeight", 16 );
|
int rowHeight = FlatUIUtils.getUIInt( "Table.rowHeight", 16 );
|
||||||
if( rowHeight > 0 )
|
if( rowHeight > 0 )
|
||||||
LookAndFeel.installProperty( table, "rowHeight", UIScale.scale( rowHeight ) );
|
LookAndFeel.installProperty( table, "rowHeight", UIScale.scale( rowHeight ) );
|
||||||
|
|
||||||
|
if( !showHorizontalLines ) {
|
||||||
|
oldShowHorizontalLines = table.getShowHorizontalLines();
|
||||||
|
table.setShowHorizontalLines( false );
|
||||||
|
}
|
||||||
|
if( !showVerticalLines ) {
|
||||||
|
oldShowVerticalLines = table.getShowVerticalLines();
|
||||||
|
table.setShowVerticalLines( false );
|
||||||
|
}
|
||||||
|
|
||||||
|
if( intercellSpacing != null ) {
|
||||||
|
oldIntercellSpacing = table.getIntercellSpacing();
|
||||||
|
table.setIntercellSpacing( intercellSpacing );
|
||||||
|
}
|
||||||
|
|
||||||
|
// checkbox is non-opaque in FlatLaf and therefore would not paint selection
|
||||||
|
// --> make checkbox renderer opaque (but opaque in Metal or Windows LaF)
|
||||||
|
TableCellRenderer booleanRenderer = table.getDefaultRenderer( Boolean.class );
|
||||||
|
if( booleanRenderer instanceof JCheckBox )
|
||||||
|
((JCheckBox)booleanRenderer).setOpaque( true );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -96,6 +150,16 @@ public class FlatTableUI
|
|||||||
selectionForeground = null;
|
selectionForeground = null;
|
||||||
selectionInactiveBackground = null;
|
selectionInactiveBackground = null;
|
||||||
selectionInactiveForeground = null;
|
selectionInactiveForeground = null;
|
||||||
|
|
||||||
|
// restore old show horizontal/vertical lines (if not modified)
|
||||||
|
if( !showHorizontalLines && oldShowHorizontalLines && !table.getShowHorizontalLines() )
|
||||||
|
table.setShowHorizontalLines( true );
|
||||||
|
if( !showVerticalLines && oldShowVerticalLines && !table.getShowVerticalLines() )
|
||||||
|
table.setShowVerticalLines( true );
|
||||||
|
|
||||||
|
// restore old intercell spacing (if not modified)
|
||||||
|
if( intercellSpacing != null && table.getIntercellSpacing().equals( intercellSpacing ) )
|
||||||
|
table.setIntercellSpacing( oldIntercellSpacing );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -104,13 +168,13 @@ public class FlatTableUI
|
|||||||
@Override
|
@Override
|
||||||
public void focusGained( FocusEvent e ) {
|
public void focusGained( FocusEvent e ) {
|
||||||
super.focusGained( e );
|
super.focusGained( e );
|
||||||
toggleSelectionColors( true );
|
toggleSelectionColors();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void focusLost( FocusEvent e ) {
|
public void focusLost( FocusEvent e ) {
|
||||||
super.focusLost( e );
|
super.focusLost( e );
|
||||||
toggleSelectionColors( false );
|
toggleSelectionColors();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -124,8 +188,8 @@ public class FlatTableUI
|
|||||||
* already used in applications. Then either the inactive colors are not used,
|
* already used in applications. Then either the inactive colors are not used,
|
||||||
* or the application has to be changed to extend a FlatLaf renderer.
|
* or the application has to be changed to extend a FlatLaf renderer.
|
||||||
*/
|
*/
|
||||||
private void toggleSelectionColors( boolean focused ) {
|
private void toggleSelectionColors() {
|
||||||
if( focused ) {
|
if( FlatUIUtils.isPermanentFocusOwner( table ) ) {
|
||||||
if( table.getSelectionBackground() == selectionInactiveBackground )
|
if( table.getSelectionBackground() == selectionInactiveBackground )
|
||||||
table.setSelectionBackground( selectionBackground );
|
table.setSelectionBackground( selectionBackground );
|
||||||
if( table.getSelectionForeground() == selectionInactiveForeground )
|
if( table.getSelectionForeground() == selectionInactiveForeground )
|
||||||
|
|||||||
@@ -16,10 +16,10 @@
|
|||||||
|
|
||||||
package com.formdev.flatlaf.ui;
|
package com.formdev.flatlaf.ui;
|
||||||
|
|
||||||
import static com.formdev.flatlaf.util.UIScale.scale;
|
|
||||||
import java.awt.Color;
|
import java.awt.Color;
|
||||||
import java.awt.Dimension;
|
import java.awt.Dimension;
|
||||||
import java.awt.Graphics;
|
import java.awt.Graphics;
|
||||||
|
import java.beans.PropertyChangeEvent;
|
||||||
import javax.swing.JComponent;
|
import javax.swing.JComponent;
|
||||||
import javax.swing.JTextArea;
|
import javax.swing.JTextArea;
|
||||||
import javax.swing.UIManager;
|
import javax.swing.UIManager;
|
||||||
@@ -83,6 +83,12 @@ public class FlatTextAreaUI
|
|||||||
inactiveBackground = null;
|
inactiveBackground = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void propertyChange( PropertyChangeEvent e ) {
|
||||||
|
super.propertyChange( e );
|
||||||
|
FlatEditorPaneUI.propertyChange( getComponent(), e );
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void paintBackground( Graphics g ) {
|
protected void paintBackground( Graphics g ) {
|
||||||
JTextComponent c = getComponent();
|
JTextComponent c = getComponent();
|
||||||
@@ -100,24 +106,19 @@ public class FlatTextAreaUI
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Dimension getPreferredSize( JComponent c ) {
|
public Dimension getPreferredSize( JComponent c ) {
|
||||||
return applyMinimumWidth( super.getPreferredSize( c ), c );
|
return applyMinimumWidth( c, super.getPreferredSize( c ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Dimension getMinimumSize( JComponent c ) {
|
public Dimension getMinimumSize( JComponent c ) {
|
||||||
return applyMinimumWidth( super.getMinimumSize( c ), c );
|
return applyMinimumWidth( c, super.getMinimumSize( c ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
private Dimension applyMinimumWidth( Dimension size, JComponent c ) {
|
private Dimension applyMinimumWidth( JComponent c, Dimension size ) {
|
||||||
// do not apply minimum width if JTextArea.columns is set
|
// do not apply minimum width if JTextArea.columns is set
|
||||||
if( c instanceof JTextArea && ((JTextArea)c).getColumns() > 0 )
|
if( c instanceof JTextArea && ((JTextArea)c).getColumns() > 0 )
|
||||||
return size;
|
return size;
|
||||||
|
|
||||||
// Assume that text area is in a scroll pane (that displays the border)
|
return FlatEditorPaneUI.applyMinimumWidth( c, size, minimumWidth );
|
||||||
// and subtract 1px border line width.
|
|
||||||
// Using "(scale( 1 ) * 2)" instead of "scale( 2 )" to deal with rounding
|
|
||||||
// issues. E.g. at scale factor 1.5 the first returns 4, but the second 3.
|
|
||||||
size.width = Math.max( size.width, scale( minimumWidth ) - (scale( 1 ) * 2) );
|
|
||||||
return size;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,38 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2019 FormDev Software GmbH
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.formdev.flatlaf.ui;
|
||||||
|
|
||||||
|
import java.awt.Component;
|
||||||
|
import javax.swing.UIManager;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Border for various text components (e.g. {@link javax.swing.JTextField}).
|
||||||
|
*
|
||||||
|
* @uiDefault TextComponent.arc int
|
||||||
|
*
|
||||||
|
* @author Karl Tauber
|
||||||
|
*/
|
||||||
|
public class FlatTextBorder
|
||||||
|
extends FlatBorder
|
||||||
|
{
|
||||||
|
protected final int arc = UIManager.getInt( "TextComponent.arc" );
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected int getArc( Component c ) {
|
||||||
|
return FlatUIUtils.isRoundRect( c ) ? Short.MAX_VALUE : arc;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -35,6 +35,7 @@ import javax.swing.UIManager;
|
|||||||
import javax.swing.plaf.ComponentUI;
|
import javax.swing.plaf.ComponentUI;
|
||||||
import javax.swing.plaf.UIResource;
|
import javax.swing.plaf.UIResource;
|
||||||
import javax.swing.plaf.basic.BasicTextFieldUI;
|
import javax.swing.plaf.basic.BasicTextFieldUI;
|
||||||
|
import javax.swing.text.Caret;
|
||||||
import javax.swing.text.JTextComponent;
|
import javax.swing.text.JTextComponent;
|
||||||
import com.formdev.flatlaf.FlatClientProperties;
|
import com.formdev.flatlaf.FlatClientProperties;
|
||||||
|
|
||||||
@@ -58,17 +59,16 @@ import com.formdev.flatlaf.FlatClientProperties;
|
|||||||
*
|
*
|
||||||
* <!-- FlatTextFieldUI -->
|
* <!-- FlatTextFieldUI -->
|
||||||
*
|
*
|
||||||
* @uiDefault Component.focusWidth int
|
|
||||||
* @uiDefault Component.minimumWidth int
|
* @uiDefault Component.minimumWidth int
|
||||||
* @uiDefault Component.isIntelliJTheme boolean
|
* @uiDefault Component.isIntelliJTheme boolean
|
||||||
* @uiDefault TextField.placeholderForeground Color
|
* @uiDefault TextField.placeholderForeground Color
|
||||||
|
* @uiDefault TextComponent.selectAllOnFocusPolicy String never, once (default) or always
|
||||||
*
|
*
|
||||||
* @author Karl Tauber
|
* @author Karl Tauber
|
||||||
*/
|
*/
|
||||||
public class FlatTextFieldUI
|
public class FlatTextFieldUI
|
||||||
extends BasicTextFieldUI
|
extends BasicTextFieldUI
|
||||||
{
|
{
|
||||||
protected int focusWidth;
|
|
||||||
protected int minimumWidth;
|
protected int minimumWidth;
|
||||||
protected boolean isIntelliJTheme;
|
protected boolean isIntelliJTheme;
|
||||||
protected Color placeholderForeground;
|
protected Color placeholderForeground;
|
||||||
@@ -84,14 +84,13 @@ public class FlatTextFieldUI
|
|||||||
super.installDefaults();
|
super.installDefaults();
|
||||||
|
|
||||||
String prefix = getPropertyPrefix();
|
String prefix = getPropertyPrefix();
|
||||||
focusWidth = UIManager.getInt( "Component.focusWidth" );
|
|
||||||
minimumWidth = UIManager.getInt( "Component.minimumWidth" );
|
minimumWidth = UIManager.getInt( "Component.minimumWidth" );
|
||||||
isIntelliJTheme = UIManager.getBoolean( "Component.isIntelliJTheme" );
|
isIntelliJTheme = UIManager.getBoolean( "Component.isIntelliJTheme" );
|
||||||
placeholderForeground = UIManager.getColor( prefix + ".placeholderForeground" );
|
placeholderForeground = UIManager.getColor( prefix + ".placeholderForeground" );
|
||||||
|
|
||||||
LookAndFeel.installProperty( getComponent(), "opaque", focusWidth == 0 );
|
LookAndFeel.installProperty( getComponent(), "opaque", false );
|
||||||
|
|
||||||
MigLayoutVisualPadding.install( getComponent(), focusWidth );
|
MigLayoutVisualPadding.install( getComponent() );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -119,17 +118,33 @@ public class FlatTextFieldUI
|
|||||||
focusListener = null;
|
focusListener = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Caret createCaret() {
|
||||||
|
return new FlatCaret( UIManager.getString( "TextComponent.selectAllOnFocusPolicy" ) );
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void propertyChange( PropertyChangeEvent e ) {
|
protected void propertyChange( PropertyChangeEvent e ) {
|
||||||
super.propertyChange( e );
|
super.propertyChange( e );
|
||||||
|
propertyChange( getComponent(), e );
|
||||||
|
}
|
||||||
|
|
||||||
if( FlatClientProperties.PLACEHOLDER_TEXT.equals( e.getPropertyName() ) )
|
static void propertyChange( JTextComponent c, PropertyChangeEvent e ) {
|
||||||
getComponent().repaint();
|
switch( e.getPropertyName() ) {
|
||||||
|
case FlatClientProperties.PLACEHOLDER_TEXT:
|
||||||
|
case FlatClientProperties.COMPONENT_ROUND_RECT:
|
||||||
|
c.repaint();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FlatClientProperties.MINIMUM_WIDTH:
|
||||||
|
c.revalidate();
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void paintSafely( Graphics g ) {
|
protected void paintSafely( Graphics g ) {
|
||||||
paintBackground( g, getComponent(), focusWidth, isIntelliJTheme );
|
paintBackground( g, getComponent(), isIntelliJTheme );
|
||||||
paintPlaceholder( g, getComponent(), placeholderForeground );
|
paintPlaceholder( g, getComponent(), placeholderForeground );
|
||||||
super.paintSafely( g );
|
super.paintSafely( g );
|
||||||
}
|
}
|
||||||
@@ -139,17 +154,20 @@ public class FlatTextFieldUI
|
|||||||
// background is painted elsewhere
|
// background is painted elsewhere
|
||||||
}
|
}
|
||||||
|
|
||||||
static void paintBackground( Graphics g, JTextComponent c, int focusWidth, boolean isIntelliJTheme ) {
|
static void paintBackground( Graphics g, JTextComponent c, boolean isIntelliJTheme ) {
|
||||||
// do not paint background if:
|
// do not paint background if:
|
||||||
// - not opaque and
|
// - not opaque and
|
||||||
// - border is not a flat border and
|
// - border is not a flat border and
|
||||||
// - opaque was explicitly set (to false)
|
// - opaque was explicitly set (to false)
|
||||||
// (same behaviour as in AquaTextFieldUI)
|
// (same behaviour as in AquaTextFieldUI)
|
||||||
if( !c.isOpaque() && !(c.getBorder() instanceof FlatBorder) && FlatUIUtils.hasOpaqueBeenExplicitlySet( c ) )
|
if( !c.isOpaque() && FlatUIUtils.getOutsideFlatBorder( c ) == null && FlatUIUtils.hasOpaqueBeenExplicitlySet( c ) )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
float focusWidth = FlatUIUtils.getBorderFocusWidth( c );
|
||||||
|
float arc = FlatUIUtils.getBorderArc( c );
|
||||||
|
|
||||||
// fill background if opaque to avoid garbage if user sets opaque to true
|
// fill background if opaque to avoid garbage if user sets opaque to true
|
||||||
if( c.isOpaque() && focusWidth > 0 )
|
if( c.isOpaque() && (focusWidth > 0 || arc > 0) )
|
||||||
FlatUIUtils.paintParentBackground( g, c );
|
FlatUIUtils.paintParentBackground( g, c );
|
||||||
|
|
||||||
// paint background
|
// paint background
|
||||||
@@ -157,15 +175,13 @@ public class FlatTextFieldUI
|
|||||||
try {
|
try {
|
||||||
FlatUIUtils.setRenderingHints( g2 );
|
FlatUIUtils.setRenderingHints( g2 );
|
||||||
|
|
||||||
float fFocusWidth = (c.getBorder() instanceof FlatBorder) ? scale( (float) focusWidth ) : 0;
|
|
||||||
|
|
||||||
Color background = c.getBackground();
|
Color background = c.getBackground();
|
||||||
g2.setColor( !(background instanceof UIResource)
|
g2.setColor( !(background instanceof UIResource)
|
||||||
? background
|
? background
|
||||||
: (isIntelliJTheme && (!c.isEnabled() || !c.isEditable())
|
: (isIntelliJTheme && (!c.isEnabled() || !c.isEditable())
|
||||||
? FlatUIUtils.getParentBackground( c )
|
? FlatUIUtils.getParentBackground( c )
|
||||||
: background) );
|
: background) );
|
||||||
FlatUIUtils.paintComponentBackground( g2, 0, 0, c.getWidth(), c.getHeight(), fFocusWidth, 0 );
|
FlatUIUtils.paintComponentBackground( g2, 0, 0, c.getWidth(), c.getHeight(), focusWidth, arc );
|
||||||
} finally {
|
} finally {
|
||||||
g2.dispose();
|
g2.dispose();
|
||||||
}
|
}
|
||||||
@@ -198,27 +214,29 @@ public class FlatTextFieldUI
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Dimension getPreferredSize( JComponent c ) {
|
public Dimension getPreferredSize( JComponent c ) {
|
||||||
return applyMinimumWidth( super.getPreferredSize( c ), c );
|
return applyMinimumWidth( c, super.getPreferredSize( c ), minimumWidth );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Dimension getMinimumSize( JComponent c ) {
|
public Dimension getMinimumSize( JComponent c ) {
|
||||||
return applyMinimumWidth( super.getMinimumSize( c ), c );
|
return applyMinimumWidth( c, super.getMinimumSize( c ), minimumWidth );
|
||||||
}
|
}
|
||||||
|
|
||||||
private Dimension applyMinimumWidth( Dimension size, JComponent c ) {
|
static Dimension applyMinimumWidth( JComponent c, Dimension size, int minimumWidth ) {
|
||||||
// do not apply minimum width if JTextField.columns is set
|
// do not apply minimum width if JTextField.columns is set
|
||||||
if( c instanceof JTextField && ((JTextField)c).getColumns() > 0 )
|
if( c instanceof JTextField && ((JTextField)c).getColumns() > 0 )
|
||||||
return size;
|
return size;
|
||||||
|
|
||||||
|
// do not apply minimum width if used in combobox or spinner
|
||||||
Container parent = c.getParent();
|
Container parent = c.getParent();
|
||||||
if( parent instanceof JComboBox ||
|
if( parent instanceof JComboBox ||
|
||||||
parent instanceof JSpinner ||
|
parent instanceof JSpinner ||
|
||||||
(parent != null && parent.getParent() instanceof JSpinner) )
|
(parent != null && parent.getParent() instanceof JSpinner) )
|
||||||
return size;
|
return size;
|
||||||
|
|
||||||
int focusWidth = (c.getBorder() instanceof FlatBorder) ? this.focusWidth : 0;
|
minimumWidth = FlatUIUtils.minimumWidth( c, minimumWidth );
|
||||||
size.width = Math.max( size.width, scale( minimumWidth + (focusWidth * 2) ) );
|
float focusWidth = FlatUIUtils.getBorderFocusWidth( c );
|
||||||
|
size.width = Math.max( size.width, scale( minimumWidth ) + Math.round( focusWidth * 2 ) );
|
||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,9 +16,9 @@
|
|||||||
|
|
||||||
package com.formdev.flatlaf.ui;
|
package com.formdev.flatlaf.ui;
|
||||||
|
|
||||||
import static com.formdev.flatlaf.util.UIScale.scale;
|
|
||||||
import java.awt.Dimension;
|
import java.awt.Dimension;
|
||||||
import java.awt.Graphics;
|
import java.awt.Graphics;
|
||||||
|
import java.beans.PropertyChangeEvent;
|
||||||
import javax.swing.JComponent;
|
import javax.swing.JComponent;
|
||||||
import javax.swing.JEditorPane;
|
import javax.swing.JEditorPane;
|
||||||
import javax.swing.UIManager;
|
import javax.swing.UIManager;
|
||||||
@@ -83,23 +83,20 @@ public class FlatTextPaneUI
|
|||||||
getComponent().putClientProperty( JEditorPane.HONOR_DISPLAY_PROPERTIES, oldHonorDisplayProperties );
|
getComponent().putClientProperty( JEditorPane.HONOR_DISPLAY_PROPERTIES, oldHonorDisplayProperties );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void propertyChange( PropertyChangeEvent e ) {
|
||||||
|
super.propertyChange( e );
|
||||||
|
FlatEditorPaneUI.propertyChange( getComponent(), e );
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Dimension getPreferredSize( JComponent c ) {
|
public Dimension getPreferredSize( JComponent c ) {
|
||||||
return applyMinimumWidth( super.getPreferredSize( c ) );
|
return FlatEditorPaneUI.applyMinimumWidth( c, super.getPreferredSize( c ), minimumWidth );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Dimension getMinimumSize( JComponent c ) {
|
public Dimension getMinimumSize( JComponent c ) {
|
||||||
return applyMinimumWidth( super.getMinimumSize( c ) );
|
return FlatEditorPaneUI.applyMinimumWidth( c, super.getMinimumSize( c ), minimumWidth );
|
||||||
}
|
|
||||||
|
|
||||||
private Dimension applyMinimumWidth( Dimension size ) {
|
|
||||||
// Assume that text area is in a scroll pane (that displays the border)
|
|
||||||
// and subtract 1px border line width.
|
|
||||||
// Using "(scale( 1 ) * 2)" instead of "scale( 2 )" to deal with rounding
|
|
||||||
// issues. E.g. at scale factor 1.5 the first returns 4, but the second 3.
|
|
||||||
size.width = Math.max( size.width, scale( minimumWidth ) - (scale( 1 ) * 2) );
|
|
||||||
return size;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -16,12 +16,18 @@
|
|||||||
|
|
||||||
package com.formdev.flatlaf.ui;
|
package com.formdev.flatlaf.ui;
|
||||||
|
|
||||||
|
import static com.formdev.flatlaf.FlatClientProperties.*;
|
||||||
import java.awt.Color;
|
import java.awt.Color;
|
||||||
|
import java.awt.Component;
|
||||||
|
import java.awt.Graphics;
|
||||||
|
import java.beans.PropertyChangeEvent;
|
||||||
import javax.swing.AbstractButton;
|
import javax.swing.AbstractButton;
|
||||||
import javax.swing.ButtonModel;
|
import javax.swing.ButtonModel;
|
||||||
import javax.swing.JComponent;
|
import javax.swing.JComponent;
|
||||||
|
import javax.swing.JToggleButton;
|
||||||
import javax.swing.UIManager;
|
import javax.swing.UIManager;
|
||||||
import javax.swing.plaf.ComponentUI;
|
import javax.swing.plaf.ComponentUI;
|
||||||
|
import com.formdev.flatlaf.util.UIScale;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides the Flat LaF UI delegate for {@link javax.swing.JToggleButton}.
|
* Provides the Flat LaF UI delegate for {@link javax.swing.JToggleButton}.
|
||||||
@@ -55,6 +61,13 @@ import javax.swing.plaf.ComponentUI;
|
|||||||
* @uiDefault ToggleButton.disabledSelectedBackground Color
|
* @uiDefault ToggleButton.disabledSelectedBackground Color
|
||||||
* @uiDefault ToggleButton.toolbar.selectedBackground Color
|
* @uiDefault ToggleButton.toolbar.selectedBackground Color
|
||||||
*
|
*
|
||||||
|
* @uiDefault ToggleButton.tab.underlineHeight int
|
||||||
|
* @uiDefault ToggleButton.tab.underlineColor Color
|
||||||
|
* @uiDefault ToggleButton.tab.disabledUnderlineColor Color
|
||||||
|
* @uiDefault ToggleButton.tab.selectedBackground Color optional
|
||||||
|
* @uiDefault ToggleButton.tab.hoverBackground Color
|
||||||
|
* @uiDefault ToggleButton.tab.focusBackground Color
|
||||||
|
*
|
||||||
*
|
*
|
||||||
* @author Karl Tauber
|
* @author Karl Tauber
|
||||||
*/
|
*/
|
||||||
@@ -67,6 +80,13 @@ public class FlatToggleButtonUI
|
|||||||
|
|
||||||
protected Color toolbarSelectedBackground;
|
protected Color toolbarSelectedBackground;
|
||||||
|
|
||||||
|
protected int tabUnderlineHeight;
|
||||||
|
protected Color tabUnderlineColor;
|
||||||
|
protected Color tabDisabledUnderlineColor;
|
||||||
|
protected Color tabSelectedBackground;
|
||||||
|
protected Color tabHoverBackground;
|
||||||
|
protected Color tabFocusBackground;
|
||||||
|
|
||||||
private boolean defaults_initialized = false;
|
private boolean defaults_initialized = false;
|
||||||
|
|
||||||
private static ComponentUI instance;
|
private static ComponentUI instance;
|
||||||
@@ -93,6 +113,13 @@ public class FlatToggleButtonUI
|
|||||||
|
|
||||||
toolbarSelectedBackground = UIManager.getColor( "ToggleButton.toolbar.selectedBackground" );
|
toolbarSelectedBackground = UIManager.getColor( "ToggleButton.toolbar.selectedBackground" );
|
||||||
|
|
||||||
|
tabUnderlineHeight = UIManager.getInt( "ToggleButton.tab.underlineHeight" );
|
||||||
|
tabUnderlineColor = UIManager.getColor( "ToggleButton.tab.underlineColor" );
|
||||||
|
tabDisabledUnderlineColor = UIManager.getColor( "ToggleButton.tab.disabledUnderlineColor" );
|
||||||
|
tabSelectedBackground = UIManager.getColor( "ToggleButton.tab.selectedBackground" );
|
||||||
|
tabHoverBackground = UIManager.getColor( "ToggleButton.tab.hoverBackground" );
|
||||||
|
tabFocusBackground = UIManager.getColor( "ToggleButton.tab.focusBackground" );
|
||||||
|
|
||||||
defaults_initialized = true;
|
defaults_initialized = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -103,6 +130,61 @@ public class FlatToggleButtonUI
|
|||||||
defaults_initialized = false;
|
defaults_initialized = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void propertyChange( AbstractButton b, PropertyChangeEvent e ) {
|
||||||
|
super.propertyChange( b, e );
|
||||||
|
|
||||||
|
switch( e.getPropertyName() ) {
|
||||||
|
case BUTTON_TYPE:
|
||||||
|
if( BUTTON_TYPE_TAB.equals( e.getOldValue() ) || BUTTON_TYPE_TAB.equals( e.getNewValue() ) ) {
|
||||||
|
MigLayoutVisualPadding.uninstall( b );
|
||||||
|
MigLayoutVisualPadding.install( b );
|
||||||
|
b.revalidate();
|
||||||
|
}
|
||||||
|
|
||||||
|
b.repaint();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TAB_BUTTON_UNDERLINE_HEIGHT:
|
||||||
|
case TAB_BUTTON_UNDERLINE_COLOR:
|
||||||
|
case TAB_BUTTON_SELECTED_BACKGROUND:
|
||||||
|
b.repaint();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static boolean isTabButton( Component c ) {
|
||||||
|
return c instanceof JToggleButton && clientPropertyEquals( (JToggleButton) c, BUTTON_TYPE, BUTTON_TYPE_TAB );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void paintBackground( Graphics g, JComponent c ) {
|
||||||
|
if( isTabButton( c ) ) {
|
||||||
|
int height = c.getHeight();
|
||||||
|
int width = c.getWidth();
|
||||||
|
boolean selected = ((AbstractButton)c).isSelected();
|
||||||
|
|
||||||
|
// paint background
|
||||||
|
Color background = buttonStateColor( c,
|
||||||
|
selected ? clientPropertyColor( c, TAB_BUTTON_SELECTED_BACKGROUND, tabSelectedBackground ) : null,
|
||||||
|
null, tabFocusBackground, tabHoverBackground, null );
|
||||||
|
if( background != null ) {
|
||||||
|
g.setColor( background );
|
||||||
|
g.fillRect( 0, 0, width, height );
|
||||||
|
}
|
||||||
|
|
||||||
|
// paint underline if selected
|
||||||
|
if( selected ) {
|
||||||
|
int underlineHeight = UIScale.scale( clientPropertyInt( c, TAB_BUTTON_UNDERLINE_HEIGHT, tabUnderlineHeight ) );
|
||||||
|
g.setColor( c.isEnabled()
|
||||||
|
? clientPropertyColor( c, TAB_BUTTON_UNDERLINE_COLOR, tabUnderlineColor )
|
||||||
|
: tabDisabledUnderlineColor );
|
||||||
|
g.fillRect( 0, height - underlineHeight, width, underlineHeight );
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
super.paintBackground( g, c );
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Color getBackground( JComponent c ) {
|
protected Color getBackground( JComponent c ) {
|
||||||
ButtonModel model = ((AbstractButton)c).getModel();
|
ButtonModel model = ((AbstractButton)c).getModel();
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ import javax.swing.UIManager;
|
|||||||
/**
|
/**
|
||||||
* Border for {@link javax.swing.JToolBar}.
|
* Border for {@link javax.swing.JToolBar}.
|
||||||
*
|
*
|
||||||
|
* @uiDefault ToolBar.borderMargins Insets
|
||||||
* @uiDefault ToolBar.gripColor Color
|
* @uiDefault ToolBar.gripColor Color
|
||||||
*
|
*
|
||||||
* @author Karl Tauber
|
* @author Karl Tauber
|
||||||
@@ -42,6 +43,10 @@ public class FlatToolBarBorder
|
|||||||
|
|
||||||
protected final Color gripColor = UIManager.getColor( "ToolBar.gripColor" );
|
protected final Color gripColor = UIManager.getColor( "ToolBar.gripColor" );
|
||||||
|
|
||||||
|
public FlatToolBarBorder() {
|
||||||
|
super( UIManager.getInsets( "ToolBar.borderMargins" ) );
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void paintBorder( Component c, Graphics g, int x, int y, int width, int height ) {
|
public void paintBorder( Component c, Graphics g, int x, int y, int width, int height ) {
|
||||||
// paint grip
|
// paint grip
|
||||||
|
|||||||
@@ -16,18 +16,14 @@
|
|||||||
|
|
||||||
package com.formdev.flatlaf.ui;
|
package com.formdev.flatlaf.ui;
|
||||||
|
|
||||||
import static com.formdev.flatlaf.util.UIScale.scale;
|
|
||||||
import java.awt.Component;
|
import java.awt.Component;
|
||||||
import java.awt.Insets;
|
import java.awt.Insets;
|
||||||
import java.awt.event.ContainerEvent;
|
import java.awt.event.ContainerEvent;
|
||||||
import java.awt.event.ContainerListener;
|
import java.awt.event.ContainerListener;
|
||||||
import javax.swing.AbstractButton;
|
import javax.swing.AbstractButton;
|
||||||
import javax.swing.JComponent;
|
import javax.swing.JComponent;
|
||||||
import javax.swing.UIManager;
|
|
||||||
import javax.swing.border.Border;
|
import javax.swing.border.Border;
|
||||||
import javax.swing.border.EmptyBorder;
|
|
||||||
import javax.swing.plaf.ComponentUI;
|
import javax.swing.plaf.ComponentUI;
|
||||||
import javax.swing.plaf.UIResource;
|
|
||||||
import javax.swing.plaf.basic.BasicToolBarUI;
|
import javax.swing.plaf.basic.BasicToolBarUI;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -45,28 +41,15 @@ import javax.swing.plaf.basic.BasicToolBarUI;
|
|||||||
* @uiDefault ToolBar.floatingForeground Color
|
* @uiDefault ToolBar.floatingForeground Color
|
||||||
* @uiDefault ToolBar.isRollover boolean
|
* @uiDefault ToolBar.isRollover boolean
|
||||||
*
|
*
|
||||||
* <!-- FlatToolBarUI -->
|
|
||||||
*
|
|
||||||
* @uiDefault ToolBar.buttonMargins Insets
|
|
||||||
*
|
|
||||||
* @author Karl Tauber
|
* @author Karl Tauber
|
||||||
*/
|
*/
|
||||||
public class FlatToolBarUI
|
public class FlatToolBarUI
|
||||||
extends BasicToolBarUI
|
extends BasicToolBarUI
|
||||||
{
|
{
|
||||||
private Border rolloverBorder;
|
|
||||||
|
|
||||||
public static ComponentUI createUI( JComponent c ) {
|
public static ComponentUI createUI( JComponent c ) {
|
||||||
return new FlatToolBarUI();
|
return new FlatToolBarUI();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void uninstallUI( JComponent c ) {
|
|
||||||
super.uninstallUI( c );
|
|
||||||
|
|
||||||
rolloverBorder = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected ContainerListener createToolBarContListener() {
|
protected ContainerListener createToolBarContListener() {
|
||||||
return new ToolBarContListener() {
|
return new ToolBarContListener() {
|
||||||
@@ -90,26 +73,15 @@ public class FlatToolBarUI
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
// disable rollover border
|
||||||
protected Border createRolloverBorder() {
|
@Override protected void setBorderToRollover( Component c ) {}
|
||||||
return getRolloverBorder();
|
@Override protected void setBorderToNonRollover( Component c ) {}
|
||||||
}
|
@Override protected void setBorderToNormal( Component c ) {}
|
||||||
|
@Override protected void installRolloverBorders( JComponent c ) {}
|
||||||
@Override
|
@Override protected void installNonRolloverBorders( JComponent c ) {}
|
||||||
protected Border createNonRolloverBorder() {
|
@Override protected void installNormalBorders( JComponent c ) {}
|
||||||
return getRolloverBorder();
|
@Override protected Border createRolloverBorder() { return null; }
|
||||||
}
|
@Override protected Border createNonRolloverBorder() { return null; }
|
||||||
|
|
||||||
@Override
|
|
||||||
protected Border getNonRolloverBorder( AbstractButton b ) {
|
|
||||||
return getRolloverBorder();
|
|
||||||
}
|
|
||||||
|
|
||||||
private Border getRolloverBorder() {
|
|
||||||
if( rolloverBorder == null )
|
|
||||||
rolloverBorder = new FlatRolloverMarginBorder();
|
|
||||||
return rolloverBorder;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setOrientation( int orientation ) {
|
public void setOrientation( int orientation ) {
|
||||||
@@ -123,46 +95,4 @@ public class FlatToolBarUI
|
|||||||
|
|
||||||
super.setOrientation( orientation );
|
super.setOrientation( orientation );
|
||||||
}
|
}
|
||||||
|
|
||||||
//---- class FlatRolloverMarginBorder -------------------------------------
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Uses button margin only if explicitly set.
|
|
||||||
* Otherwise uses insets specified in constructor.
|
|
||||||
*/
|
|
||||||
private static class FlatRolloverMarginBorder
|
|
||||||
extends EmptyBorder
|
|
||||||
{
|
|
||||||
public FlatRolloverMarginBorder() {
|
|
||||||
super( UIManager.getInsets( "ToolBar.buttonMargins" ) );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Insets getBorderInsets( Component c, Insets insets ) {
|
|
||||||
Insets margin = (c instanceof AbstractButton)
|
|
||||||
? ((AbstractButton) c).getMargin()
|
|
||||||
: null;
|
|
||||||
|
|
||||||
if( margin == null || margin instanceof UIResource ) {
|
|
||||||
insets.top = top;
|
|
||||||
insets.left = left;
|
|
||||||
insets.bottom = bottom;
|
|
||||||
insets.right = right;
|
|
||||||
} else {
|
|
||||||
// margin explicitly set
|
|
||||||
insets.top = margin.top;
|
|
||||||
insets.left = margin.left;
|
|
||||||
insets.bottom = margin.bottom;
|
|
||||||
insets.right = margin.right;
|
|
||||||
}
|
|
||||||
|
|
||||||
// scale
|
|
||||||
insets.top = scale( insets.top );
|
|
||||||
insets.left = scale( insets.left );
|
|
||||||
insets.bottom = scale( insets.bottom );
|
|
||||||
insets.right = scale( insets.right );
|
|
||||||
|
|
||||||
return insets;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,11 +21,13 @@ import java.awt.FontMetrics;
|
|||||||
import java.awt.Graphics;
|
import java.awt.Graphics;
|
||||||
import java.awt.Graphics2D;
|
import java.awt.Graphics2D;
|
||||||
import java.awt.Insets;
|
import java.awt.Insets;
|
||||||
|
import java.beans.PropertyChangeListener;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import javax.swing.JComponent;
|
import javax.swing.JComponent;
|
||||||
import javax.swing.JToolTip;
|
import javax.swing.JToolTip;
|
||||||
import javax.swing.SwingUtilities;
|
import javax.swing.SwingUtilities;
|
||||||
import javax.swing.plaf.ComponentUI;
|
import javax.swing.plaf.ComponentUI;
|
||||||
|
import javax.swing.plaf.basic.BasicHTML;
|
||||||
import javax.swing.plaf.basic.BasicToolTipUI;
|
import javax.swing.plaf.basic.BasicToolTipUI;
|
||||||
import com.formdev.flatlaf.util.StringUtils;
|
import com.formdev.flatlaf.util.StringUtils;
|
||||||
|
|
||||||
@@ -47,6 +49,8 @@ import com.formdev.flatlaf.util.StringUtils;
|
|||||||
public class FlatToolTipUI
|
public class FlatToolTipUI
|
||||||
extends BasicToolTipUI
|
extends BasicToolTipUI
|
||||||
{
|
{
|
||||||
|
private static PropertyChangeListener sharedPropertyChangedListener;
|
||||||
|
|
||||||
private static ComponentUI instance;
|
private static ComponentUI instance;
|
||||||
|
|
||||||
public static ComponentUI createUI( JComponent c ) {
|
public static ComponentUI createUI( JComponent c ) {
|
||||||
@@ -55,6 +59,38 @@ public class FlatToolTipUI
|
|||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void installUI( JComponent c ) {
|
||||||
|
super.installUI( c );
|
||||||
|
|
||||||
|
// update HTML renderer if necessary
|
||||||
|
FlatLabelUI.updateHTMLRenderer( c, ((JToolTip)c).getTipText(), false );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void installListeners( JComponent c ) {
|
||||||
|
super.installListeners( c );
|
||||||
|
|
||||||
|
if( sharedPropertyChangedListener == null ) {
|
||||||
|
sharedPropertyChangedListener = e -> {
|
||||||
|
String name = e.getPropertyName();
|
||||||
|
if( name == "text" || name == "font" || name == "foreground" ) {
|
||||||
|
JToolTip toolTip = (JToolTip) e.getSource();
|
||||||
|
FlatLabelUI.updateHTMLRenderer( toolTip, toolTip.getTipText(), false );
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
c.addPropertyChangeListener( sharedPropertyChangedListener );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void uninstallListeners( JComponent c ) {
|
||||||
|
super.uninstallListeners( c );
|
||||||
|
|
||||||
|
c.removePropertyChangeListener( sharedPropertyChangedListener );
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Dimension getPreferredSize( JComponent c ) {
|
public Dimension getPreferredSize( JComponent c ) {
|
||||||
if( isMultiLine( c ) ) {
|
if( isMultiLine( c ) ) {
|
||||||
@@ -67,7 +103,7 @@ public class FlatToolTipUI
|
|||||||
for( String line : lines )
|
for( String line : lines )
|
||||||
width = Math.max( width, SwingUtilities.computeStringWidth( fm, line ) );
|
width = Math.max( width, SwingUtilities.computeStringWidth( fm, line ) );
|
||||||
|
|
||||||
return new Dimension( insets.left + width + insets.right, insets.top + height + insets.bottom );
|
return new Dimension( insets.left + width + insets.right + 6, insets.top + height + insets.bottom );
|
||||||
} else
|
} else
|
||||||
return super.getPreferredSize( c );
|
return super.getPreferredSize( c );
|
||||||
}
|
}
|
||||||
@@ -83,8 +119,8 @@ public class FlatToolTipUI
|
|||||||
|
|
||||||
List<String> lines = StringUtils.split( ((JToolTip)c).getTipText(), '\n' );
|
List<String> lines = StringUtils.split( ((JToolTip)c).getTipText(), '\n' );
|
||||||
|
|
||||||
int x = insets.left;
|
int x = insets.left + 3;
|
||||||
int x2 = c.getWidth() - insets.right;
|
int x2 = c.getWidth() - insets.right - 3;
|
||||||
int y = insets.top - fm.getDescent();
|
int y = insets.top - fm.getDescent();
|
||||||
int lineHeight = fm.getHeight();
|
int lineHeight = fm.getHeight();
|
||||||
JComponent comp = ((JToolTip)c).getComponent();
|
JComponent comp = ((JToolTip)c).getComponent();
|
||||||
@@ -99,6 +135,6 @@ public class FlatToolTipUI
|
|||||||
|
|
||||||
private boolean isMultiLine( JComponent c ) {
|
private boolean isMultiLine( JComponent c ) {
|
||||||
String text = ((JToolTip)c).getTipText();
|
String text = ((JToolTip)c).getTipText();
|
||||||
return c.getClientProperty( "html" ) == null && text != null && text.indexOf( '\n' ) >= 0;
|
return c.getClientProperty( BasicHTML.propertyKey ) == null && text != null && text.indexOf( '\n' ) >= 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,9 +21,16 @@ import java.awt.Component;
|
|||||||
import java.awt.Graphics;
|
import java.awt.Graphics;
|
||||||
import java.awt.Insets;
|
import java.awt.Insets;
|
||||||
import java.awt.Rectangle;
|
import java.awt.Rectangle;
|
||||||
|
import java.awt.event.MouseEvent;
|
||||||
|
import java.awt.event.MouseListener;
|
||||||
|
import java.beans.PropertyChangeEvent;
|
||||||
|
import java.beans.PropertyChangeListener;
|
||||||
import javax.swing.JComponent;
|
import javax.swing.JComponent;
|
||||||
|
import javax.swing.JTree;
|
||||||
import javax.swing.LookAndFeel;
|
import javax.swing.LookAndFeel;
|
||||||
|
import javax.swing.SwingUtilities;
|
||||||
import javax.swing.UIManager;
|
import javax.swing.UIManager;
|
||||||
|
import javax.swing.JTree.DropLocation;
|
||||||
import javax.swing.plaf.ComponentUI;
|
import javax.swing.plaf.ComponentUI;
|
||||||
import javax.swing.plaf.basic.BasicTreeUI;
|
import javax.swing.plaf.basic.BasicTreeUI;
|
||||||
import javax.swing.tree.DefaultTreeCellRenderer;
|
import javax.swing.tree.DefaultTreeCellRenderer;
|
||||||
@@ -33,13 +40,50 @@ import com.formdev.flatlaf.util.UIScale;
|
|||||||
/**
|
/**
|
||||||
* Provides the Flat LaF UI delegate for {@link javax.swing.JTree}.
|
* Provides the Flat LaF UI delegate for {@link javax.swing.JTree}.
|
||||||
*
|
*
|
||||||
* TODO document used UI defaults of superclass
|
* <!-- BasicTreeUI -->
|
||||||
|
*
|
||||||
|
* @uiDefault Tree.font Font
|
||||||
|
* @uiDefault Tree.background Color
|
||||||
|
* @uiDefault Tree.hash Color
|
||||||
|
* @uiDefault Tree.dropLineColor Color
|
||||||
|
* @uiDefault Tree.expandedIcon Icon
|
||||||
|
* @uiDefault Tree.collapsedIcon Icon
|
||||||
|
* @uiDefault Tree.leftChildIndent int
|
||||||
|
* @uiDefault Tree.rightChildIndent int
|
||||||
|
* @uiDefault Tree.rowHeight int
|
||||||
|
* @uiDefault Tree.scrollsOnExpand boolean
|
||||||
|
* @uiDefault Tree.scrollsHorizontallyAndVertically boolean
|
||||||
|
* @uiDefault Tree.paintLines boolean
|
||||||
|
* @uiDefault Tree.lineTypeDashed boolean
|
||||||
|
* @uiDefault Tree.showsRootHandles boolean
|
||||||
|
* @uiDefault Tree.repaintWholeRow boolean
|
||||||
|
*
|
||||||
|
* <!-- DefaultTreeCellRenderer -->
|
||||||
|
*
|
||||||
|
* @uiDefault Tree.leafIcon Icon
|
||||||
|
* @uiDefault Tree.closedIcon Icon
|
||||||
|
* @uiDefault Tree.openIcon Icon
|
||||||
|
* @uiDefault Tree.textBackground Color
|
||||||
|
* @uiDefault Tree.textForeground Color
|
||||||
|
* @uiDefault Tree.selectionBackground Color
|
||||||
|
* @uiDefault Tree.selectionForeground Color
|
||||||
|
* @uiDefault Tree.selectionBorderColor Color focus indicator border color
|
||||||
|
* @uiDefault Tree.drawsFocusBorderAroundIcon boolean
|
||||||
|
* @uiDefault Tree.drawDashedFocusIndicator boolean
|
||||||
|
* @uiDefault Tree.rendererFillBackground boolean default is true
|
||||||
|
* @uiDefault Tree.rendererMargins Insets
|
||||||
|
* @uiDefault Tree.dropCellBackground Color
|
||||||
|
* @uiDefault Tree.dropCellForeground Color
|
||||||
|
*
|
||||||
|
* <!-- FlatTreeUI -->
|
||||||
*
|
*
|
||||||
* @uiDefault Tree.border Border
|
* @uiDefault Tree.border Border
|
||||||
* @uiDefault Tree.selectionBackground Color
|
* @uiDefault Tree.selectionBackground Color
|
||||||
* @uiDefault Tree.selectionForeground Color
|
* @uiDefault Tree.selectionForeground Color
|
||||||
* @uiDefault Tree.selectionInactiveBackground Color
|
* @uiDefault Tree.selectionInactiveBackground Color
|
||||||
* @uiDefault Tree.selectionInactiveForeground Color
|
* @uiDefault Tree.selectionInactiveForeground Color
|
||||||
|
* @uiDefault Tree.wideSelection boolean
|
||||||
|
* @uiDefault Tree.showCellFocusIndicator boolean
|
||||||
*
|
*
|
||||||
* @author Karl Tauber
|
* @author Karl Tauber
|
||||||
*/
|
*/
|
||||||
@@ -50,6 +94,9 @@ public class FlatTreeUI
|
|||||||
protected Color selectionForeground;
|
protected Color selectionForeground;
|
||||||
protected Color selectionInactiveBackground;
|
protected Color selectionInactiveBackground;
|
||||||
protected Color selectionInactiveForeground;
|
protected Color selectionInactiveForeground;
|
||||||
|
protected Color selectionBorderColor;
|
||||||
|
protected boolean wideSelection;
|
||||||
|
protected boolean showCellFocusIndicator;
|
||||||
|
|
||||||
public static ComponentUI createUI( JComponent c ) {
|
public static ComponentUI createUI( JComponent c ) {
|
||||||
return new FlatTreeUI();
|
return new FlatTreeUI();
|
||||||
@@ -65,6 +112,9 @@ public class FlatTreeUI
|
|||||||
selectionForeground = UIManager.getColor( "Tree.selectionForeground" );
|
selectionForeground = UIManager.getColor( "Tree.selectionForeground" );
|
||||||
selectionInactiveBackground = UIManager.getColor( "Tree.selectionInactiveBackground" );
|
selectionInactiveBackground = UIManager.getColor( "Tree.selectionInactiveBackground" );
|
||||||
selectionInactiveForeground = UIManager.getColor( "Tree.selectionInactiveForeground" );
|
selectionInactiveForeground = UIManager.getColor( "Tree.selectionInactiveForeground" );
|
||||||
|
selectionBorderColor = UIManager.getColor( "Tree.selectionBorderColor" );
|
||||||
|
wideSelection = UIManager.getBoolean( "Tree.wideSelection" );
|
||||||
|
showCellFocusIndicator = UIManager.getBoolean( "Tree.showCellFocusIndicator" );
|
||||||
|
|
||||||
// scale
|
// scale
|
||||||
int rowHeight = FlatUIUtils.getUIInt( "Tree.rowHeight", 16 );
|
int rowHeight = FlatUIUtils.getUIInt( "Tree.rowHeight", 16 );
|
||||||
@@ -84,21 +134,115 @@ public class FlatTreeUI
|
|||||||
selectionForeground = null;
|
selectionForeground = null;
|
||||||
selectionInactiveBackground = null;
|
selectionInactiveBackground = null;
|
||||||
selectionInactiveForeground = null;
|
selectionInactiveForeground = null;
|
||||||
|
selectionBorderColor = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected MouseListener createMouseListener() {
|
||||||
|
if( !wideSelection )
|
||||||
|
return super.createMouseListener();
|
||||||
|
|
||||||
|
return new BasicTreeUI.MouseHandler() {
|
||||||
|
@Override
|
||||||
|
public void mousePressed( MouseEvent e ) {
|
||||||
|
super.mousePressed( handleWideMouseEvent( e ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void mouseReleased( MouseEvent e ) {
|
||||||
|
super.mouseReleased( handleWideMouseEvent( e ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void mouseDragged( MouseEvent e ) {
|
||||||
|
super.mouseDragged( handleWideMouseEvent( e ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
private MouseEvent handleWideMouseEvent( MouseEvent e ) {
|
||||||
|
if( !tree.isEnabled() || !SwingUtilities.isLeftMouseButton( e ) || e.isConsumed() )
|
||||||
|
return e;
|
||||||
|
|
||||||
|
int x = e.getX();
|
||||||
|
int y = e.getY();
|
||||||
|
TreePath path = getClosestPathForLocation( tree, x, y );
|
||||||
|
if( path == null || isLocationInExpandControl( path, x, y ) )
|
||||||
|
return e;
|
||||||
|
|
||||||
|
Rectangle bounds = getPathBounds( tree, path );
|
||||||
|
if( bounds == null || y < bounds.y || y >= (bounds.y + bounds.height) )
|
||||||
|
return e;
|
||||||
|
|
||||||
|
int newX = Math.max( bounds.x, Math.min( x, bounds.x + bounds.width - 1 ) );
|
||||||
|
if( newX == x )
|
||||||
|
return e;
|
||||||
|
|
||||||
|
// clone mouse event, but with new X coordinate
|
||||||
|
return new MouseEvent( e.getComponent(), e.getID(), e.getWhen(),
|
||||||
|
e.getModifiers() | e.getModifiersEx(), newX, e.getY(),
|
||||||
|
e.getClickCount(), e.isPopupTrigger(), e.getButton() );
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected PropertyChangeListener createPropertyChangeListener() {
|
||||||
|
if( !wideSelection )
|
||||||
|
return super.createPropertyChangeListener();
|
||||||
|
|
||||||
|
return new BasicTreeUI.PropertyChangeHandler() {
|
||||||
|
@Override
|
||||||
|
public void propertyChange( PropertyChangeEvent e ) {
|
||||||
|
super.propertyChange( e );
|
||||||
|
|
||||||
|
if( e.getSource() == tree && e.getPropertyName() == "dropLocation" ) {
|
||||||
|
JTree.DropLocation oldValue = (JTree.DropLocation) e.getOldValue();
|
||||||
|
repaintWideDropLocation( oldValue );
|
||||||
|
repaintWideDropLocation( tree.getDropLocation() );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void repaintWideDropLocation(JTree.DropLocation loc) {
|
||||||
|
if( loc == null || isDropLine( loc ) )
|
||||||
|
return;
|
||||||
|
|
||||||
|
Rectangle r = tree.getPathBounds( loc.getPath() );
|
||||||
|
if( r != null )
|
||||||
|
tree.repaint( 0, r.y, tree.getWidth(), r.height );
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Same as super.paintRow(), but uses inactive selection background/foreground if tree is not focused.
|
* Same as super.paintRow(), but supports wide selection and uses
|
||||||
|
* inactive selection background/foreground if tree is not focused.
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected void paintRow( Graphics g, Rectangle clipBounds, Insets insets, Rectangle bounds, TreePath path, int row,
|
protected void paintRow( Graphics g, Rectangle clipBounds, Insets insets, Rectangle bounds,
|
||||||
boolean isExpanded, boolean hasBeenExpanded, boolean isLeaf )
|
TreePath path, int row, boolean isExpanded, boolean hasBeenExpanded, boolean isLeaf )
|
||||||
{
|
{
|
||||||
if( editingComponent != null && editingRow == row )
|
boolean isEditing = (editingComponent != null && editingRow == row);
|
||||||
return;
|
boolean hasFocus = FlatUIUtils.isPermanentFocusOwner( tree );
|
||||||
|
|
||||||
boolean hasFocus = tree.hasFocus();
|
|
||||||
boolean cellHasFocus = hasFocus && (row == getLeadSelectionRow());
|
boolean cellHasFocus = hasFocus && (row == getLeadSelectionRow());
|
||||||
boolean isSelected = tree.isRowSelected( row );
|
boolean isSelected = tree.isRowSelected( row );
|
||||||
|
boolean isDropRow = isDropRow( row );
|
||||||
|
|
||||||
|
// wide selection background
|
||||||
|
if( wideSelection && (isSelected || isDropRow) ) {
|
||||||
|
// fill background
|
||||||
|
g.setColor( isDropRow
|
||||||
|
? UIManager.getColor( "Tree.dropCellBackground" )
|
||||||
|
: (hasFocus ? selectionBackground : selectionInactiveBackground) );
|
||||||
|
g.fillRect( 0, bounds.y, tree.getWidth(), bounds.height );
|
||||||
|
|
||||||
|
// paint expand/collapse icon
|
||||||
|
if( shouldPaintExpandControl( path, row, isExpanded, hasBeenExpanded, isLeaf ) ) {
|
||||||
|
paintExpandControl( g, clipBounds, insets, bounds,
|
||||||
|
path, row, isExpanded, hasBeenExpanded, isLeaf );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if( isEditing )
|
||||||
|
return;
|
||||||
|
|
||||||
// get renderer component
|
// get renderer component
|
||||||
Component rendererComponent = currentCellRenderer.getTreeCellRendererComponent( tree,
|
Component rendererComponent = currentCellRenderer.getTreeCellRendererComponent( tree,
|
||||||
@@ -106,7 +250,7 @@ public class FlatTreeUI
|
|||||||
|
|
||||||
// apply inactive selection background/foreground if tree is not focused
|
// apply inactive selection background/foreground if tree is not focused
|
||||||
Color oldBackgroundSelectionColor = null;
|
Color oldBackgroundSelectionColor = null;
|
||||||
if( isSelected && !hasFocus ) {
|
if( isSelected && !hasFocus && !isDropRow ) {
|
||||||
if( rendererComponent instanceof DefaultTreeCellRenderer ) {
|
if( rendererComponent instanceof DefaultTreeCellRenderer ) {
|
||||||
DefaultTreeCellRenderer renderer = (DefaultTreeCellRenderer) rendererComponent;
|
DefaultTreeCellRenderer renderer = (DefaultTreeCellRenderer) rendererComponent;
|
||||||
if( renderer.getBackgroundSelectionColor() == selectionBackground ) {
|
if( renderer.getBackgroundSelectionColor() == selectionBackground ) {
|
||||||
@@ -122,11 +266,43 @@ public class FlatTreeUI
|
|||||||
rendererComponent.setForeground( selectionInactiveForeground );
|
rendererComponent.setForeground( selectionInactiveForeground );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// remove focus selection border if exactly one item is selected
|
||||||
|
Color oldBorderSelectionColor = null;
|
||||||
|
if( isSelected && hasFocus &&
|
||||||
|
(!showCellFocusIndicator || tree.getMinSelectionRow() == tree.getMaxSelectionRow()) &&
|
||||||
|
rendererComponent instanceof DefaultTreeCellRenderer )
|
||||||
|
{
|
||||||
|
DefaultTreeCellRenderer renderer = (DefaultTreeCellRenderer) rendererComponent;
|
||||||
|
if( renderer.getBorderSelectionColor() == selectionBorderColor ) {
|
||||||
|
oldBorderSelectionColor = renderer.getBorderSelectionColor();
|
||||||
|
renderer.setBorderSelectionColor( null );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// paint renderer
|
// paint renderer
|
||||||
rendererPane.paintComponent( g, rendererComponent, tree, bounds.x, bounds.y, bounds.width, bounds.height, true );
|
rendererPane.paintComponent( g, rendererComponent, tree, bounds.x, bounds.y, bounds.width, bounds.height, true );
|
||||||
|
|
||||||
// restore background selection color
|
// restore background selection color and border selection color
|
||||||
if( oldBackgroundSelectionColor != null )
|
if( oldBackgroundSelectionColor != null )
|
||||||
((DefaultTreeCellRenderer)rendererComponent).setBackgroundSelectionColor( oldBackgroundSelectionColor );
|
((DefaultTreeCellRenderer)rendererComponent).setBackgroundSelectionColor( oldBackgroundSelectionColor );
|
||||||
|
if( oldBorderSelectionColor != null )
|
||||||
|
((DefaultTreeCellRenderer)rendererComponent).setBorderSelectionColor( oldBorderSelectionColor );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether dropping on a row.
|
||||||
|
* See DefaultTreeCellRenderer.getTreeCellRendererComponent().
|
||||||
|
*/
|
||||||
|
private boolean isDropRow( int row ) {
|
||||||
|
JTree.DropLocation dropLocation = tree.getDropLocation();
|
||||||
|
return dropLocation != null &&
|
||||||
|
dropLocation.getChildIndex() == -1 &&
|
||||||
|
tree.getRowForPath( dropLocation.getPath() ) == row;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Rectangle getDropLineRect( DropLocation loc ) {
|
||||||
|
Rectangle r = super.getDropLineRect( loc );
|
||||||
|
return wideSelection ? new Rectangle( 0, r.y, tree.getWidth(), r.height ) : r;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,9 +20,11 @@ import java.awt.Color;
|
|||||||
import java.awt.Component;
|
import java.awt.Component;
|
||||||
import java.awt.Container;
|
import java.awt.Container;
|
||||||
import java.awt.Dimension;
|
import java.awt.Dimension;
|
||||||
|
import java.awt.Font;
|
||||||
import java.awt.Graphics;
|
import java.awt.Graphics;
|
||||||
import java.awt.Graphics2D;
|
import java.awt.Graphics2D;
|
||||||
import java.awt.Insets;
|
import java.awt.Insets;
|
||||||
|
import java.awt.KeyboardFocusManager;
|
||||||
import java.awt.Rectangle;
|
import java.awt.Rectangle;
|
||||||
import java.awt.RenderingHints;
|
import java.awt.RenderingHints;
|
||||||
import java.awt.Shape;
|
import java.awt.Shape;
|
||||||
@@ -37,8 +39,12 @@ import java.util.function.Consumer;
|
|||||||
import javax.swing.JComponent;
|
import javax.swing.JComponent;
|
||||||
import javax.swing.LookAndFeel;
|
import javax.swing.LookAndFeel;
|
||||||
import javax.swing.UIManager;
|
import javax.swing.UIManager;
|
||||||
import javax.swing.plaf.ColorUIResource;
|
import javax.swing.border.Border;
|
||||||
|
import javax.swing.border.CompoundBorder;
|
||||||
|
import javax.swing.plaf.UIResource;
|
||||||
|
import com.formdev.flatlaf.FlatClientProperties;
|
||||||
import com.formdev.flatlaf.util.DerivedColor;
|
import com.formdev.flatlaf.util.DerivedColor;
|
||||||
|
import com.formdev.flatlaf.util.Graphics2DProxy;
|
||||||
import com.formdev.flatlaf.util.HiDPIUtils;
|
import com.formdev.flatlaf.util.HiDPIUtils;
|
||||||
import com.formdev.flatlaf.util.JavaCompatibility;
|
import com.formdev.flatlaf.util.JavaCompatibility;
|
||||||
import com.formdev.flatlaf.util.UIScale;
|
import com.formdev.flatlaf.util.UIScale;
|
||||||
@@ -82,6 +88,13 @@ public class FlatUIUtils
|
|||||||
insets1.right + insets2.right );
|
insets1.right + insets2.right );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void setInsets( Insets dest, Insets src ) {
|
||||||
|
dest.top = src.top;
|
||||||
|
dest.left = src.left;
|
||||||
|
dest.bottom = src.bottom;
|
||||||
|
dest.right = src.right;
|
||||||
|
}
|
||||||
|
|
||||||
public static Color getUIColor( String key, int defaultColorRGB ) {
|
public static Color getUIColor( String key, int defaultColorRGB ) {
|
||||||
Color color = UIManager.getColor( key );
|
Color color = UIManager.getColor( key );
|
||||||
return (color != null) ? color : new Color( defaultColorRGB );
|
return (color != null) ? color : new Color( defaultColorRGB );
|
||||||
@@ -102,14 +115,76 @@ public class FlatUIUtils
|
|||||||
return (value instanceof Integer) ? (Integer) value : defaultValue;
|
return (value instanceof Integer) ? (Integer) value : defaultValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static float getUIFloat( String key, float defaultValue ) {
|
||||||
|
Object value = UIManager.get( key );
|
||||||
|
return (value instanceof Number) ? ((Number)value).floatValue() : defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
public static Color nonUIResource( Color c ) {
|
public static Color nonUIResource( Color c ) {
|
||||||
return (c instanceof ColorUIResource) ? new Color( c.getRGB(), true ) : c;
|
return (c instanceof UIResource) ? new Color( c.getRGB(), true ) : c;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Font nonUIResource( Font font ) {
|
||||||
|
return (font instanceof UIResource) ? font.deriveFont( font.getStyle() ) : font;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int minimumWidth( JComponent c, int minimumWidth ) {
|
||||||
|
return FlatClientProperties.clientPropertyInt( c, FlatClientProperties.MINIMUM_WIDTH, minimumWidth );
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int minimumHeight( JComponent c, int minimumHeight ) {
|
||||||
|
return FlatClientProperties.clientPropertyInt( c, FlatClientProperties.MINIMUM_HEIGHT, minimumHeight );
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isTableCellEditor( Component c ) {
|
public static boolean isTableCellEditor( Component c ) {
|
||||||
return c instanceof JComponent && Boolean.TRUE.equals( ((JComponent)c).getClientProperty( "JComboBox.isTableCellEditor" ) );
|
return c instanceof JComponent && Boolean.TRUE.equals( ((JComponent)c).getClientProperty( "JComboBox.isTableCellEditor" ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean isPermanentFocusOwner( Component c ) {
|
||||||
|
return (KeyboardFocusManager.getCurrentKeyboardFocusManager().getPermanentFocusOwner() == c);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isRoundRect( Component c ) {
|
||||||
|
return c instanceof JComponent && FlatClientProperties.clientPropertyBoolean(
|
||||||
|
(JComponent) c, FlatClientProperties.COMPONENT_ROUND_RECT, false );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the scaled thickness of the outer focus border for the given component.
|
||||||
|
*/
|
||||||
|
public static float getBorderFocusWidth( JComponent c ) {
|
||||||
|
FlatBorder border = getOutsideFlatBorder( c );
|
||||||
|
return (border != null)
|
||||||
|
? UIScale.scale( (float) border.getFocusWidth( c ) )
|
||||||
|
: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the scaled arc diameter of the border for the given component.
|
||||||
|
*/
|
||||||
|
public static float getBorderArc( JComponent c ) {
|
||||||
|
FlatBorder border = getOutsideFlatBorder( c );
|
||||||
|
return (border != null)
|
||||||
|
? UIScale.scale( (float) border.getArc( c ) )
|
||||||
|
: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean hasRoundBorder( JComponent c ) {
|
||||||
|
return getBorderArc( c ) >= c.getHeight();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static FlatBorder getOutsideFlatBorder( JComponent c ) {
|
||||||
|
Border border = c.getBorder();
|
||||||
|
for(;;) {
|
||||||
|
if( border instanceof FlatBorder )
|
||||||
|
return (FlatBorder) border;
|
||||||
|
else if( border instanceof CompoundBorder )
|
||||||
|
border = ((CompoundBorder)border).getOutsideBorder();
|
||||||
|
else
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets rendering hints used for painting.
|
* Sets rendering hints used for painting.
|
||||||
*/
|
*/
|
||||||
@@ -119,10 +194,10 @@ public class FlatUIUtils
|
|||||||
MAC_USE_QUARTZ ? RenderingHints.VALUE_STROKE_PURE : RenderingHints.VALUE_STROKE_NORMALIZE );
|
MAC_USE_QUARTZ ? RenderingHints.VALUE_STROKE_PURE : RenderingHints.VALUE_STROKE_NORMALIZE );
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void setColor( Graphics g, Color color, Color baseColor ) {
|
public static Color deriveColor( Color color, Color baseColor ) {
|
||||||
if( color instanceof DerivedColor )
|
return (color instanceof DerivedColor)
|
||||||
color = ((DerivedColor)color).derive( baseColor );
|
? ((DerivedColor)color).derive( baseColor )
|
||||||
g.setColor( color );
|
: color;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -141,7 +216,7 @@ public class FlatUIUtils
|
|||||||
double systemScaleFactor = UIScale.getSystemScaleFactor( g );
|
double systemScaleFactor = UIScale.getSystemScaleFactor( g );
|
||||||
if( systemScaleFactor != 1 && systemScaleFactor != 2 ) {
|
if( systemScaleFactor != 1 && systemScaleFactor != 2 ) {
|
||||||
// paint at scale 1x to avoid clipping on right and bottom edges at 125%, 150% or 175%
|
// paint at scale 1x to avoid clipping on right and bottom edges at 125%, 150% or 175%
|
||||||
HiDPIUtils.paintAtScale1x( g, x, y, width, height, systemScaleFactor,
|
HiDPIUtils.paintAtScale1x( g, x, y, width, height,
|
||||||
(g2d, x2, y2, width2, height2, scaleFactor) -> {
|
(g2d, x2, y2, width2, height2, scaleFactor) -> {
|
||||||
paintComponentOuterBorderImpl( g2d, x2, y2, width2, height2,
|
paintComponentOuterBorderImpl( g2d, x2, y2, width2, height2,
|
||||||
(float) (focusWidth * scaleFactor), (float) (lineWidth * scaleFactor), (float) (arc * scaleFactor) );
|
(float) (focusWidth * scaleFactor), (float) (lineWidth * scaleFactor), (float) (arc * scaleFactor) );
|
||||||
@@ -155,13 +230,17 @@ public class FlatUIUtils
|
|||||||
private static void paintComponentOuterBorderImpl( Graphics2D g, int x, int y, int width, int height,
|
private static void paintComponentOuterBorderImpl( Graphics2D g, int x, int y, int width, int height,
|
||||||
float focusWidth, float lineWidth, float arc )
|
float focusWidth, float lineWidth, float arc )
|
||||||
{
|
{
|
||||||
float outerRadius = (arc > 0) ? arc + focusWidth - UIScale.scale( 2f ) : focusWidth;
|
|
||||||
float ow = focusWidth + lineWidth;
|
float ow = focusWidth + lineWidth;
|
||||||
float innerRadius = outerRadius - ow;
|
float outerArc = arc + (focusWidth * 2);
|
||||||
|
float innerArc = arc - (lineWidth * 2);
|
||||||
|
|
||||||
|
// reduce outer arc slightly for small arcs to make the curve slightly wider
|
||||||
|
if( arc > 0 && arc < UIScale.scale( 10 ) )
|
||||||
|
outerArc -= UIScale.scale( 2f );
|
||||||
|
|
||||||
Path2D path = new Path2D.Float( Path2D.WIND_EVEN_ODD );
|
Path2D path = new Path2D.Float( Path2D.WIND_EVEN_ODD );
|
||||||
path.append( createRoundRectanglePath( x, y, width, height, outerRadius, outerRadius, outerRadius, outerRadius ), false );
|
path.append( createComponentRectangle( x, y, width, height, outerArc ), false );
|
||||||
path.append( createRoundRectanglePath( x + ow, y + ow, width - (ow * 2), height - (ow * 2), innerRadius, innerRadius, innerRadius, innerRadius ), false );
|
path.append( createComponentRectangle( x + ow, y + ow, width - (ow * 2), height - (ow * 2), innerArc ), false );
|
||||||
g.fill( path );
|
g.fill( path );
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -181,7 +260,7 @@ public class FlatUIUtils
|
|||||||
double systemScaleFactor = UIScale.getSystemScaleFactor( g );
|
double systemScaleFactor = UIScale.getSystemScaleFactor( g );
|
||||||
if( systemScaleFactor != 1 && systemScaleFactor != 2 ) {
|
if( systemScaleFactor != 1 && systemScaleFactor != 2 ) {
|
||||||
// paint at scale 1x to avoid clipping on right and bottom edges at 125%, 150% or 175%
|
// paint at scale 1x to avoid clipping on right and bottom edges at 125%, 150% or 175%
|
||||||
HiDPIUtils.paintAtScale1x( g, x, y, width, height, systemScaleFactor,
|
HiDPIUtils.paintAtScale1x( g, x, y, width, height,
|
||||||
(g2d, x2, y2, width2, height2, scaleFactor) -> {
|
(g2d, x2, y2, width2, height2, scaleFactor) -> {
|
||||||
paintComponentBorderImpl( g2d, x2, y2, width2, height2,
|
paintComponentBorderImpl( g2d, x2, y2, width2, height2,
|
||||||
(float) (focusWidth * scaleFactor), (float) (lineWidth * scaleFactor), (float) (arc * scaleFactor) );
|
(float) (focusWidth * scaleFactor), (float) (lineWidth * scaleFactor), (float) (arc * scaleFactor) );
|
||||||
@@ -195,14 +274,16 @@ public class FlatUIUtils
|
|||||||
private static void paintComponentBorderImpl( Graphics2D g, int x, int y, int width, int height,
|
private static void paintComponentBorderImpl( Graphics2D g, int x, int y, int width, int height,
|
||||||
float focusWidth, float lineWidth, float arc )
|
float focusWidth, float lineWidth, float arc )
|
||||||
{
|
{
|
||||||
float arc2 = arc > lineWidth ? arc - lineWidth : 0f;
|
float x1 = x + focusWidth;
|
||||||
|
float y1 = y + focusWidth;
|
||||||
|
float width1 = width - focusWidth * 2;
|
||||||
|
float height1 = height - focusWidth * 2;
|
||||||
|
float arc2 = arc - (lineWidth * 2);
|
||||||
|
|
||||||
RoundRectangle2D.Float r1 = new RoundRectangle2D.Float(
|
Shape r1 = createComponentRectangle( x1, y1, width1, height1, arc );
|
||||||
x + focusWidth, y + focusWidth,
|
Shape r2 = createComponentRectangle(
|
||||||
width - focusWidth * 2, height - focusWidth * 2, arc, arc );
|
x1 + lineWidth, y1 + lineWidth,
|
||||||
RoundRectangle2D.Float r2 = new RoundRectangle2D.Float(
|
width1 - lineWidth * 2, height1 - lineWidth * 2, arc2 );
|
||||||
r1.x + lineWidth, r1.y + lineWidth,
|
|
||||||
r1.width - lineWidth * 2, r1.height - lineWidth * 2, arc2, arc2 );
|
|
||||||
|
|
||||||
Path2D border = new Path2D.Float( Path2D.WIND_EVEN_ODD );
|
Path2D border = new Path2D.Float( Path2D.WIND_EVEN_ODD );
|
||||||
border.append( r1, false );
|
border.append( r1, false );
|
||||||
@@ -226,7 +307,7 @@ public class FlatUIUtils
|
|||||||
double systemScaleFactor = UIScale.getSystemScaleFactor( g );
|
double systemScaleFactor = UIScale.getSystemScaleFactor( g );
|
||||||
if( systemScaleFactor != 1 && systemScaleFactor != 2 ) {
|
if( systemScaleFactor != 1 && systemScaleFactor != 2 ) {
|
||||||
// paint at scale 1x to avoid clipping on right and bottom edges at 125%, 150% or 175%
|
// paint at scale 1x to avoid clipping on right and bottom edges at 125%, 150% or 175%
|
||||||
HiDPIUtils.paintAtScale1x( g, x, y, width, height, systemScaleFactor,
|
HiDPIUtils.paintAtScale1x( g, x, y, width, height,
|
||||||
(g2d, x2, y2, width2, height2, scaleFactor) -> {
|
(g2d, x2, y2, width2, height2, scaleFactor) -> {
|
||||||
paintComponentBackgroundImpl( g2d, x2, y2, width2, height2,
|
paintComponentBackgroundImpl( g2d, x2, y2, width2, height2,
|
||||||
(float) (focusWidth * scaleFactor), (float) (arc * scaleFactor) );
|
(float) (focusWidth * scaleFactor), (float) (arc * scaleFactor) );
|
||||||
@@ -240,9 +321,21 @@ public class FlatUIUtils
|
|||||||
private static void paintComponentBackgroundImpl( Graphics2D g, int x, int y, int width, int height,
|
private static void paintComponentBackgroundImpl( Graphics2D g, int x, int y, int width, int height,
|
||||||
float focusWidth, float arc )
|
float focusWidth, float arc )
|
||||||
{
|
{
|
||||||
g.fill( new RoundRectangle2D.Float(
|
g.fill( createComponentRectangle(
|
||||||
x + focusWidth, y + focusWidth,
|
x + focusWidth, y + focusWidth,
|
||||||
width - focusWidth * 2, height - focusWidth * 2, arc, arc ) );
|
width - focusWidth * 2, height - focusWidth * 2, arc ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a (rounded) rectangle used to paint components (border, background, etc).
|
||||||
|
* The given arc diameter is limited to min(width,height).
|
||||||
|
*/
|
||||||
|
public static Shape createComponentRectangle( float x, float y, float w, float h, float arc ) {
|
||||||
|
if( arc <= 0 )
|
||||||
|
return new Rectangle2D.Float( x, y, w, h );
|
||||||
|
|
||||||
|
arc = Math.min( arc, Math.min( w, h ) );
|
||||||
|
return new RoundRectangle2D.Float( x, y, w, h, arc, arc );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -278,6 +371,17 @@ public class FlatUIUtils
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a not-filled rectangle shape with the given line width.
|
||||||
|
*/
|
||||||
|
public static Path2D createRectangle( float x, float y, float width, float height, float lineWidth ) {
|
||||||
|
Path2D path = new Path2D.Float( Path2D.WIND_EVEN_ODD );
|
||||||
|
path.append( new Rectangle2D.Float( x, y, width, height ), false );
|
||||||
|
path.append( new Rectangle2D.Float( x + lineWidth, y + lineWidth,
|
||||||
|
width - (lineWidth * 2), height - (lineWidth * 2) ), false );
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a not-filled rounded rectangle shape and allows specifying the line width and the radius or each corner.
|
* Creates a not-filled rounded rectangle shape and allows specifying the line width and the radius or each corner.
|
||||||
*/
|
*/
|
||||||
@@ -300,14 +404,12 @@ public class FlatUIUtils
|
|||||||
if( arcTopLeft <= 0 && arcTopRight <= 0 && arcBottomLeft <= 0 && arcBottomRight <= 0 )
|
if( arcTopLeft <= 0 && arcTopRight <= 0 && arcBottomLeft <= 0 && arcBottomRight <= 0 )
|
||||||
return new Rectangle2D.Float( x, y, width, height );
|
return new Rectangle2D.Float( x, y, width, height );
|
||||||
|
|
||||||
if( arcTopLeft < 0 )
|
// limit arcs to min(width,height)
|
||||||
arcTopLeft = 0;
|
float maxArc = Math.min( width, height ) / 2;
|
||||||
if( arcTopRight < 0 )
|
arcTopLeft = (arcTopLeft > 0) ? Math.min( arcTopLeft, maxArc ) : 0;
|
||||||
arcTopRight = 0;
|
arcTopRight = (arcTopRight > 0) ? Math.min( arcTopRight, maxArc ) : 0;
|
||||||
if( arcBottomLeft < 0 )
|
arcBottomLeft = (arcBottomLeft > 0) ? Math.min( arcBottomLeft, maxArc ) : 0;
|
||||||
arcBottomLeft = 0;
|
arcBottomRight = (arcBottomRight > 0) ? Math.min( arcBottomRight, maxArc ) : 0;
|
||||||
if( arcBottomRight < 0 )
|
|
||||||
arcBottomRight = 0;
|
|
||||||
|
|
||||||
float x2 = x + width;
|
float x2 = x + width;
|
||||||
float y2 = y + height;
|
float y2 = y + height;
|
||||||
@@ -368,6 +470,23 @@ public class FlatUIUtils
|
|||||||
public static void drawStringUnderlineCharAt( JComponent c, Graphics g,
|
public static void drawStringUnderlineCharAt( JComponent c, Graphics g,
|
||||||
String text, int underlinedIndex, int x, int y )
|
String text, int underlinedIndex, int x, int y )
|
||||||
{
|
{
|
||||||
|
// scale underline height if necessary
|
||||||
|
if( underlinedIndex >= 0 && UIScale.getUserScaleFactor() > 1 ) {
|
||||||
|
g = new Graphics2DProxy( (Graphics2D) g ) {
|
||||||
|
@Override
|
||||||
|
public void fillRect( int x, int y, int width, int height ) {
|
||||||
|
if( height == 1 ) {
|
||||||
|
// scale height and correct y position
|
||||||
|
// (using 0.9f so that underline height is 1 at scale factor 1.5x)
|
||||||
|
height = Math.round( UIScale.scale( 0.9f ) );
|
||||||
|
y += height - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
super.fillRect( x, y, width, height );
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
JavaCompatibility.drawStringUnderlineCharAt( c, g, text, underlinedIndex, x, y );
|
JavaCompatibility.drawStringUnderlineCharAt( c, g, text, underlinedIndex, x, y );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -69,14 +69,17 @@ public class MigLayoutVisualPadding
|
|||||||
/**
|
/**
|
||||||
* Convenience method that checks whether component border is a FlatBorder.
|
* Convenience method that checks whether component border is a FlatBorder.
|
||||||
*/
|
*/
|
||||||
public static void install( JComponent c, int focusWidth ) {
|
public static void install( JComponent c ) {
|
||||||
if( !migLayoutAvailable )
|
if( !migLayoutAvailable )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
install( c, c2 -> {
|
install( c, c2 -> {
|
||||||
return (c2.getBorder() instanceof FlatBorder)
|
FlatBorder border = FlatUIUtils.getOutsideFlatBorder( c2 );
|
||||||
? new Insets( focusWidth, focusWidth, focusWidth, focusWidth )
|
if( border != null ) {
|
||||||
: null;
|
int focusWidth = border.getFocusWidth( c2 );
|
||||||
|
return new Insets( focusWidth, focusWidth, focusWidth, focusWidth );
|
||||||
|
} else
|
||||||
|
return null;
|
||||||
}, "border" );
|
}, "border" );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ import java.awt.Color;
|
|||||||
*/
|
*/
|
||||||
public class ColorFunctions
|
public class ColorFunctions
|
||||||
{
|
{
|
||||||
public static Color applyFunctions( Color color, ColorFunction[] functions ) {
|
public static Color applyFunctions( Color color, ColorFunction... functions ) {
|
||||||
float[] hsl = HSLColor.fromRGB( color );
|
float[] hsl = HSLColor.fromRGB( color );
|
||||||
float alpha = color.getAlpha() / 255f;
|
float alpha = color.getAlpha() / 255f;
|
||||||
|
|
||||||
@@ -49,20 +49,26 @@ public class ColorFunctions
|
|||||||
void apply( float[] hsl );
|
void apply( float[] hsl );
|
||||||
}
|
}
|
||||||
|
|
||||||
//---- class Lighten ------------------------------------------------------
|
//---- class HSLIncreaseDecrease ------------------------------------------
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Increase the lightness of a color in the HSL color space by an absolute
|
* Increase or decrease hue, saturation or luminance of a color in the HSL color space
|
||||||
* or relative amount.
|
* by an absolute or relative amount.
|
||||||
*/
|
*/
|
||||||
public static class Lighten
|
public static class HSLIncreaseDecrease
|
||||||
implements ColorFunction
|
implements ColorFunction
|
||||||
{
|
{
|
||||||
private final float amount;
|
public final int hslIndex;
|
||||||
private final boolean relative;
|
public final boolean increase;
|
||||||
private final boolean autoInverse;
|
public final float amount;
|
||||||
|
public final boolean relative;
|
||||||
|
public final boolean autoInverse;
|
||||||
|
|
||||||
public Lighten( float amount, boolean relative, boolean autoInverse ) {
|
public HSLIncreaseDecrease( int hslIndex, boolean increase,
|
||||||
|
float amount, boolean relative, boolean autoInverse )
|
||||||
|
{
|
||||||
|
this.hslIndex = hslIndex;
|
||||||
|
this.increase = increase;
|
||||||
this.amount = amount;
|
this.amount = amount;
|
||||||
this.relative = relative;
|
this.relative = relative;
|
||||||
this.autoInverse = autoInverse;
|
this.autoInverse = autoInverse;
|
||||||
@@ -70,33 +76,17 @@ public class ColorFunctions
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void apply( float[] hsl ) {
|
public void apply( float[] hsl ) {
|
||||||
float amount2 = autoInverse && shouldInverse( hsl ) ? -amount : amount;
|
float amount2 = increase ? amount : -amount;
|
||||||
hsl[2] = clamp( relative
|
amount2 = autoInverse && shouldInverse( hsl ) ? -amount2 : amount2;
|
||||||
? (hsl[2] * ((100 + amount2) / 100))
|
hsl[hslIndex] = clamp( relative
|
||||||
: (hsl[2] + amount2) );
|
? (hsl[hslIndex] * ((100 + amount2) / 100))
|
||||||
|
: (hsl[hslIndex] + amount2) );
|
||||||
}
|
}
|
||||||
|
|
||||||
protected boolean shouldInverse( float[] hsl ) {
|
protected boolean shouldInverse( float[] hsl ) {
|
||||||
return hsl[2] >= 50;
|
return increase
|
||||||
}
|
? hsl[hslIndex] >= 50
|
||||||
}
|
: hsl[hslIndex] < 50;
|
||||||
|
|
||||||
//---- class Darken -------------------------------------------------------
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Decrease the lightness of a color in the HSL color space by an absolute
|
|
||||||
* or relative amount.
|
|
||||||
*/
|
|
||||||
public static class Darken
|
|
||||||
extends Lighten
|
|
||||||
{
|
|
||||||
public Darken( float amount, boolean relative, boolean autoInverse ) {
|
|
||||||
super( -amount, relative, autoInverse );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected boolean shouldInverse( float[] hsl ) {
|
|
||||||
return hsl[2] < 50;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,12 +32,31 @@ public class DerivedColor
|
|||||||
{
|
{
|
||||||
private final ColorFunction[] functions;
|
private final ColorFunction[] functions;
|
||||||
|
|
||||||
public DerivedColor( ColorFunction... functions ) {
|
private boolean hasBaseOfDefaultColor;
|
||||||
super( Color.red );
|
private int baseOfDefaultColorRGB;
|
||||||
|
|
||||||
|
public DerivedColor( Color defaultColor, ColorFunction... functions ) {
|
||||||
|
super( (defaultColor != null) ? defaultColor : Color.red );
|
||||||
this.functions = functions;
|
this.functions = functions;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Color derive( Color baseColor ) {
|
public Color derive( Color baseColor ) {
|
||||||
return ColorFunctions.applyFunctions( baseColor, functions );
|
if( (hasBaseOfDefaultColor && baseOfDefaultColorRGB == baseColor.getRGB()) || baseColor == this )
|
||||||
|
return this; // return default color
|
||||||
|
|
||||||
|
Color result = ColorFunctions.applyFunctions( baseColor, functions );
|
||||||
|
|
||||||
|
// if the result is equal to the default color, then the original base color
|
||||||
|
// was passed and we can cache this to avoid color calculations
|
||||||
|
if( !hasBaseOfDefaultColor && result.getRGB() == this.getRGB() ) {
|
||||||
|
hasBaseOfDefaultColor = true;
|
||||||
|
baseOfDefaultColorRGB = baseColor.getRGB();
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ColorFunction[] getFunctions() {
|
||||||
|
return functions;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,479 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2020 FormDev Software GmbH
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.formdev.flatlaf.util;
|
||||||
|
|
||||||
|
import java.awt.Color;
|
||||||
|
import java.awt.Composite;
|
||||||
|
import java.awt.Font;
|
||||||
|
import java.awt.FontMetrics;
|
||||||
|
import java.awt.Graphics;
|
||||||
|
import java.awt.Graphics2D;
|
||||||
|
import java.awt.GraphicsConfiguration;
|
||||||
|
import java.awt.Image;
|
||||||
|
import java.awt.Paint;
|
||||||
|
import java.awt.Polygon;
|
||||||
|
import java.awt.Rectangle;
|
||||||
|
import java.awt.RenderingHints;
|
||||||
|
import java.awt.RenderingHints.Key;
|
||||||
|
import java.awt.Shape;
|
||||||
|
import java.awt.Stroke;
|
||||||
|
import java.awt.font.FontRenderContext;
|
||||||
|
import java.awt.font.GlyphVector;
|
||||||
|
import java.awt.geom.AffineTransform;
|
||||||
|
import java.awt.image.BufferedImage;
|
||||||
|
import java.awt.image.BufferedImageOp;
|
||||||
|
import java.awt.image.ImageObserver;
|
||||||
|
import java.awt.image.RenderedImage;
|
||||||
|
import java.awt.image.renderable.RenderableImage;
|
||||||
|
import java.text.AttributedCharacterIterator;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A proxy for {@link Graphics2D}.
|
||||||
|
*
|
||||||
|
* @author Karl Tauber
|
||||||
|
*/
|
||||||
|
public class Graphics2DProxy
|
||||||
|
extends Graphics2D
|
||||||
|
{
|
||||||
|
private final Graphics2D delegate;
|
||||||
|
|
||||||
|
public Graphics2DProxy( Graphics2D delegate ) {
|
||||||
|
this.delegate = delegate;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Graphics create() {
|
||||||
|
return delegate.create();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Graphics create( int x, int y, int width, int height ) {
|
||||||
|
return delegate.create( x, y, width, height );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Color getColor() {
|
||||||
|
return delegate.getColor();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setColor( Color c ) {
|
||||||
|
delegate.setColor( c );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setPaintMode() {
|
||||||
|
delegate.setPaintMode();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setXORMode( Color c1 ) {
|
||||||
|
delegate.setXORMode( c1 );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Font getFont() {
|
||||||
|
return delegate.getFont();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setFont( Font font ) {
|
||||||
|
delegate.setFont( font );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public FontMetrics getFontMetrics() {
|
||||||
|
return delegate.getFontMetrics();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public FontMetrics getFontMetrics( Font f ) {
|
||||||
|
return delegate.getFontMetrics( f );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Rectangle getClipBounds() {
|
||||||
|
return delegate.getClipBounds();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void clipRect( int x, int y, int width, int height ) {
|
||||||
|
delegate.clipRect( x, y, width, height );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setClip( int x, int y, int width, int height ) {
|
||||||
|
delegate.setClip( x, y, width, height );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Shape getClip() {
|
||||||
|
return delegate.getClip();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setClip( Shape clip ) {
|
||||||
|
delegate.setClip( clip );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void copyArea( int x, int y, int width, int height, int dx, int dy ) {
|
||||||
|
delegate.copyArea( x, y, width, height, dx, dy );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void drawLine( int x1, int y1, int x2, int y2 ) {
|
||||||
|
delegate.drawLine( x1, y1, x2, y2 );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void fillRect( int x, int y, int width, int height ) {
|
||||||
|
delegate.fillRect( x, y, width, height );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void drawRect( int x, int y, int width, int height ) {
|
||||||
|
delegate.drawRect( x, y, width, height );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void clearRect( int x, int y, int width, int height ) {
|
||||||
|
delegate.clearRect( x, y, width, height );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void drawRoundRect( int x, int y, int width, int height, int arcWidth, int arcHeight ) {
|
||||||
|
delegate.drawRoundRect( x, y, width, height, arcWidth, arcHeight );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void fillRoundRect( int x, int y, int width, int height, int arcWidth, int arcHeight ) {
|
||||||
|
delegate.fillRoundRect( x, y, width, height, arcWidth, arcHeight );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void drawOval( int x, int y, int width, int height ) {
|
||||||
|
delegate.drawOval( x, y, width, height );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void fillOval( int x, int y, int width, int height ) {
|
||||||
|
delegate.fillOval( x, y, width, height );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void drawArc( int x, int y, int width, int height, int startAngle, int arcAngle ) {
|
||||||
|
delegate.drawArc( x, y, width, height, startAngle, arcAngle );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void fillArc( int x, int y, int width, int height, int startAngle, int arcAngle ) {
|
||||||
|
delegate.fillArc( x, y, width, height, startAngle, arcAngle );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void drawPolyline( int[] xPoints, int[] yPoints, int nPoints ) {
|
||||||
|
delegate.drawPolyline( xPoints, yPoints, nPoints );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void drawPolygon( int[] xPoints, int[] yPoints, int nPoints ) {
|
||||||
|
delegate.drawPolygon( xPoints, yPoints, nPoints );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void drawPolygon( Polygon p ) {
|
||||||
|
delegate.drawPolygon( p );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void fillPolygon( int[] xPoints, int[] yPoints, int nPoints ) {
|
||||||
|
delegate.fillPolygon( xPoints, yPoints, nPoints );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void fillPolygon( Polygon p ) {
|
||||||
|
delegate.fillPolygon( p );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void drawChars( char[] data, int offset, int length, int x, int y ) {
|
||||||
|
delegate.drawChars( data, offset, length, x, y );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void drawBytes( byte[] data, int offset, int length, int x, int y ) {
|
||||||
|
delegate.drawBytes( data, offset, length, x, y );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean drawImage( Image img, int x, int y, ImageObserver observer ) {
|
||||||
|
return delegate.drawImage( img, x, y, observer );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean drawImage( Image img, int x, int y, int width, int height, ImageObserver observer ) {
|
||||||
|
return delegate.drawImage( img, x, y, width, height, observer );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean drawImage( Image img, int x, int y, Color bgcolor, ImageObserver observer ) {
|
||||||
|
return delegate.drawImage( img, x, y, bgcolor, observer );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean drawImage( Image img, int x, int y, int width, int height, Color bgcolor, ImageObserver observer ) {
|
||||||
|
return delegate.drawImage( img, x, y, width, height, bgcolor, observer );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean drawImage( Image img, int dx1, int dy1, int dx2, int dy2, int sx1, int sy1, int sx2, int sy2, ImageObserver observer ) {
|
||||||
|
return delegate.drawImage( img, dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2, observer );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean drawImage( Image img, int dx1, int dy1, int dx2, int dy2, int sx1, int sy1, int sx2, int sy2, Color bgcolor, ImageObserver observer ) {
|
||||||
|
return delegate.drawImage( img, dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2, bgcolor, observer );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void dispose() {
|
||||||
|
delegate.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void finalize() {
|
||||||
|
delegate.finalize();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return delegate.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings( "deprecation" )
|
||||||
|
@Override
|
||||||
|
public Rectangle getClipRect() {
|
||||||
|
return delegate.getClipRect();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hitClip( int x, int y, int width, int height ) {
|
||||||
|
return delegate.hitClip( x, y, width, height );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Rectangle getClipBounds( Rectangle r ) {
|
||||||
|
return delegate.getClipBounds( r );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void draw3DRect( int x, int y, int width, int height, boolean raised ) {
|
||||||
|
delegate.draw3DRect( x, y, width, height, raised );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void fill3DRect( int x, int y, int width, int height, boolean raised ) {
|
||||||
|
delegate.fill3DRect( x, y, width, height, raised );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void draw( Shape s ) {
|
||||||
|
delegate.draw( s );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean drawImage( Image img, AffineTransform xform, ImageObserver obs ) {
|
||||||
|
return delegate.drawImage( img, xform, obs );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void drawImage( BufferedImage img, BufferedImageOp op, int x, int y ) {
|
||||||
|
delegate.drawImage( img, op, x, y );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void drawRenderedImage( RenderedImage img, AffineTransform xform ) {
|
||||||
|
delegate.drawRenderedImage( img, xform );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void drawRenderableImage( RenderableImage img, AffineTransform xform ) {
|
||||||
|
delegate.drawRenderableImage( img, xform );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void drawString( String str, int x, int y ) {
|
||||||
|
delegate.drawString( str, x, y );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void drawString( String str, float x, float y ) {
|
||||||
|
delegate.drawString( str, x, y );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void drawString( AttributedCharacterIterator iterator, int x, int y ) {
|
||||||
|
delegate.drawString( iterator, x, y );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void drawString( AttributedCharacterIterator iterator, float x, float y ) {
|
||||||
|
delegate.drawString( iterator, x, y );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void drawGlyphVector( GlyphVector g, float x, float y ) {
|
||||||
|
delegate.drawGlyphVector( g, x, y );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void fill( Shape s ) {
|
||||||
|
delegate.fill( s );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hit( Rectangle rect, Shape s, boolean onStroke ) {
|
||||||
|
return delegate.hit( rect, s, onStroke );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public GraphicsConfiguration getDeviceConfiguration() {
|
||||||
|
return delegate.getDeviceConfiguration();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setComposite( Composite comp ) {
|
||||||
|
delegate.setComposite( comp );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setPaint( Paint paint ) {
|
||||||
|
delegate.setPaint( paint );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setStroke( Stroke s ) {
|
||||||
|
delegate.setStroke( s );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setRenderingHint( Key hintKey, Object hintValue ) {
|
||||||
|
delegate.setRenderingHint( hintKey, hintValue );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object getRenderingHint( Key hintKey ) {
|
||||||
|
return delegate.getRenderingHint( hintKey );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setRenderingHints( Map<?, ?> hints ) {
|
||||||
|
delegate.setRenderingHints( hints );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addRenderingHints( Map<?, ?> hints ) {
|
||||||
|
delegate.addRenderingHints( hints );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public RenderingHints getRenderingHints() {
|
||||||
|
return delegate.getRenderingHints();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void translate( int x, int y ) {
|
||||||
|
delegate.translate( x, y );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void translate( double tx, double ty ) {
|
||||||
|
delegate.translate( tx, ty );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void rotate( double theta ) {
|
||||||
|
delegate.rotate( theta );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void rotate( double theta, double x, double y ) {
|
||||||
|
delegate.rotate( theta, x, y );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void scale( double sx, double sy ) {
|
||||||
|
delegate.scale( sx, sy );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void shear( double shx, double shy ) {
|
||||||
|
delegate.shear( shx, shy );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void transform( AffineTransform Tx ) {
|
||||||
|
delegate.transform( Tx );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setTransform( AffineTransform Tx ) {
|
||||||
|
delegate.setTransform( Tx );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AffineTransform getTransform() {
|
||||||
|
return delegate.getTransform();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Paint getPaint() {
|
||||||
|
return delegate.getPaint();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Composite getComposite() {
|
||||||
|
return delegate.getComposite();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setBackground( Color color ) {
|
||||||
|
delegate.setBackground( color );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Color getBackground() {
|
||||||
|
return delegate.getBackground();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Stroke getStroke() {
|
||||||
|
return delegate.getStroke();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void clip( Shape s ) {
|
||||||
|
delegate.clip( s );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public FontRenderContext getFontRenderContext() {
|
||||||
|
return delegate.getFontRenderContext();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,87 @@
|
|||||||
|
// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||||
|
package com.formdev.flatlaf.util;
|
||||||
|
|
||||||
|
import java.awt.image.RGBImageFilter;
|
||||||
|
|
||||||
|
// based on https://github.com/JetBrains/intellij-community/blob/3840eab54746f5c4f301bb3ac78f00a980b5fd6e/platform/util/ui/src/com/intellij/util/ui/UIUtil.java#L253-L347
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An image filter that turns an image into a grayscale image.
|
||||||
|
* Used for icons in disabled buttons and labels.
|
||||||
|
*/
|
||||||
|
public class GrayFilter
|
||||||
|
extends RGBImageFilter
|
||||||
|
{
|
||||||
|
private final float brightness;
|
||||||
|
private final float contrast;
|
||||||
|
private final int alpha;
|
||||||
|
|
||||||
|
private final int origContrast;
|
||||||
|
private final int origBrightness;
|
||||||
|
|
||||||
|
public static GrayFilter createDisabledIconFilter( boolean dark ) {
|
||||||
|
return dark
|
||||||
|
? new GrayFilter( -20, -70, 100 )
|
||||||
|
: new GrayFilter( 25, -25, 100 );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param brightness in range [-100..100] where 0 has no effect
|
||||||
|
* @param contrast in range [-100..100] where 0 has no effect
|
||||||
|
* @param alpha in range [0..100] where 0 is transparent, 100 has no effect
|
||||||
|
*/
|
||||||
|
public GrayFilter( int brightness, int contrast, int alpha ) {
|
||||||
|
this.origBrightness = Math.max( -100, Math.min( 100, brightness ) );
|
||||||
|
this.origContrast = Math.max( -100, Math.min( 100, contrast ) );
|
||||||
|
this.alpha = Math.max( 0, Math.min( 100, alpha ) );
|
||||||
|
|
||||||
|
this.brightness = (float) (Math.pow( origBrightness, 3 ) / (100f * 100f)); // cubic in [0..100]
|
||||||
|
this.contrast = origContrast / 100f;
|
||||||
|
|
||||||
|
canFilterIndexColorModel = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public GrayFilter() {
|
||||||
|
this( 0, 0, 100 );
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getBrightness() {
|
||||||
|
return origBrightness;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getContrast() {
|
||||||
|
return origContrast;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getAlpha() {
|
||||||
|
return alpha;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int filterRGB( int x, int y, int rgb ) {
|
||||||
|
// use NTSC conversion formula
|
||||||
|
int gray = (int)(
|
||||||
|
0.30 * (rgb >> 16 & 0xff) +
|
||||||
|
0.59 * (rgb >> 8 & 0xff) +
|
||||||
|
0.11 * (rgb & 0xff));
|
||||||
|
|
||||||
|
if( brightness >= 0 )
|
||||||
|
gray = (int) ((gray + brightness * 255) / (1 + brightness));
|
||||||
|
else
|
||||||
|
gray = (int) (gray / (1 - brightness));
|
||||||
|
|
||||||
|
if( contrast >= 0 ) {
|
||||||
|
if( gray >= 127 )
|
||||||
|
gray = (int) (gray + (255 - gray) * contrast);
|
||||||
|
else
|
||||||
|
gray = (int) (gray - gray * contrast);
|
||||||
|
} else
|
||||||
|
gray = (int) (127 + (gray - 127) * (contrast + 1));
|
||||||
|
|
||||||
|
int a = (alpha != 100)
|
||||||
|
? (((rgb >> 24) & 0xff) * alpha / 100) << 24
|
||||||
|
: (rgb & 0xff000000);
|
||||||
|
|
||||||
|
return a | (gray << 16) | (gray << 8) | gray;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -34,10 +34,6 @@ public class HiDPIUtils
|
|||||||
paintAtScale1x( g, 0, 0, c.getWidth(), c.getHeight(), painter );
|
paintAtScale1x( g, 0, 0, c.getWidth(), c.getHeight(), painter );
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void paintAtScale1x( Graphics2D g, int x, int y, int width, int height, Painter painter ) {
|
|
||||||
paintAtScale1x( g, x, y, width, height, UIScale.getSystemScaleFactor( g ), painter );
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Paint at system scale factor 1x to avoid rounding issues at 125%, 150% and 175% scaling.
|
* Paint at system scale factor 1x to avoid rounding issues at 125%, 150% and 175% scaling.
|
||||||
* <p>
|
* <p>
|
||||||
@@ -46,38 +42,29 @@ public class HiDPIUtils
|
|||||||
* <p>
|
* <p>
|
||||||
* Uses the same scaling calculation as the JRE uses.
|
* Uses the same scaling calculation as the JRE uses.
|
||||||
*/
|
*/
|
||||||
public static void paintAtScale1x( Graphics2D g, int x, int y, int width, int height,
|
public static void paintAtScale1x( Graphics2D g, int x, int y, int width, int height, Painter painter ) {
|
||||||
double scaleFactor, Painter painter )
|
// save original transform
|
||||||
{
|
AffineTransform transform = g.getTransform();
|
||||||
if( scaleFactor == 1 ) {
|
|
||||||
|
// check whether scaled
|
||||||
|
if( transform.getScaleX() == 1 && transform.getScaleY() == 1 ) {
|
||||||
painter.paint( g, x, y, width, height, 1 );
|
painter.paint( g, x, y, width, height, 1 );
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// save original transform
|
|
||||||
AffineTransform transform = g.getTransform();
|
|
||||||
|
|
||||||
// scale rectangle
|
// scale rectangle
|
||||||
Rectangle2D.Double scaledRect = scale( transform, x, y, width, height );
|
Rectangle2D.Double scaledRect = scale( transform, x, y, width, height );
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// unscale to factor 1.0
|
// unscale to factor 1.0 and move origin (to whole numbers)
|
||||||
double scale = 1.0 / scaleFactor;
|
g.setTransform( new AffineTransform( 1, 0, 0, 1,
|
||||||
g.scale( scale, scale );
|
Math.floor( scaledRect.x ), Math.floor( scaledRect.y ) ) );
|
||||||
|
|
||||||
// compute origin delta x/y
|
|
||||||
double dx = Math.floor( scaledRect.x ) - transform.getTranslateX();
|
|
||||||
double dy = Math.floor( scaledRect.y ) - transform.getTranslateY();
|
|
||||||
|
|
||||||
// move origin to make sure that origin x/y are at whole numbers
|
|
||||||
if( dx != 0 || dy != 0 )
|
|
||||||
g.translate( dx, dy );
|
|
||||||
|
|
||||||
int swidth = (int) scaledRect.width;
|
int swidth = (int) scaledRect.width;
|
||||||
int sheight = (int) scaledRect.height;
|
int sheight = (int) scaledRect.height;
|
||||||
|
|
||||||
// paint
|
// paint
|
||||||
painter.paint( g, 0, 0, swidth, sheight, scaleFactor );
|
painter.paint( g, 0, 0, swidth, sheight, transform.getScaleX() );
|
||||||
} finally {
|
} finally {
|
||||||
// restore original transform
|
// restore original transform
|
||||||
g.setTransform( transform );
|
g.setTransform( transform );
|
||||||
|
|||||||
@@ -20,7 +20,10 @@ import java.awt.Graphics;
|
|||||||
import java.awt.Graphics2D;
|
import java.awt.Graphics2D;
|
||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import java.util.logging.Logger;
|
||||||
import javax.swing.JComponent;
|
import javax.swing.JComponent;
|
||||||
|
import com.formdev.flatlaf.FlatLaf;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides Java version compatibility methods.
|
* Provides Java version compatibility methods.
|
||||||
@@ -52,7 +55,7 @@ public class JavaCompatibility
|
|||||||
? new Class[] { JComponent.class, Graphics2D.class, String.class, int.class, float.class, float.class }
|
? new Class[] { JComponent.class, Graphics2D.class, String.class, int.class, float.class, float.class }
|
||||||
: new Class[] { JComponent.class, Graphics.class, String.class, int.class, int.class, int.class } );
|
: new Class[] { JComponent.class, Graphics.class, String.class, int.class, int.class, int.class } );
|
||||||
} catch( Exception ex ) {
|
} catch( Exception ex ) {
|
||||||
ex.printStackTrace();
|
Logger.getLogger( FlatLaf.class.getName() ).log( Level.SEVERE, null, ex );
|
||||||
throw new RuntimeException( ex );
|
throw new RuntimeException( ex );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -64,7 +67,7 @@ public class JavaCompatibility
|
|||||||
else
|
else
|
||||||
drawStringUnderlineCharAtMethod.invoke( null, c, g, text, underlinedIndex, x, y );
|
drawStringUnderlineCharAtMethod.invoke( null, c, g, text, underlinedIndex, x, y );
|
||||||
} catch( IllegalAccessException | IllegalArgumentException | InvocationTargetException ex ) {
|
} catch( IllegalAccessException | IllegalArgumentException | InvocationTargetException ex ) {
|
||||||
ex.printStackTrace();
|
Logger.getLogger( FlatLaf.class.getName() ).log( Level.SEVERE, null, ex );
|
||||||
throw new RuntimeException( ex );
|
throw new RuntimeException( ex );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,54 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2020 FormDev Software GmbH
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.formdev.flatlaf.util;
|
||||||
|
|
||||||
|
import java.awt.Image;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Support for multi-resolution images available since Java 9.
|
||||||
|
*
|
||||||
|
* @author Karl Tauber
|
||||||
|
*/
|
||||||
|
public class MultiResolutionImageSupport
|
||||||
|
{
|
||||||
|
public static boolean isAvailable() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isMultiResolutionImage( Image image ) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Image create( int baseImageIndex, Image... resolutionVariants ) {
|
||||||
|
return resolutionVariants[baseImageIndex];
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Image map( Image image, Function<Image, Image> mapper ) {
|
||||||
|
return mapper.apply( image );
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Image getResolutionVariant( Image image, int destImageWidth, int destImageHeight ) {
|
||||||
|
return image;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<Image> getResolutionVariants( Image image ) {
|
||||||
|
return Collections.singletonList( image );
|
||||||
|
}
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user