mirror of
https://github.com/JFormDesigner/FlatLaf.git
synced 2026-02-11 22:47:13 -06:00
Compare commits
144 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4f5a3e8d8b | ||
|
|
614ac956de | ||
|
|
c228362c01 | ||
|
|
f6c5db07f2 | ||
|
|
78e7839213 | ||
|
|
86a4f306c6 | ||
|
|
0e523f1193 | ||
|
|
9041a16b22 | ||
|
|
596ff3382d | ||
|
|
cbd80252ed | ||
|
|
bcd7a7e3dd | ||
|
|
9c98f1a553 | ||
|
|
c3d214aa23 | ||
|
|
d301f6e104 | ||
|
|
eb9fa585f7 | ||
|
|
16c6ffb032 | ||
|
|
7858e42e37 | ||
|
|
30132aa6b0 | ||
|
|
bf4d4cc2c5 | ||
|
|
9f0554c883 | ||
|
|
218ea6ce47 | ||
|
|
0baae7da8b | ||
|
|
fb4576fc1b | ||
|
|
16f3f9e6ff | ||
|
|
fee7cf6265 | ||
|
|
2dd75c4a64 | ||
|
|
d2f46cd0b5 | ||
|
|
10914083e6 | ||
|
|
5d167da55e | ||
|
|
b381e20e57 | ||
|
|
475cc9a9a5 | ||
|
|
264d6fbd6d | ||
|
|
2826cf379b | ||
|
|
d28745df29 | ||
|
|
94f9e4a1be | ||
|
|
ec547e1d65 | ||
|
|
61d4eb649b | ||
|
|
52ad15e375 | ||
|
|
ff00a6c0f0 | ||
|
|
9b1ebd658d | ||
|
|
f842530537 | ||
|
|
63077bbb19 | ||
|
|
4dad337377 | ||
|
|
10a965d765 | ||
|
|
3e9c9c9066 | ||
|
|
8b5a738e65 | ||
|
|
2c041dce3a | ||
|
|
ef151c68f4 | ||
|
|
52feaac92a | ||
|
|
cddbb3d7d4 | ||
|
|
42764550e6 | ||
|
|
6ee737b314 | ||
|
|
f460ef7685 | ||
|
|
9977bcb468 | ||
|
|
7437d984c7 | ||
|
|
5cd0b2403c | ||
|
|
a372da22f3 | ||
|
|
8b10d3ba5a | ||
|
|
a8b15c6a12 | ||
|
|
23bac7e5fd | ||
|
|
b82ee2ef61 | ||
|
|
b7761f4b71 | ||
|
|
f9a4f9771c | ||
|
|
d2acb2c98a | ||
|
|
d60bd5df14 | ||
|
|
73b6ca3762 | ||
|
|
6c18431a30 | ||
|
|
a49d20249f | ||
|
|
ad384acd57 | ||
|
|
69851b7f3a | ||
|
|
92b53bf0df | ||
|
|
93e0496fd2 | ||
|
|
5151951f46 | ||
|
|
58dbccec2d | ||
|
|
90de14d013 | ||
|
|
5f961618bf | ||
|
|
37c375e2fa | ||
|
|
1758c175ed | ||
|
|
96f2a02cfa | ||
|
|
96d4bda6c8 | ||
|
|
02cf6050a1 | ||
|
|
38cf32a2e9 | ||
|
|
2ae7589d14 | ||
|
|
bcb2e1f0a1 | ||
|
|
14932d3f07 | ||
|
|
c3b9dc397d | ||
|
|
58b653f55d | ||
|
|
1dcdc42dde | ||
|
|
58a0a16985 | ||
|
|
a117243f14 | ||
|
|
22411060be | ||
|
|
045263ae58 | ||
|
|
024b6daaf6 | ||
|
|
bd5512c121 | ||
|
|
9afce83a02 | ||
|
|
07a8bd9486 | ||
|
|
bcdc0a8fce | ||
|
|
b295809432 | ||
|
|
52763ab932 | ||
|
|
99666265c9 | ||
|
|
af3e280d74 | ||
|
|
b57e4c0565 | ||
|
|
aca9931560 | ||
|
|
d09e166e4a | ||
|
|
68a7a60ff2 | ||
|
|
f21261914b | ||
|
|
7b11339fdc | ||
|
|
081fd43d98 | ||
|
|
ef2eedfc7c | ||
|
|
0dba9265be | ||
|
|
301aae9b8e | ||
|
|
c63f4e9662 | ||
|
|
47508dc6ac | ||
|
|
3a8879608a | ||
|
|
b221889549 | ||
|
|
c00d99b85f | ||
|
|
0bf87b753d | ||
|
|
53f2730064 | ||
|
|
d487c3b005 | ||
|
|
fef6ae7ff7 | ||
|
|
f6b42754de | ||
|
|
2ae9bb381d | ||
|
|
53bde84710 | ||
|
|
d006ac27ff | ||
|
|
c478d28b71 | ||
|
|
99f7b9ad84 | ||
|
|
ab58101ce3 | ||
|
|
d8f3682dc0 | ||
|
|
1fec7ba553 | ||
|
|
418f55f34e | ||
|
|
05d795b2ae | ||
|
|
a365b750d9 | ||
|
|
0aecfb565f | ||
|
|
0cf4edd9e5 | ||
|
|
dd7b7c6aef | ||
|
|
0bd677c46b | ||
|
|
1a131d5206 | ||
|
|
016e515ae2 | ||
|
|
456ceb3c58 | ||
|
|
2169be1b45 | ||
|
|
49eb0b0201 | ||
|
|
2e222bcdea | ||
|
|
c7fa475128 | ||
|
|
4174b065f3 |
14
.github/workflows/ci.yml
vendored
14
.github/workflows/ci.yml
vendored
@@ -28,13 +28,13 @@ jobs:
|
|||||||
- 17 # LTS
|
- 17 # LTS
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
- uses: gradle/wrapper-validation-action@v1
|
- uses: gradle/wrapper-validation-action@v1
|
||||||
if: matrix.java == '8'
|
if: matrix.java == '8'
|
||||||
|
|
||||||
- name: Setup Java ${{ matrix.java }}
|
- name: Setup Java ${{ matrix.java }}
|
||||||
uses: actions/setup-java@v2
|
uses: actions/setup-java@v3
|
||||||
with:
|
with:
|
||||||
java-version: ${{ matrix.java }}
|
java-version: ${{ matrix.java }}
|
||||||
distribution: adopt # Java 8 and 11 are pre-installed on ubuntu-latest
|
distribution: adopt # Java 8 and 11 are pre-installed on ubuntu-latest
|
||||||
@@ -44,7 +44,7 @@ jobs:
|
|||||||
run: ./gradlew build
|
run: ./gradlew build
|
||||||
|
|
||||||
- name: Upload artifacts
|
- name: Upload artifacts
|
||||||
uses: actions/upload-artifact@v2
|
uses: actions/upload-artifact@v3
|
||||||
if: matrix.java == '11'
|
if: matrix.java == '11'
|
||||||
with:
|
with:
|
||||||
name: FlatLaf-build-artifacts
|
name: FlatLaf-build-artifacts
|
||||||
@@ -63,10 +63,10 @@ jobs:
|
|||||||
github.repository == 'JFormDesigner/FlatLaf'
|
github.repository == 'JFormDesigner/FlatLaf'
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
- name: Setup Java 11
|
- name: Setup Java 11
|
||||||
uses: actions/setup-java@v2
|
uses: actions/setup-java@v3
|
||||||
with:
|
with:
|
||||||
java-version: 11
|
java-version: 11
|
||||||
distribution: adopt # pre-installed on ubuntu-latest
|
distribution: adopt # pre-installed on ubuntu-latest
|
||||||
@@ -99,10 +99,10 @@ jobs:
|
|||||||
github.repository == 'JFormDesigner/FlatLaf'
|
github.repository == 'JFormDesigner/FlatLaf'
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
- name: Setup Java 11
|
- name: Setup Java 11
|
||||||
uses: actions/setup-java@v2
|
uses: actions/setup-java@v3
|
||||||
with:
|
with:
|
||||||
java-version: 11
|
java-version: 11
|
||||||
distribution: adopt # pre-installed on ubuntu-latest
|
distribution: adopt # pre-installed on ubuntu-latest
|
||||||
|
|||||||
29
.github/workflows/natives.yml
vendored
29
.github/workflows/natives.yml
vendored
@@ -9,41 +9,48 @@ on:
|
|||||||
tags:
|
tags:
|
||||||
- '[0-9]*'
|
- '[0-9]*'
|
||||||
paths:
|
paths:
|
||||||
- 'flatlaf-natives/flatlaf-natives-windows/**'
|
- 'flatlaf-natives/**'
|
||||||
- '.github/workflows/natives.yml'
|
- '.github/workflows/natives.yml'
|
||||||
- 'gradle/wrapper/gradle-wrapper.properties'
|
- 'gradle/wrapper/gradle-wrapper.properties'
|
||||||
pull_request:
|
pull_request:
|
||||||
branches:
|
branches:
|
||||||
- '*'
|
- '*'
|
||||||
paths:
|
paths:
|
||||||
- 'flatlaf-natives/flatlaf-natives-windows/**'
|
- 'flatlaf-natives/**'
|
||||||
- '.github/workflows/natives.yml'
|
- '.github/workflows/natives.yml'
|
||||||
- 'gradle/wrapper/gradle-wrapper.properties'
|
- 'gradle/wrapper/gradle-wrapper.properties'
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
Windows:
|
Natives:
|
||||||
runs-on: windows-latest
|
strategy:
|
||||||
|
matrix:
|
||||||
|
os:
|
||||||
|
- windows
|
||||||
|
- ubuntu
|
||||||
|
|
||||||
|
runs-on: ${{ matrix.os }}-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
- uses: gradle/wrapper-validation-action@v1
|
- uses: gradle/wrapper-validation-action@v1
|
||||||
|
|
||||||
- name: Setup Java 11
|
- name: Setup Java 11
|
||||||
uses: actions/setup-java@v2
|
uses: actions/setup-java@v3
|
||||||
with:
|
with:
|
||||||
java-version: 11
|
java-version: 11
|
||||||
distribution: adopt
|
distribution: adopt
|
||||||
cache: gradle
|
cache: gradle
|
||||||
|
|
||||||
- name: Build with Gradle
|
- name: Build with Gradle
|
||||||
# --no-daemon is necessary on Windows otherwise caching Gradle would fail with:
|
# --no-daemon is necessary on Windows otherwise caching Gradle would fail with:
|
||||||
# tar.exe: Couldn't open ~/.gradle/caches/modules-2/modules-2.lock: Permission denied
|
# tar.exe: Couldn't open ~/.gradle/caches/modules-2/modules-2.lock: Permission denied
|
||||||
run: ./gradlew :flatlaf-natives-windows:build --no-daemon
|
run: ./gradlew build-natives --no-daemon
|
||||||
|
|
||||||
- name: Upload artifacts
|
- name: Upload artifacts
|
||||||
uses: actions/upload-artifact@v2
|
uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: FlatLaf-natives-windows-build-artifacts
|
name: FlatLaf-natives-build-artifacts-${{ matrix.os }}
|
||||||
path: |
|
path: |
|
||||||
flatlaf-natives/flatlaf-natives-windows/build
|
flatlaf-core/src/main/resources/com/formdev/flatlaf/natives
|
||||||
|
flatlaf-natives/flatlaf-natives-*/build
|
||||||
|
|||||||
174
CHANGELOG.md
174
CHANGELOG.md
@@ -1,6 +1,178 @@
|
|||||||
FlatLaf Change Log
|
FlatLaf Change Log
|
||||||
==================
|
==================
|
||||||
|
|
||||||
|
## 2.5
|
||||||
|
|
||||||
|
#### New features and improvements
|
||||||
|
|
||||||
|
- Linux: Use X11 window manager events to move window and to show window menu
|
||||||
|
(right-click on window title bar), if custom window decorations are enabled.
|
||||||
|
This gives FlatLaf windows a more "native" feeling. (issue #482)
|
||||||
|
- MenuBar: Support different menu selection style UI defaults for `MenuBar` and
|
||||||
|
`MenuItem`. (issue #587)
|
||||||
|
- MenuBar: Top level menus now use `MenuBar.font` instead of `Menu.font`. (issue
|
||||||
|
#589)
|
||||||
|
- PasswordField: Reveal button is now hidden (and turned off) if password field
|
||||||
|
is disabled. (issue #501)
|
||||||
|
- TabbedPane: New option to disable tab run rotation in wrap layout. Set UI
|
||||||
|
value `TabbedPane.rotateTabRuns` to `false`. (issue #574)
|
||||||
|
- Window decorations:
|
||||||
|
- Added client property to mark components in embedded menu bar as "caption"
|
||||||
|
(allow moving window). (issue #569)
|
||||||
|
- Option to show window icon only in frames, but not in dialogs. Set UI value
|
||||||
|
`TitlePane.showIconInDialogs` to `false`. (issue #589)
|
||||||
|
- Added UI value `TitlePane.font` to customize window title font. (issue #589)
|
||||||
|
- Added system property `flatlaf.updateUIOnSystemFontChange` to allow disabling
|
||||||
|
automatic UI update when system font changes. (issue #580)
|
||||||
|
|
||||||
|
#### Fixed bugs
|
||||||
|
|
||||||
|
- Fixed missing UI value `MenuItem.acceleratorDelimiter` on macOS. (was `null`,
|
||||||
|
is now an empty string)
|
||||||
|
- Fixed possible exception in `FlatUIUtils.resetRenderingHints()`. (issue #575)
|
||||||
|
- Fixed AWT components on macOS, which use Swing components internally. (issue
|
||||||
|
#583)
|
||||||
|
- SwingX: Fixed missing highlighting of "today" in `JXMonthView` and
|
||||||
|
`JXDatePicker`.
|
||||||
|
|
||||||
|
|
||||||
|
## 2.4
|
||||||
|
|
||||||
|
#### New features and improvements
|
||||||
|
|
||||||
|
- Native window decorations (Windows 10/11 only):
|
||||||
|
- There is now a small area at top of the embedded menu bar to resize the
|
||||||
|
window.
|
||||||
|
- Improved window title bar layout for small window widths:
|
||||||
|
- Width of iconify/maximize/close buttons is reduced (if necessary) to give
|
||||||
|
more space to embedded menu bar and title.
|
||||||
|
- Window title now has a minimum width to always allow moving window
|
||||||
|
(click-and-drag on window title). Instead, embedded menu bar is made
|
||||||
|
smaller.
|
||||||
|
- Option to show window icon beside window title, if menu bar is embedded or
|
||||||
|
title is centered. Set UI value `TitlePane.showIconBesideTitle` to `true`.
|
||||||
|
- No longer reduce height of window title bar if it has an embedded menu bar
|
||||||
|
and is maximized.
|
||||||
|
|
||||||
|
#### Fixed bugs
|
||||||
|
|
||||||
|
- ComboBox: Fixed vertical alignment of text in popup list with text in combo
|
||||||
|
box in IntelliJ/Darcula themes.
|
||||||
|
- Menus: Fixed application freeze under very special conditions (invoking
|
||||||
|
`FlatLaf.initialize()` twice in NetBeans GUI builder) and using menu that has
|
||||||
|
submenus. See
|
||||||
|
[NetBeans issue #4231](https://github.com/apache/netbeans/issues/4231#issuecomment-1179611682)
|
||||||
|
for details.
|
||||||
|
- MenuItem: Fixed sometimes wrapped HTML text on HiDPI screens on Windows.
|
||||||
|
- TableHeader: Fixed exception when changing table structure (e.g. removing
|
||||||
|
column) from a table header popup menu action. (issue #532)
|
||||||
|
- `HiDPIUtils.paintAtScale1x()` now supports rotated graphics. (issue #557)
|
||||||
|
- Typography: No longer use `Consolas` or `Courier New` as monospaced font on
|
||||||
|
Windows because they have bad vertically placement.
|
||||||
|
- Native window decorations (Windows 10/11 only):
|
||||||
|
- Do not center window title if embedded menu bar is empty or has no menus at
|
||||||
|
left side, but some components at right side. (issue #558)
|
||||||
|
- Do not use window decorations if system property `sun.java2d.opengl` is
|
||||||
|
`true` on Windows 10. (issue #540)
|
||||||
|
- Fixed missing top window border in dark themes if window drop shadows are
|
||||||
|
disabled in system settings. (issue #554; Windows 10 only)
|
||||||
|
- Right-to-left component orientation of title bar was lost when switching
|
||||||
|
theme.
|
||||||
|
|
||||||
|
|
||||||
|
## 2.3
|
||||||
|
|
||||||
|
#### New features and improvements
|
||||||
|
|
||||||
|
- FileChooser: Added (optional) shortcuts panel. On Windows it contains "Recent
|
||||||
|
Items", "Desktop", "Documents", "This PC" and "Network". On macOS and Linux it
|
||||||
|
is empty/hidden. (issue #100)
|
||||||
|
- Button and ToggleButton: Added missing foreground colors for hover, pressed,
|
||||||
|
focused and selected states. (issue #535)
|
||||||
|
- Table: Optionally paint alternating rows below table if table is smaller than
|
||||||
|
scroll pane. Set UI value `Table.paintOutsideAlternateRows` to `true`.
|
||||||
|
Requires that `Table.alternateRowColor` is set to a color. (issue #504)
|
||||||
|
- ToggleButton: Made the underline placement of tab-style toggle buttons
|
||||||
|
configurable. (PR #530; issue #529)
|
||||||
|
- Added spanish translation. (PR #525)
|
||||||
|
|
||||||
|
#### Fixed bugs
|
||||||
|
|
||||||
|
- IntelliJ Themes: Fixed `TitledBorder` text color in "Monokai Pro" theme.
|
||||||
|
(issue #524)
|
||||||
|
|
||||||
|
|
||||||
|
## 2.2
|
||||||
|
|
||||||
|
#### New features and improvements
|
||||||
|
|
||||||
|
- SplitPane: Allow limiting one-touch expanding to a single side (set client
|
||||||
|
property `JSplitPane.expandableSide` to `"left"` or `"right"`). (issue #355)
|
||||||
|
- TabbedPane: Selected tab underline color now changes depending on whether the
|
||||||
|
focus is within the tab content. (issue #398)
|
||||||
|
- IntelliJ Themes:
|
||||||
|
- Added "Monokai Pro" and "Xcode-Dark" themes.
|
||||||
|
- TabbedPane now use different background color for selected tabs in all "Arc"
|
||||||
|
themes, in "Hiberbee Dark" and in all "Material UI Lite" themes.
|
||||||
|
|
||||||
|
#### Fixed bugs
|
||||||
|
|
||||||
|
- Native window decorations (Windows 10/11 only): Fixed wrong window title
|
||||||
|
character encoding used in Windows taskbar. (issue #502)
|
||||||
|
- Button: Fixed icon layout and preferred width of default buttons that use bold
|
||||||
|
font. (issue #506)
|
||||||
|
- FileChooser: Enabled full row selection for details view to fix alternate row
|
||||||
|
coloring. (issue #512)
|
||||||
|
- SplitPane: Fixed `StackOverflowError` caused by layout loop that may occur
|
||||||
|
under special circumstances. (issue #513)
|
||||||
|
- Table: Slightly changed grid colors to make grid better recognizable. (issue
|
||||||
|
#514)
|
||||||
|
- ToolBar: Fixed endless loop in focus navigation that may occur under special
|
||||||
|
circumstances. (issue #505)
|
||||||
|
- IntelliJ Themes: `Component.accentColor` UI property now has useful theme
|
||||||
|
specific values. (issue #507)
|
||||||
|
|
||||||
|
|
||||||
|
## 2.1
|
||||||
|
|
||||||
|
#### New features and improvements
|
||||||
|
|
||||||
|
- Menus: Improved usability of submenus. (PR #490; issue #247)
|
||||||
|
- Menus: Scroll large menus using mouse wheel or up/down arrows. (issue #225)
|
||||||
|
- Linux: Support using custom window decorations. Enable with
|
||||||
|
`JFrame.setDefaultLookAndFeelDecorated(true)` and
|
||||||
|
`JDialog.setDefaultLookAndFeelDecorated(true)` before creating a window.
|
||||||
|
(issue #482)
|
||||||
|
- ScrollBar: Added UI value `ScrollBar.minimumButtonSize` to specify minimum
|
||||||
|
scroll arrow button size (if shown). (issue #493)
|
||||||
|
|
||||||
|
#### Fixed bugs
|
||||||
|
|
||||||
|
- PasswordField: Fixed reveal button appearance in IntelliJ themes. (issue #494)
|
||||||
|
- ScrollBar: Center and scale arrows in scroll up/down buttons (if shown).
|
||||||
|
(issue #493)
|
||||||
|
- TextArea, TextPane and EditorPane: No longer select all text when component is
|
||||||
|
focused for the first time. (issue #498; regression in FlatLaf 2.0)
|
||||||
|
- TabbedPane: Disable all items in "Show Hidden Tabs" popup menu if tabbed pane
|
||||||
|
is disabled.
|
||||||
|
|
||||||
|
#### Incompatibilities
|
||||||
|
|
||||||
|
- Method `FlatUIUtils.paintArrow()` (and class `FlatArrowButton`) now paints
|
||||||
|
arrows one pixel smaller than before. To fix this, increase parameter
|
||||||
|
`arrowSize` by one.
|
||||||
|
|
||||||
|
|
||||||
|
## 2.0.2
|
||||||
|
|
||||||
|
- Native window decorations (Windows 10/11 only): Fixed rendering artifacts on
|
||||||
|
HiDPI screens when dragging window partly offscreen and back into screen
|
||||||
|
bounds. (issue #477)
|
||||||
|
- Repaint component when setting client property `JComponent.outline` (issue
|
||||||
|
#480).
|
||||||
|
- macOS: Fixed NPE when using some icons in main menu items. (issue #483)
|
||||||
|
|
||||||
|
|
||||||
## 2.0.1
|
## 2.0.1
|
||||||
|
|
||||||
- Fixed memory leak in Panel, Separator and ToolBarSeparator. (issue #471;
|
- Fixed memory leak in Panel, Separator and ToolBarSeparator. (issue #471;
|
||||||
@@ -70,7 +242,7 @@ FlatLaf Change Log
|
|||||||
- Possibility to hide window title bar icon (for single window set client
|
- Possibility to hide window title bar icon (for single window set client
|
||||||
property `JRootPane.titleBarShowIcon` to `false`; for all windows set UI
|
property `JRootPane.titleBarShowIcon` to `false`; for all windows set UI
|
||||||
value `TitlePane.showIcon` to `false`).
|
value `TitlePane.showIcon` to `false`).
|
||||||
- OptionPane: Hide window title bar icon by default. Can be be made visibly by
|
- OptionPane: Hide window title bar icon by default. Can be made visibly by
|
||||||
setting UI default `OptionPane.showIcon` to `true`. (issue #416)
|
setting UI default `OptionPane.showIcon` to `true`. (issue #416)
|
||||||
- No longer show the Java "duke/cup" icon if no window icon image is set.
|
- No longer show the Java "duke/cup" icon if no window icon image is set.
|
||||||
(issue #416)
|
(issue #416)
|
||||||
|
|||||||
@@ -126,6 +126,8 @@ Buzz
|
|||||||
Applications using FlatLaf
|
Applications using FlatLaf
|
||||||
--------------------------
|
--------------------------
|
||||||
|
|
||||||
|
-  [Ultorg](https://www.ultorg.com/) (**commercial**) - a
|
||||||
|
visual query system for relational databases
|
||||||
-  [MooInfo](https://github.com/rememberber/MooInfo) -
|
-  [MooInfo](https://github.com/rememberber/MooInfo) -
|
||||||
visual implementation of OSHI, to view information about the system and
|
visual implementation of OSHI, to view information about the system and
|
||||||
hardware
|
hardware
|
||||||
|
|||||||
@@ -14,8 +14,8 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
val releaseVersion = "2.0.1"
|
val releaseVersion = "2.5"
|
||||||
val developmentVersion = "2.1-SNAPSHOT"
|
val developmentVersion = "3.0-SNAPSHOT"
|
||||||
|
|
||||||
version = if( java.lang.Boolean.getBoolean( "release" ) ) releaseVersion else developmentVersion
|
version = if( java.lang.Boolean.getBoolean( "release" ) ) releaseVersion else developmentVersion
|
||||||
|
|
||||||
|
|||||||
46
buildSrc/src/main/kotlin/flatlaf-cpp-library.gradle.kts
Normal file
46
buildSrc/src/main/kotlin/flatlaf-cpp-library.gradle.kts
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2022 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 {
|
||||||
|
`cpp-library`
|
||||||
|
}
|
||||||
|
|
||||||
|
library {
|
||||||
|
// disable debuggable for release builds to make shared libraries smaller
|
||||||
|
binaries.configureEach( CppSharedLibrary::class ) {
|
||||||
|
with( compileTask.get() ) {
|
||||||
|
if( name.contains( "Release" ) )
|
||||||
|
isDebuggable = false
|
||||||
|
}
|
||||||
|
with( linkTask.get() ) {
|
||||||
|
if( name.contains( "Release" ) )
|
||||||
|
debuggable.set( false )
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks {
|
||||||
|
withType<CppCompile>().configureEach {
|
||||||
|
doFirst {
|
||||||
|
println( "Used Tool Chain:" )
|
||||||
|
println( " - ${toolChain.get()}" )
|
||||||
|
println( "Available Tool Chains:" )
|
||||||
|
toolChains.forEach {
|
||||||
|
println( " - $it" )
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
36
buildSrc/src/main/kotlin/flatlaf-jni-headers.gradle.kts
Normal file
36
buildSrc/src/main/kotlin/flatlaf-jni-headers.gradle.kts
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2022 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 JniHeadersExtension {
|
||||||
|
var headers: List<String> = emptyList()
|
||||||
|
}
|
||||||
|
|
||||||
|
val extension = project.extensions.create<JniHeadersExtension>( "flatlafJniHeaders" )
|
||||||
|
|
||||||
|
|
||||||
|
tasks {
|
||||||
|
register<Copy>( "jni-headers" ) {
|
||||||
|
// depend on :flatlaf-core:compileJava because it generates the JNI headers
|
||||||
|
dependsOn( ":flatlaf-core:compileJava" )
|
||||||
|
|
||||||
|
from( project( ":flatlaf-core" ).buildDir.resolve( "generated/jni-headers" ) )
|
||||||
|
into( "src/main/headers" )
|
||||||
|
include( extension.headers )
|
||||||
|
filter<org.apache.tools.ant.filters.FixCrLfFilter>(
|
||||||
|
"eol" to org.apache.tools.ant.filters.FixCrLfFilter.CrLf.newInstance( "lf" )
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -61,8 +61,6 @@ if( JavaVersion.current() >= JavaVersion.VERSION_1_9 ) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
jar {
|
jar {
|
||||||
manifest.attributes( "Multi-Release" to "true" )
|
|
||||||
|
|
||||||
from( sourceSets["module-info"].output ) {
|
from( sourceSets["module-info"].output ) {
|
||||||
include( "module-info.class" )
|
include( "module-info.class" )
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ dependencies {
|
|||||||
testRuntimeOnly( "org.junit.jupiter:junit-jupiter-engine" )
|
testRuntimeOnly( "org.junit.jupiter:junit-jupiter-engine" )
|
||||||
|
|
||||||
// https://github.com/jtulach/netbeans-apitest
|
// https://github.com/jtulach/netbeans-apitest
|
||||||
sigtest( "org.netbeans.tools:sigtest-maven-plugin:1.4" )
|
sigtest( "org.netbeans.tools:sigtest-maven-plugin:1.7" )
|
||||||
}
|
}
|
||||||
|
|
||||||
java {
|
java {
|
||||||
@@ -43,11 +43,6 @@ tasks {
|
|||||||
options.headerOutputDirectory.set( buildDir.resolve( "generated/jni-headers" ) )
|
options.headerOutputDirectory.set( buildDir.resolve( "generated/jni-headers" ) )
|
||||||
}
|
}
|
||||||
|
|
||||||
processResources {
|
|
||||||
// build native libraries
|
|
||||||
dependsOn( ":flatlaf-natives-windows:assemble" )
|
|
||||||
}
|
|
||||||
|
|
||||||
jar {
|
jar {
|
||||||
archiveBaseName.set( "flatlaf" )
|
archiveBaseName.set( "flatlaf" )
|
||||||
|
|
||||||
@@ -71,6 +66,9 @@ tasks {
|
|||||||
test {
|
test {
|
||||||
useJUnitPlatform()
|
useJUnitPlatform()
|
||||||
testLogging.exceptionFormat = org.gradle.api.tasks.testing.logging.TestExceptionFormat.FULL
|
testLogging.exceptionFormat = org.gradle.api.tasks.testing.logging.TestExceptionFormat.FULL
|
||||||
|
|
||||||
|
if( JavaVersion.current() >= JavaVersion.VERSION_1_9 )
|
||||||
|
jvmArgs( listOf( "--add-opens", "java.desktop/javax.swing.plaf.basic=ALL-UNNAMED" ) )
|
||||||
}
|
}
|
||||||
|
|
||||||
register( "sigtestGenerate" ) {
|
register( "sigtestGenerate" ) {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
#Signature file v4.1
|
#Signature file v4.1
|
||||||
#Version 2.0
|
#Version 2.4
|
||||||
|
|
||||||
CLSS public abstract interface com.formdev.flatlaf.FlatClientProperties
|
CLSS public abstract interface com.formdev.flatlaf.FlatClientProperties
|
||||||
fld public final static java.lang.String BUTTON_TYPE = "JButton.buttonType"
|
fld public final static java.lang.String BUTTON_TYPE = "JButton.buttonType"
|
||||||
@@ -30,6 +30,9 @@ fld public final static java.lang.String SELECT_ALL_ON_FOCUS_POLICY = "JTextFiel
|
|||||||
fld public final static java.lang.String SELECT_ALL_ON_FOCUS_POLICY_ALWAYS = "always"
|
fld public final static java.lang.String SELECT_ALL_ON_FOCUS_POLICY_ALWAYS = "always"
|
||||||
fld public final static java.lang.String SELECT_ALL_ON_FOCUS_POLICY_NEVER = "never"
|
fld public final static java.lang.String SELECT_ALL_ON_FOCUS_POLICY_NEVER = "never"
|
||||||
fld public final static java.lang.String SELECT_ALL_ON_FOCUS_POLICY_ONCE = "once"
|
fld public final static java.lang.String SELECT_ALL_ON_FOCUS_POLICY_ONCE = "once"
|
||||||
|
fld public final static java.lang.String SPLIT_PANE_EXPANDABLE_SIDE = "JSplitPane.expandableSide"
|
||||||
|
fld public final static java.lang.String SPLIT_PANE_EXPANDABLE_SIDE_LEFT = "left"
|
||||||
|
fld public final static java.lang.String SPLIT_PANE_EXPANDABLE_SIDE_RIGHT = "right"
|
||||||
fld public final static java.lang.String SQUARE_SIZE = "JButton.squareSize"
|
fld public final static java.lang.String SQUARE_SIZE = "JButton.squareSize"
|
||||||
fld public final static java.lang.String STYLE = "FlatLaf.style"
|
fld public final static java.lang.String STYLE = "FlatLaf.style"
|
||||||
fld public final static java.lang.String STYLE_CLASS = "FlatLaf.styleClass"
|
fld public final static java.lang.String STYLE_CLASS = "FlatLaf.styleClass"
|
||||||
@@ -72,6 +75,7 @@ fld public final static java.lang.String TABBED_PANE_TRAILING_COMPONENT = "JTabb
|
|||||||
fld public final static java.lang.String TAB_BUTTON_SELECTED_BACKGROUND = "JToggleButton.tab.selectedBackground"
|
fld public final static java.lang.String TAB_BUTTON_SELECTED_BACKGROUND = "JToggleButton.tab.selectedBackground"
|
||||||
fld public final static java.lang.String TAB_BUTTON_UNDERLINE_COLOR = "JToggleButton.tab.underlineColor"
|
fld public final static java.lang.String TAB_BUTTON_UNDERLINE_COLOR = "JToggleButton.tab.underlineColor"
|
||||||
fld public final static java.lang.String TAB_BUTTON_UNDERLINE_HEIGHT = "JToggleButton.tab.underlineHeight"
|
fld public final static java.lang.String TAB_BUTTON_UNDERLINE_HEIGHT = "JToggleButton.tab.underlineHeight"
|
||||||
|
fld public final static java.lang.String TAB_BUTTON_UNDERLINE_PLACEMENT = "JToggleButton.tab.underlinePlacement"
|
||||||
fld public final static java.lang.String TEXT_FIELD_CLEAR_CALLBACK = "JTextField.clearCallback"
|
fld public final static java.lang.String TEXT_FIELD_CLEAR_CALLBACK = "JTextField.clearCallback"
|
||||||
fld public final static java.lang.String TEXT_FIELD_LEADING_COMPONENT = "JTextField.leadingComponent"
|
fld public final static java.lang.String TEXT_FIELD_LEADING_COMPONENT = "JTextField.leadingComponent"
|
||||||
fld public final static java.lang.String TEXT_FIELD_LEADING_ICON = "JTextField.leadingIcon"
|
fld public final static java.lang.String TEXT_FIELD_LEADING_ICON = "JTextField.leadingIcon"
|
||||||
@@ -216,7 +220,7 @@ meth public void setExtraDefaults(java.util.Map<java.lang.String,java.lang.Strin
|
|||||||
meth public void uninitialize()
|
meth public void uninitialize()
|
||||||
meth public void unregisterUIDefaultsGetter(java.util.function.Function<java.lang.Object,java.lang.Object>)
|
meth public void unregisterUIDefaultsGetter(java.util.function.Function<java.lang.Object,java.lang.Object>)
|
||||||
supr javax.swing.plaf.basic.BasicLookAndFeel
|
supr javax.swing.plaf.basic.BasicLookAndFeel
|
||||||
hfds DESKTOPFONTHINTS,aquaLoaded,customDefaultsSources,desktopPropertyListener,desktopPropertyName,desktopPropertyName2,extraDefaults,globalExtraDefaults,mnemonicHandler,oldPopupFactory,postInitialization,uiDefaultsGetters,updateUIPending
|
hfds DESKTOPFONTHINTS,aquaLoaded,customDefaultsSources,desktopPropertyListener,desktopPropertyName,desktopPropertyName2,extraDefaults,globalExtraDefaults,mnemonicHandler,oldPopupFactory,postInitialization,subMenuUsabilityHelperInstalled,uiDefaultsGetters,updateUIPending
|
||||||
hcls ActiveFont,FlatUIDefaults,ImageIconUIResource
|
hcls ActiveFont,FlatUIDefaults,ImageIconUIResource
|
||||||
|
|
||||||
CLSS public abstract interface static com.formdev.flatlaf.FlatLaf$DisabledIconProvider
|
CLSS public abstract interface static com.formdev.flatlaf.FlatLaf$DisabledIconProvider
|
||||||
@@ -662,6 +666,7 @@ CLSS public com.formdev.flatlaf.util.SystemInfo
|
|||||||
cons public init()
|
cons public init()
|
||||||
fld public final static boolean isAARCH64
|
fld public final static boolean isAARCH64
|
||||||
fld public final static boolean isJava_11_orLater
|
fld public final static boolean isJava_11_orLater
|
||||||
|
fld public final static boolean isJava_12_orLater
|
||||||
fld public final static boolean isJava_15_orLater
|
fld public final static boolean isJava_15_orLater
|
||||||
fld public final static boolean isJava_17_orLater
|
fld public final static boolean isJava_17_orLater
|
||||||
fld public final static boolean isJava_18_orLater
|
fld public final static boolean isJava_18_orLater
|
||||||
@@ -670,6 +675,7 @@ fld public final static boolean isJetBrainsJVM
|
|||||||
fld public final static boolean isJetBrainsJVM_11_orLater
|
fld public final static boolean isJetBrainsJVM_11_orLater
|
||||||
fld public final static boolean isKDE
|
fld public final static boolean isKDE
|
||||||
fld public final static boolean isLinux
|
fld public final static boolean isLinux
|
||||||
|
fld public final static boolean isMacFullWindowContentSupported
|
||||||
fld public final static boolean isMacOS
|
fld public final static boolean isMacOS
|
||||||
fld public final static boolean isMacOS_10_11_ElCapitan_orLater
|
fld public final static boolean isMacOS_10_11_ElCapitan_orLater
|
||||||
fld public final static boolean isMacOS_10_14_Mojave_orLater
|
fld public final static boolean isMacOS_10_14_Mojave_orLater
|
||||||
|
|||||||
@@ -253,6 +253,19 @@ public interface FlatClientProperties
|
|||||||
*/
|
*/
|
||||||
String COMPONENT_FOCUS_OWNER = "JComponent.focusOwner";
|
String COMPONENT_FOCUS_OWNER = "JComponent.focusOwner";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specifies whether a component in an embedded menu bar should behave as caption
|
||||||
|
* (left-click allows moving window, right-click shows window system menu).
|
||||||
|
* The component does not receive mouse pressed/released/clicked/dragged events,
|
||||||
|
* but it gets mouse entered/exited/moved events.
|
||||||
|
* <p>
|
||||||
|
* <strong>Component</strong> {@link javax.swing.JComponent}<br>
|
||||||
|
* <strong>Value type</strong> {@link java.lang.Boolean}
|
||||||
|
*
|
||||||
|
* @since 2.5
|
||||||
|
*/
|
||||||
|
String COMPONENT_TITLE_BAR_CAPTION = "JComponent.titleBarCaption";
|
||||||
|
|
||||||
//---- Popup --------------------------------------------------------------
|
//---- Popup --------------------------------------------------------------
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -391,6 +404,40 @@ public interface FlatClientProperties
|
|||||||
*/
|
*/
|
||||||
String SCROLL_PANE_SMOOTH_SCROLLING = "JScrollPane.smoothScrolling";
|
String SCROLL_PANE_SMOOTH_SCROLLING = "JScrollPane.smoothScrolling";
|
||||||
|
|
||||||
|
//---- JSplitPane ---------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specifies what side of the spilt pane is allowed to expand
|
||||||
|
* via one-touch expanding arrow buttons.
|
||||||
|
* Requires that one-touch expanding is enabled with
|
||||||
|
* {@link javax.swing.JSplitPane#setOneTouchExpandable(boolean)}.
|
||||||
|
* <p>
|
||||||
|
* <strong>Component</strong> {@link javax.swing.JSplitPane}<br>
|
||||||
|
* <strong>Value type</strong> {@link java.lang.String}<br>
|
||||||
|
* <strong>Allowed Values</strong>
|
||||||
|
* {@link #SPLIT_PANE_EXPANDABLE_SIDE_LEFT} or
|
||||||
|
* {@link #SPLIT_PANE_EXPANDABLE_SIDE_RIGHT}
|
||||||
|
*
|
||||||
|
* @since 2.2
|
||||||
|
*/
|
||||||
|
String SPLIT_PANE_EXPANDABLE_SIDE = "JSplitPane.expandableSide";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allow expanding only left/top side of the split pane.
|
||||||
|
*
|
||||||
|
* @see #SPLIT_PANE_EXPANDABLE_SIDE
|
||||||
|
* @since 2.2
|
||||||
|
*/
|
||||||
|
String SPLIT_PANE_EXPANDABLE_SIDE_LEFT = "left";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allow expanding only right/bottom side of the split pane.
|
||||||
|
*
|
||||||
|
* @see #SPLIT_PANE_EXPANDABLE_SIDE
|
||||||
|
* @since 2.2
|
||||||
|
*/
|
||||||
|
String SPLIT_PANE_EXPANDABLE_SIDE_RIGHT = "right";
|
||||||
|
|
||||||
//---- JTabbedPane --------------------------------------------------------
|
//---- JTabbedPane --------------------------------------------------------
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -958,7 +1005,22 @@ public interface FlatClientProperties
|
|||||||
//---- JToggleButton ------------------------------------------------------
|
//---- JToggleButton ------------------------------------------------------
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Height of underline if toggle button type is {@link #BUTTON_TYPE_TAB}.
|
* Placement 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}<br>
|
||||||
|
* <strong>SupportedValues:</strong>
|
||||||
|
* {@link SwingConstants#BOTTOM} (default)
|
||||||
|
* {@link SwingConstants#TOP},
|
||||||
|
* {@link SwingConstants#LEFT} or
|
||||||
|
* {@link SwingConstants#RIGHT}
|
||||||
|
*
|
||||||
|
* @since 2.3
|
||||||
|
*/
|
||||||
|
String TAB_BUTTON_UNDERLINE_PLACEMENT = "JToggleButton.tab.underlinePlacement";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Thickness of underline if toggle button type is {@link #BUTTON_TYPE_TAB}.
|
||||||
* <p>
|
* <p>
|
||||||
* <strong>Component</strong> {@link javax.swing.JToggleButton}<br>
|
* <strong>Component</strong> {@link javax.swing.JToggleButton}<br>
|
||||||
* <strong>Value type</strong> {@link java.lang.Integer}
|
* <strong>Value type</strong> {@link java.lang.Integer}
|
||||||
|
|||||||
@@ -34,6 +34,8 @@ public class FlatDarculaLaf
|
|||||||
/**
|
/**
|
||||||
* Sets the application look and feel to this LaF
|
* Sets the application look and feel to this LaF
|
||||||
* using {@link UIManager#setLookAndFeel(javax.swing.LookAndFeel)}.
|
* using {@link UIManager#setLookAndFeel(javax.swing.LookAndFeel)}.
|
||||||
|
*
|
||||||
|
* @since 1.2
|
||||||
*/
|
*/
|
||||||
public static boolean setup() {
|
public static boolean setup() {
|
||||||
return setup( new FlatDarculaLaf() );
|
return setup( new FlatDarculaLaf() );
|
||||||
|
|||||||
@@ -33,6 +33,8 @@ public class FlatDarkLaf
|
|||||||
/**
|
/**
|
||||||
* Sets the application look and feel to this LaF
|
* Sets the application look and feel to this LaF
|
||||||
* using {@link UIManager#setLookAndFeel(javax.swing.LookAndFeel)}.
|
* using {@link UIManager#setLookAndFeel(javax.swing.LookAndFeel)}.
|
||||||
|
*
|
||||||
|
* @since 1.2
|
||||||
*/
|
*/
|
||||||
public static boolean setup() {
|
public static boolean setup() {
|
||||||
return setup( new FlatDarkLaf() );
|
return setup( new FlatDarkLaf() );
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ import javax.swing.UIDefaults;
|
|||||||
* 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>
|
* <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 an 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>
|
* <p>
|
||||||
@@ -61,7 +61,7 @@ public abstract class FlatDefaultsAddon
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the priority used to sort addon loading.
|
* Returns the priority used to sort addon loading.
|
||||||
* The order is only important if you want overwrite UI defaults of other addons.
|
* The order is only important if you want to overwrite UI defaults of other addons.
|
||||||
* Lower numbers mean higher priority.
|
* Lower numbers mean higher priority.
|
||||||
* Returns 10000 by default.
|
* Returns 10000 by default.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ package com.formdev.flatlaf;
|
|||||||
/**
|
/**
|
||||||
* Default color palette for action icons and object icons.
|
* Default color palette for action icons and object icons.
|
||||||
* <p>
|
* <p>
|
||||||
* The idea is to use only this well defined set of colors in SVG icons and
|
* 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 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.
|
* 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.
|
* IntelliJ Platform uses this mechanism to allow themes to change IntelliJ Platform icons.
|
||||||
@@ -35,7 +35,7 @@ package com.formdev.flatlaf;
|
|||||||
* <p>
|
* <p>
|
||||||
* You may use these colors also in your application (outside of SVG icons), but do
|
* 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>
|
* not use the RGB values defined in this enum.<br>
|
||||||
* Instead use {@code UIManager.getColor( FlatIconColors.ACTIONS_GREY.key )}.
|
* Instead, use {@code UIManager.getColor( FlatIconColors.ACTIONS_GREY.key )}.
|
||||||
*
|
*
|
||||||
* @author Karl Tauber
|
* @author Karl Tauber
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -596,7 +596,7 @@ class FlatInputMaps
|
|||||||
//---- class LazyInputMapEx -----------------------------------------------
|
//---- class LazyInputMapEx -----------------------------------------------
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Lazily creates a input map.
|
* Lazily creates an input map.
|
||||||
* Similar to {@link UIDefaults.LazyInputMap}, but can use multiple bindings arrays.
|
* Similar to {@link UIDefaults.LazyInputMap}, but can use multiple bindings arrays.
|
||||||
*/
|
*/
|
||||||
private static class LazyInputMapEx
|
private static class LazyInputMapEx
|
||||||
|
|||||||
@@ -34,6 +34,8 @@ public class FlatIntelliJLaf
|
|||||||
/**
|
/**
|
||||||
* Sets the application look and feel to this LaF
|
* Sets the application look and feel to this LaF
|
||||||
* using {@link UIManager#setLookAndFeel(javax.swing.LookAndFeel)}.
|
* using {@link UIManager#setLookAndFeel(javax.swing.LookAndFeel)}.
|
||||||
|
*
|
||||||
|
* @since 1.2
|
||||||
*/
|
*/
|
||||||
public static boolean setup() {
|
public static boolean setup() {
|
||||||
return setup( new FlatIntelliJLaf() );
|
return setup( new FlatIntelliJLaf() );
|
||||||
|
|||||||
@@ -30,6 +30,9 @@ import java.awt.image.ImageProducer;
|
|||||||
import java.beans.PropertyChangeEvent;
|
import java.beans.PropertyChangeEvent;
|
||||||
import java.beans.PropertyChangeListener;
|
import java.beans.PropertyChangeListener;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.lang.invoke.MethodHandle;
|
||||||
|
import java.lang.invoke.MethodHandles;
|
||||||
|
import java.lang.invoke.MethodType;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@@ -63,6 +66,7 @@ import javax.swing.UIDefaults.LazyValue;
|
|||||||
import javax.swing.UIManager;
|
import javax.swing.UIManager;
|
||||||
import javax.swing.UnsupportedLookAndFeelException;
|
import javax.swing.UnsupportedLookAndFeelException;
|
||||||
import javax.swing.plaf.ColorUIResource;
|
import javax.swing.plaf.ColorUIResource;
|
||||||
|
import javax.swing.plaf.ComponentUI;
|
||||||
import javax.swing.plaf.FontUIResource;
|
import javax.swing.plaf.FontUIResource;
|
||||||
import javax.swing.plaf.IconUIResource;
|
import javax.swing.plaf.IconUIResource;
|
||||||
import javax.swing.plaf.UIResource;
|
import javax.swing.plaf.UIResource;
|
||||||
@@ -73,6 +77,7 @@ import com.formdev.flatlaf.ui.FlatNativeWindowBorder;
|
|||||||
import com.formdev.flatlaf.ui.FlatPopupFactory;
|
import com.formdev.flatlaf.ui.FlatPopupFactory;
|
||||||
import com.formdev.flatlaf.ui.FlatRootPaneUI;
|
import com.formdev.flatlaf.ui.FlatRootPaneUI;
|
||||||
import com.formdev.flatlaf.ui.FlatUIUtils;
|
import com.formdev.flatlaf.ui.FlatUIUtils;
|
||||||
|
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
|
||||||
import com.formdev.flatlaf.util.GrayFilter;
|
import com.formdev.flatlaf.util.GrayFilter;
|
||||||
import com.formdev.flatlaf.util.LoggingFacade;
|
import com.formdev.flatlaf.util.LoggingFacade;
|
||||||
import com.formdev.flatlaf.util.MultiResolutionImageSupport;
|
import com.formdev.flatlaf.util.MultiResolutionImageSupport;
|
||||||
@@ -103,6 +108,7 @@ public abstract class FlatLaf
|
|||||||
|
|
||||||
private PopupFactory oldPopupFactory;
|
private PopupFactory oldPopupFactory;
|
||||||
private MnemonicHandler mnemonicHandler;
|
private MnemonicHandler mnemonicHandler;
|
||||||
|
private boolean subMenuUsabilityHelperInstalled;
|
||||||
|
|
||||||
private Consumer<UIDefaults> postInitialization;
|
private Consumer<UIDefaults> postInitialization;
|
||||||
private List<Function<Object, Object>> uiDefaultsGetters;
|
private List<Function<Object, Object>> uiDefaultsGetters;
|
||||||
@@ -110,6 +116,8 @@ public abstract class FlatLaf
|
|||||||
/**
|
/**
|
||||||
* Sets the application look and feel to the given LaF
|
* Sets the application look and feel to the given LaF
|
||||||
* using {@link UIManager#setLookAndFeel(javax.swing.LookAndFeel)}.
|
* using {@link UIManager#setLookAndFeel(javax.swing.LookAndFeel)}.
|
||||||
|
*
|
||||||
|
* @since 1.2
|
||||||
*/
|
*/
|
||||||
public static boolean setup( LookAndFeel newLookAndFeel ) {
|
public static boolean setup( LookAndFeel newLookAndFeel ) {
|
||||||
try {
|
try {
|
||||||
@@ -166,18 +174,19 @@ public abstract class FlatLaf
|
|||||||
* Returns whether FlatLaf supports custom window decorations.
|
* Returns whether FlatLaf supports custom window decorations.
|
||||||
* This depends on the operating system and on the used Java runtime.
|
* This depends on the operating system and on the used Java runtime.
|
||||||
* <p>
|
* <p>
|
||||||
* This method returns {@code true} on Windows 10 (see exception below), {@code false} otherwise.
|
* This method returns {@code true} on Windows 10/11 (see exception below)
|
||||||
|
* and on Linux, {@code false} otherwise.
|
||||||
* <p>
|
* <p>
|
||||||
* Returns also {@code false} on Windows 10 if:
|
* Returns also {@code false} on Windows 10/11 if:
|
||||||
* <ul>
|
* <ul>
|
||||||
* <li>FlatLaf native window border support is available (requires Windows 10)</li>
|
* <li>FlatLaf native window border support is available (requires Windows 10/11)</li>
|
||||||
* <li>running in
|
* <li>running in
|
||||||
* <a href="https://confluence.jetbrains.com/display/JBR/JetBrains+Runtime">JetBrains Runtime 11 (or later)</a>
|
* <a href="https://confluence.jetbrains.com/display/JBR/JetBrains+Runtime">JetBrains Runtime 11 (or later)</a>
|
||||||
* (<a href="https://github.com/JetBrains/JetBrainsRuntime">source code on github</a>)
|
* (<a href="https://github.com/JetBrains/JetBrainsRuntime">source code on github</a>)
|
||||||
* and JBR supports custom window decorations
|
* and JBR supports custom window decorations
|
||||||
* </li>
|
* </li>
|
||||||
* </ul>
|
* </ul>
|
||||||
* In this cases, custom decorations are enabled by the root pane.
|
* In these cases, custom decorations are enabled by the root pane.
|
||||||
* Usage of {@link JFrame#setDefaultLookAndFeelDecorated(boolean)} or
|
* Usage of {@link JFrame#setDefaultLookAndFeelDecorated(boolean)} or
|
||||||
* {@link JDialog#setDefaultLookAndFeelDecorated(boolean)} is not necessary.
|
* {@link JDialog#setDefaultLookAndFeelDecorated(boolean)} is not necessary.
|
||||||
*/
|
*/
|
||||||
@@ -190,7 +199,7 @@ public abstract class FlatLaf
|
|||||||
FlatNativeWindowBorder.isSupported() )
|
FlatNativeWindowBorder.isSupported() )
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return SystemInfo.isWindows_10_orLater;
|
return SystemInfo.isWindows_10_orLater || SystemInfo.isLinux;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -230,6 +239,15 @@ public abstract class FlatLaf
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void initialize() {
|
public void initialize() {
|
||||||
|
// do not initialize if this is not the current look and feel
|
||||||
|
// This is only necessary for special Laf usage. E.g. in GUI builders,
|
||||||
|
// which may use multiple Lafs and may invoke this method directly.
|
||||||
|
// This avoids that listeners and factories are installed multiple times.
|
||||||
|
// In case of the NetBeans GUI builder, which does not invoke uninitialize(),
|
||||||
|
// this also avoids that listeners stay registered in the system.
|
||||||
|
if( UIManager.getLookAndFeel() != this )
|
||||||
|
return;
|
||||||
|
|
||||||
if( SystemInfo.isMacOS )
|
if( SystemInfo.isMacOS )
|
||||||
initializeAqua();
|
initializeAqua();
|
||||||
|
|
||||||
@@ -243,6 +261,9 @@ public abstract class FlatLaf
|
|||||||
mnemonicHandler = new MnemonicHandler();
|
mnemonicHandler = new MnemonicHandler();
|
||||||
mnemonicHandler.install();
|
mnemonicHandler.install();
|
||||||
|
|
||||||
|
// install submenu usability helper
|
||||||
|
subMenuUsabilityHelperInstalled = SubMenuUsabilityHelper.install();
|
||||||
|
|
||||||
// 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.isWindows ) {
|
if( SystemInfo.isWindows ) {
|
||||||
// Windows 10 allows increasing font size independent of scaling:
|
// Windows 10 allows increasing font size independent of scaling:
|
||||||
@@ -260,6 +281,9 @@ public abstract class FlatLaf
|
|||||||
}
|
}
|
||||||
if( desktopPropertyName != null ) {
|
if( desktopPropertyName != null ) {
|
||||||
desktopPropertyListener = e -> {
|
desktopPropertyListener = e -> {
|
||||||
|
if( !FlatSystemProperties.getBoolean( FlatSystemProperties.UPDATE_UI_ON_SYSTEM_FONT_CHANGE, true ) )
|
||||||
|
return;
|
||||||
|
|
||||||
String propertyName = e.getPropertyName();
|
String propertyName = e.getPropertyName();
|
||||||
if( desktopPropertyName.equals( propertyName ) || propertyName.equals( desktopPropertyName2 ) )
|
if( desktopPropertyName.equals( propertyName ) || propertyName.equals( desktopPropertyName2 ) )
|
||||||
reSetLookAndFeel();
|
reSetLookAndFeel();
|
||||||
@@ -298,6 +322,10 @@ public abstract class FlatLaf
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void uninitialize() {
|
public void uninitialize() {
|
||||||
|
// do not uninitialize if this is not the current look and feel
|
||||||
|
if( UIManager.getLookAndFeel() != this )
|
||||||
|
return;
|
||||||
|
|
||||||
// remove desktop property listener
|
// remove desktop property listener
|
||||||
if( desktopPropertyListener != null ) {
|
if( desktopPropertyListener != null ) {
|
||||||
Toolkit toolkit = Toolkit.getDefaultToolkit();
|
Toolkit toolkit = Toolkit.getDefaultToolkit();
|
||||||
@@ -322,6 +350,12 @@ public abstract class FlatLaf
|
|||||||
mnemonicHandler = null;
|
mnemonicHandler = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// uninstall submenu usability helper
|
||||||
|
if( subMenuUsabilityHelperInstalled ) {
|
||||||
|
SubMenuUsabilityHelper.uninstall();
|
||||||
|
subMenuUsabilityHelperInstalled = false;
|
||||||
|
}
|
||||||
|
|
||||||
// restore default link color
|
// restore default link color
|
||||||
new HTMLEditorKit().getStyleSheet().addRule( "a, address { color: blue; }" );
|
new HTMLEditorKit().getStyleSheet().addRule( "a, address { color: blue; }" );
|
||||||
postInitialization = null;
|
postInitialization = null;
|
||||||
@@ -547,6 +581,7 @@ public abstract class FlatLaf
|
|||||||
|
|
||||||
// add fonts that are not set in BasicLookAndFeel
|
// add fonts that are not set in BasicLookAndFeel
|
||||||
defaults.put( "RootPane.font", activeFont );
|
defaults.put( "RootPane.font", activeFont );
|
||||||
|
defaults.put( "TitlePane.font", activeFont );
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initDefaultFont( UIDefaults defaults ) {
|
private void initDefaultFont( UIDefaults defaults ) {
|
||||||
@@ -605,7 +640,7 @@ public abstract class FlatLaf
|
|||||||
uiFont = ((ActiveFont)defaultFont).derive( baseFont, fontSize -> {
|
uiFont = ((ActiveFont)defaultFont).derive( baseFont, fontSize -> {
|
||||||
return Math.round( fontSize * UIScale.computeFontScaleFactor( baseFont ) );
|
return Math.round( fontSize * UIScale.computeFontScaleFactor( baseFont ) );
|
||||||
} );
|
} );
|
||||||
};
|
}
|
||||||
|
|
||||||
// increase font size if system property "flatlaf.uiScale" is set
|
// increase font size if system property "flatlaf.uiScale" is set
|
||||||
uiFont = UIScale.applyCustomScaleFactor( uiFont );
|
uiFont = UIScale.applyCustomScaleFactor( uiFont );
|
||||||
@@ -760,7 +795,7 @@ public abstract class FlatLaf
|
|||||||
* Invoke this method before setting the look and feel.
|
* Invoke this method before setting the look and feel.
|
||||||
* <p>
|
* <p>
|
||||||
* If using Java modules, the package must be opened in {@code module-info.java}.
|
* If using Java modules, the package must be opened in {@code module-info.java}.
|
||||||
* Otherwise use {@link #registerCustomDefaultsSource(URL)}.
|
* Otherwise, use {@link #registerCustomDefaultsSource(URL)}.
|
||||||
*
|
*
|
||||||
* @param packageName a package name (e.g. "com.myapp.resources")
|
* @param packageName a package name (e.g. "com.myapp.resources")
|
||||||
*/
|
*/
|
||||||
@@ -862,7 +897,7 @@ public abstract class FlatLaf
|
|||||||
* E.g. using {@link UIManager#setLookAndFeel(LookAndFeel)} or {@link #setup(LookAndFeel)}.
|
* E.g. using {@link UIManager#setLookAndFeel(LookAndFeel)} or {@link #setup(LookAndFeel)}.
|
||||||
* <p>
|
* <p>
|
||||||
* The global extra defaults are useful for smaller additional defaults that may change.
|
* The global extra defaults are useful for smaller additional defaults that may change.
|
||||||
* E.g. accent color. Otherwise FlatLaf properties files should be used.
|
* E.g. accent color. Otherwise, FlatLaf properties files should be used.
|
||||||
* See {@link #registerCustomDefaultsSource(String)}.
|
* See {@link #registerCustomDefaultsSource(String)}.
|
||||||
* <p>
|
* <p>
|
||||||
* The keys and values are strings in same format as in FlatLaf properties files.
|
* The keys and values are strings in same format as in FlatLaf properties files.
|
||||||
@@ -894,7 +929,7 @@ public abstract class FlatLaf
|
|||||||
* E.g. using {@link UIManager#setLookAndFeel(LookAndFeel)} or {@link #setup(LookAndFeel)}.
|
* E.g. using {@link UIManager#setLookAndFeel(LookAndFeel)} or {@link #setup(LookAndFeel)}.
|
||||||
* <p>
|
* <p>
|
||||||
* The extra defaults are useful for smaller additional defaults that may change.
|
* The extra defaults are useful for smaller additional defaults that may change.
|
||||||
* E.g. accent color. Otherwise FlatLaf properties files should be used.
|
* E.g. accent color. Otherwise, FlatLaf properties files should be used.
|
||||||
* See {@link #registerCustomDefaultsSource(String)}.
|
* See {@link #registerCustomDefaultsSource(String)}.
|
||||||
* <p>
|
* <p>
|
||||||
* The keys and values are strings in same format as in FlatLaf properties files.
|
* The keys and values are strings in same format as in FlatLaf properties files.
|
||||||
@@ -951,7 +986,7 @@ public abstract class FlatLaf
|
|||||||
// re-set current LaF
|
// re-set current LaF
|
||||||
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 ourselves because old and new LaF are the same
|
||||||
PropertyChangeEvent e = new PropertyChangeEvent( UIManager.class, "lookAndFeel", lookAndFeel, lookAndFeel );
|
PropertyChangeEvent e = new PropertyChangeEvent( UIManager.class, "lookAndFeel", lookAndFeel, lookAndFeel );
|
||||||
for( PropertyChangeListener l : UIManager.getPropertyChangeListeners() )
|
for( PropertyChangeListener l : UIManager.getPropertyChangeListeners() )
|
||||||
l.propertyChange( e );
|
l.propertyChange( e );
|
||||||
@@ -995,7 +1030,7 @@ public abstract class FlatLaf
|
|||||||
/**
|
/**
|
||||||
* Returns whether native window decorations are supported on current platform.
|
* Returns whether native window decorations are supported on current platform.
|
||||||
* <p>
|
* <p>
|
||||||
* This requires Windows 10, but may be disabled if running in special environments
|
* This requires Windows 10/11, but may be disabled if running in special environments
|
||||||
* (JetBrains Projector, Webswing or WinPE) or if loading native library fails.
|
* (JetBrains Projector, Webswing or WinPE) or if loading native library fails.
|
||||||
* If system property {@link FlatSystemProperties#USE_WINDOW_DECORATIONS} is set to
|
* If system property {@link FlatSystemProperties#USE_WINDOW_DECORATIONS} is set to
|
||||||
* {@code false}, then this method also returns {@code false}.
|
* {@code false}, then this method also returns {@code false}.
|
||||||
@@ -1201,6 +1236,62 @@ public abstract class FlatLaf
|
|||||||
*/
|
*/
|
||||||
public static final Object NULL_VALUE = new Object();
|
public static final Object NULL_VALUE = new Object();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns information about styleable values of a component.
|
||||||
|
* <p>
|
||||||
|
* This is equivalent to: {@code ((StyleableUI)c.getUI()).getStyleableInfos(c)}
|
||||||
|
*
|
||||||
|
* @since 2.5
|
||||||
|
*/
|
||||||
|
public static Map<String, Class<?>> getStyleableInfos( JComponent c ) {
|
||||||
|
StyleableUI ui = getStyleableUI( c );
|
||||||
|
return (ui != null) ? ui.getStyleableInfos( c ) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the (styled) value for the given key from the given component.
|
||||||
|
* <p>
|
||||||
|
* This is equivalent to: {@code ((StyleableUI)c.getUI()).getStyleableValue(c, key)}
|
||||||
|
*
|
||||||
|
* @since 2.5
|
||||||
|
*/
|
||||||
|
@SuppressWarnings( "unchecked" )
|
||||||
|
public static <T> T getStyleableValue( JComponent c, String key ) {
|
||||||
|
StyleableUI ui = getStyleableUI( c );
|
||||||
|
return (ui != null) ? (T) ui.getStyleableValue( c, key ) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static StyleableUI getStyleableUI( JComponent c ) {
|
||||||
|
if( !getUIMethodInitialized ) {
|
||||||
|
getUIMethodInitialized = true;
|
||||||
|
|
||||||
|
if( SystemInfo.isJava_9_orLater ) {
|
||||||
|
try {
|
||||||
|
// JComponent.getUI() is available since Java 9
|
||||||
|
getUIMethod = MethodHandles.lookup().findVirtual( JComponent.class, "getUI",
|
||||||
|
MethodType.methodType( ComponentUI.class ) );
|
||||||
|
} catch( Exception ex ) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
Object ui;
|
||||||
|
if( getUIMethod != null )
|
||||||
|
ui = getUIMethod.invoke( c );
|
||||||
|
else
|
||||||
|
ui = c.getClass().getMethod( "getUI" ).invoke( c );
|
||||||
|
return (ui instanceof StyleableUI) ? (StyleableUI) ui : null;
|
||||||
|
} catch( Throwable ex ) {
|
||||||
|
// ignore
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean getUIMethodInitialized;
|
||||||
|
private static MethodHandle getUIMethod;
|
||||||
|
|
||||||
//---- class FlatUIDefaults -----------------------------------------------
|
//---- class FlatUIDefaults -----------------------------------------------
|
||||||
|
|
||||||
private class FlatUIDefaults
|
private class FlatUIDefaults
|
||||||
|
|||||||
@@ -33,6 +33,8 @@ public class FlatLightLaf
|
|||||||
/**
|
/**
|
||||||
* Sets the application look and feel to this LaF
|
* Sets the application look and feel to this LaF
|
||||||
* using {@link UIManager#setLookAndFeel(javax.swing.LookAndFeel)}.
|
* using {@link UIManager#setLookAndFeel(javax.swing.LookAndFeel)}.
|
||||||
|
*
|
||||||
|
* @since 1.2
|
||||||
*/
|
*/
|
||||||
public static boolean setup() {
|
public static boolean setup() {
|
||||||
return setup( new FlatLightLaf() );
|
return setup( new FlatLightLaf() );
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
package com.formdev.flatlaf;
|
package com.formdev.flatlaf;
|
||||||
|
|
||||||
|
import javax.swing.SwingUtilities;
|
||||||
import com.formdev.flatlaf.util.UIScale;
|
import com.formdev.flatlaf.util.UIScale;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -34,7 +35,7 @@ public interface FlatSystemProperties
|
|||||||
* To replace the Java 9+ system scale factor, use system property "sun.java2d.uiScale",
|
* To replace the Java 9+ system scale factor, use system property "sun.java2d.uiScale",
|
||||||
* which has the same syntax as this one.
|
* which has the same syntax as this one.
|
||||||
* <p>
|
* <p>
|
||||||
* Since FlatLaf 1.1.2: Scale factors less then 100% are allowed.
|
* Since FlatLaf 1.1.2: Scale factors less than 100% are allowed.
|
||||||
* <p>
|
* <p>
|
||||||
* <strong>Allowed Values</strong> e.g. {@code 1.5}, {@code 1.5x}, {@code 150%} or {@code 144dpi} (96dpi is 100%)<br>
|
* <strong>Allowed Values</strong> e.g. {@code 1.5}, {@code 1.5x}, {@code 150%} or {@code 144dpi} (96dpi is 100%)<br>
|
||||||
*/
|
*/
|
||||||
@@ -139,6 +140,20 @@ public interface FlatSystemProperties
|
|||||||
*/
|
*/
|
||||||
String USE_TEXT_Y_CORRECTION = "flatlaf.useTextYCorrection";
|
String USE_TEXT_Y_CORRECTION = "flatlaf.useTextYCorrection";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specifies whether FlatLaf updates the UI when the system font changes.
|
||||||
|
* If {@code true}, {@link SwingUtilities#updateComponentTreeUI(java.awt.Component)}
|
||||||
|
* gets invoked for all windows if the system font has changed.
|
||||||
|
* This is the similar to when switching to another look and feel (theme).
|
||||||
|
* Applications that do not work correctly when switching look and feel,
|
||||||
|
* should disable this option to avoid corrupted UI.
|
||||||
|
* <p>
|
||||||
|
* <strong>Allowed Values</strong> {@code false} and {@code true}<br>
|
||||||
|
* <strong>Default</strong> {@code true}
|
||||||
|
* @since 2.5
|
||||||
|
*/
|
||||||
|
String UPDATE_UI_ON_SYSTEM_FONT_CHANGE = "flatlaf.updateUIOnSystemFontChange";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Specifies a directory in which the native FlatLaf library have been extracted.
|
* Specifies a directory in which the native FlatLaf library have been extracted.
|
||||||
* The path can be absolute or relative to current application working directory.
|
* The path can be absolute or relative to current application working directory.
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ import com.formdev.flatlaf.json.ParseException;
|
|||||||
import com.formdev.flatlaf.util.ColorFunctions;
|
import com.formdev.flatlaf.util.ColorFunctions;
|
||||||
import com.formdev.flatlaf.util.LoggingFacade;
|
import com.formdev.flatlaf.util.LoggingFacade;
|
||||||
import com.formdev.flatlaf.util.StringUtils;
|
import com.formdev.flatlaf.util.StringUtils;
|
||||||
|
import com.formdev.flatlaf.util.SystemInfo;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class supports loading IntelliJ .theme.json files and using them as a Laf.
|
* This class supports loading IntelliJ .theme.json files and using them as a Laf.
|
||||||
@@ -72,6 +73,8 @@ public class IntelliJTheme
|
|||||||
*
|
*
|
||||||
* The input stream is automatically closed.
|
* The input stream is automatically closed.
|
||||||
* Using a buffered input stream is not necessary.
|
* Using a buffered input stream is not necessary.
|
||||||
|
*
|
||||||
|
* @since 1.2
|
||||||
*/
|
*/
|
||||||
public static boolean setup( InputStream in ) {
|
public static boolean setup( InputStream in ) {
|
||||||
try {
|
try {
|
||||||
@@ -162,8 +165,11 @@ public class IntelliJTheme
|
|||||||
applyCheckBoxColors( defaults );
|
applyCheckBoxColors( defaults );
|
||||||
|
|
||||||
// copy values
|
// copy values
|
||||||
for( Map.Entry<String, String> e : uiKeyCopying.entrySet() )
|
for( Map.Entry<String, String> e : uiKeyCopying.entrySet() ) {
|
||||||
defaults.put( e.getKey(), defaults.get( e.getValue() ) );
|
Object value = defaults.get( e.getValue() );
|
||||||
|
if( value != null )
|
||||||
|
defaults.put( e.getKey(), value );
|
||||||
|
}
|
||||||
|
|
||||||
// IDEA does not paint button background if disabled, but FlatLaf does
|
// IDEA does not paint button background if disabled, but FlatLaf does
|
||||||
Object panelBackground = defaults.get( "Panel.background" );
|
Object panelBackground = defaults.get( "Panel.background" );
|
||||||
@@ -175,7 +181,7 @@ public class IntelliJTheme
|
|||||||
defaults.put( "Button.hoverBorderColor", defaults.get( "Button.focusedBorderColor" ) );
|
defaults.put( "Button.hoverBorderColor", defaults.get( "Button.focusedBorderColor" ) );
|
||||||
defaults.put( "HelpButton.hoverBorderColor", defaults.get( "Button.focusedBorderColor" ) );
|
defaults.put( "HelpButton.hoverBorderColor", defaults.get( "Button.focusedBorderColor" ) );
|
||||||
|
|
||||||
// IDEA uses a SVG icon for the help button, but paints the background with Button.startBackground and Button.endBackground
|
// IDEA uses an SVG icon for the help button, but paints the background with Button.startBackground and Button.endBackground
|
||||||
Object helpButtonBackground = defaults.get( "Button.startBackground" );
|
Object helpButtonBackground = defaults.get( "Button.startBackground" );
|
||||||
Object helpButtonBorderColor = defaults.get( "Button.startBorderColor" );
|
Object helpButtonBorderColor = defaults.get( "Button.startBorderColor" );
|
||||||
if( helpButtonBackground == null )
|
if( helpButtonBackground == null )
|
||||||
@@ -242,7 +248,19 @@ public class IntelliJTheme
|
|||||||
defaults.put( "Tree.rowHeight", 22 );
|
defaults.put( "Tree.rowHeight", 22 );
|
||||||
|
|
||||||
// apply theme specific UI defaults at the end to allow overwriting
|
// apply theme specific UI defaults at the end to allow overwriting
|
||||||
defaults.putAll( themeSpecificDefaults );
|
for( Map.Entry<Object, Object> e : themeSpecificDefaults.entrySet() ) {
|
||||||
|
Object key = e.getKey();
|
||||||
|
Object value = e.getValue();
|
||||||
|
|
||||||
|
// append styles to existing styles
|
||||||
|
if( key instanceof String && ((String)key).startsWith( "[style]" ) ) {
|
||||||
|
Object oldValue = defaults.get( key );
|
||||||
|
if( oldValue != null )
|
||||||
|
value = oldValue + "; " + value;
|
||||||
|
}
|
||||||
|
|
||||||
|
defaults.put( key, value );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Map<Object, Object> removeThemeSpecificDefaults( UIDefaults defaults ) {
|
private Map<Object, Object> removeThemeSpecificDefaults( UIDefaults defaults ) {
|
||||||
@@ -299,8 +317,19 @@ public class IntelliJTheme
|
|||||||
@SuppressWarnings( "unchecked" )
|
@SuppressWarnings( "unchecked" )
|
||||||
private void apply( String key, Object value, UIDefaults defaults, ArrayList<Object> defaultsKeysCache, Set<String> uiKeys ) {
|
private void apply( String key, Object value, UIDefaults defaults, ArrayList<Object> defaultsKeysCache, Set<String> uiKeys ) {
|
||||||
if( value instanceof Map ) {
|
if( value instanceof Map ) {
|
||||||
for( Map.Entry<String, Object> e : ((Map<String, Object>)value).entrySet() )
|
Map<String, Object> map = (Map<String, Object>)value;
|
||||||
apply( key + '.' + e.getKey(), e.getValue(), defaults, defaultsKeysCache, uiKeys );
|
if( map.containsKey( "os.default" ) || map.containsKey( "os.windows" ) || map.containsKey( "os.mac" ) || map.containsKey( "os.linux" ) ) {
|
||||||
|
String osKey = SystemInfo.isWindows ? "os.windows"
|
||||||
|
: SystemInfo.isMacOS ? "os.mac"
|
||||||
|
: SystemInfo.isLinux ? "os.linux" : null;
|
||||||
|
if( osKey != null && map.containsKey( osKey ) )
|
||||||
|
apply( key, map.get( osKey ), defaults, defaultsKeysCache, uiKeys );
|
||||||
|
else if( map.containsKey( "os.default" ) )
|
||||||
|
apply( key, map.get( "os.default" ), defaults, defaultsKeysCache, uiKeys );
|
||||||
|
} else {
|
||||||
|
for( Map.Entry<String, Object> e : map.entrySet() )
|
||||||
|
apply( key + '.' + e.getKey(), e.getValue(), defaults, defaultsKeysCache, uiKeys );
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
if( "".equals( value ) )
|
if( "".equals( value ) )
|
||||||
return; // ignore empty value
|
return; // ignore empty value
|
||||||
@@ -534,12 +563,12 @@ public class IntelliJTheme
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Rename UI default keys (key --> value). */
|
/** Rename UI default keys (key --> value). */
|
||||||
private static Map<String, String> uiKeyMapping = new HashMap<>();
|
private static final Map<String, String> uiKeyMapping = new HashMap<>();
|
||||||
/** Copy UI default keys (value --> key). */
|
/** Copy UI default keys (value --> key). */
|
||||||
private static Map<String, String> uiKeyCopying = new HashMap<>();
|
private static final Map<String, String> uiKeyCopying = new HashMap<>();
|
||||||
private static Map<String, String> uiKeyInverseMapping = new HashMap<>();
|
private static final Map<String, String> uiKeyInverseMapping = new HashMap<>();
|
||||||
private static Map<String, String> checkboxKeyMapping = new HashMap<>();
|
private static final Map<String, String> checkboxKeyMapping = new HashMap<>();
|
||||||
private static Map<String, String> checkboxDuplicateColors = new HashMap<>();
|
private static final Map<String, String> checkboxDuplicateColors = new HashMap<>();
|
||||||
|
|
||||||
static {
|
static {
|
||||||
// ComboBox
|
// ComboBox
|
||||||
@@ -600,6 +629,11 @@ public class IntelliJTheme
|
|||||||
uiKeyCopying.put( "Spinner.buttonSeparatorColor", "Component.borderColor" );
|
uiKeyCopying.put( "Spinner.buttonSeparatorColor", "Component.borderColor" );
|
||||||
uiKeyCopying.put( "Spinner.buttonDisabledSeparatorColor", "Component.disabledBorderColor" );
|
uiKeyCopying.put( "Spinner.buttonDisabledSeparatorColor", "Component.disabledBorderColor" );
|
||||||
|
|
||||||
|
// TabbedPane
|
||||||
|
uiKeyCopying.put( "TabbedPane.selectedBackground", "DefaultTabs.underlinedTabBackground" );
|
||||||
|
uiKeyCopying.put( "TabbedPane.selectedForeground", "DefaultTabs.underlinedTabForeground" );
|
||||||
|
uiKeyCopying.put( "TabbedPane.inactiveUnderlineColor", "DefaultTabs.inactiveUnderlineColor" );
|
||||||
|
|
||||||
// TitlePane
|
// TitlePane
|
||||||
uiKeyCopying.put( "TitlePane.inactiveBackground", "TitlePane.background" );
|
uiKeyCopying.put( "TitlePane.inactiveBackground", "TitlePane.background" );
|
||||||
uiKeyMapping.put( "TitlePane.infoForeground", "TitlePane.foreground" );
|
uiKeyMapping.put( "TitlePane.infoForeground", "TitlePane.foreground" );
|
||||||
|
|||||||
@@ -170,7 +170,7 @@ class LinuxFontPolicy
|
|||||||
|
|
||||||
Object value = Toolkit.getDefaultToolkit().getDesktopProperty( "gnome.Xft/DPI" );
|
Object value = Toolkit.getDefaultToolkit().getDesktopProperty( "gnome.Xft/DPI" );
|
||||||
if( value instanceof Integer ) {
|
if( value instanceof Integer ) {
|
||||||
int dpi = ((Integer)value).intValue() / 1024;
|
int dpi = (Integer) value / 1024;
|
||||||
if( dpi == -1 )
|
if( dpi == -1 )
|
||||||
dpi = 96;
|
dpi = 96;
|
||||||
if( dpi < 50 )
|
if( dpi < 50 )
|
||||||
@@ -197,7 +197,7 @@ class LinuxFontPolicy
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the default font for KDE for KDE configuration files.
|
* Gets the default font for KDE from KDE configuration files.
|
||||||
*
|
*
|
||||||
* The Swing fonts are not updated when the user changes system font size
|
* The Swing fonts are not updated when the user changes system font size
|
||||||
* (System Settings > Fonts > Force Font DPI). A application restart is necessary.
|
* (System Settings > Fonts > Force Font DPI). A application restart is necessary.
|
||||||
@@ -278,7 +278,7 @@ class LinuxFontPolicy
|
|||||||
// read config file
|
// read config file
|
||||||
ArrayList<String> lines = new ArrayList<>( 200 );
|
ArrayList<String> lines = new ArrayList<>( 200 );
|
||||||
try( BufferedReader reader = new BufferedReader( new FileReader( file ) ) ) {
|
try( BufferedReader reader = new BufferedReader( new FileReader( file ) ) ) {
|
||||||
String line = null;
|
String line;
|
||||||
while( (line = reader.readLine()) != null )
|
while( (line = reader.readLine()) != null )
|
||||||
lines.add( line );
|
lines.add( line );
|
||||||
} catch( IOException ex ) {
|
} catch( IOException ex ) {
|
||||||
|
|||||||
@@ -0,0 +1,336 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2022 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.AWTEvent;
|
||||||
|
import java.awt.Color;
|
||||||
|
import java.awt.Component;
|
||||||
|
import java.awt.Container;
|
||||||
|
import java.awt.Dimension;
|
||||||
|
import java.awt.EventQueue;
|
||||||
|
import java.awt.Graphics;
|
||||||
|
import java.awt.Graphics2D;
|
||||||
|
import java.awt.MouseInfo;
|
||||||
|
import java.awt.Point;
|
||||||
|
import java.awt.PointerInfo;
|
||||||
|
import java.awt.Polygon;
|
||||||
|
import java.awt.Rectangle;
|
||||||
|
import java.awt.Toolkit;
|
||||||
|
import java.awt.Window;
|
||||||
|
import java.awt.event.MouseEvent;
|
||||||
|
import javax.swing.JComponent;
|
||||||
|
import javax.swing.JLayeredPane;
|
||||||
|
import javax.swing.JMenu;
|
||||||
|
import javax.swing.JPopupMenu;
|
||||||
|
import javax.swing.MenuElement;
|
||||||
|
import javax.swing.MenuSelectionManager;
|
||||||
|
import javax.swing.RootPaneContainer;
|
||||||
|
import javax.swing.SwingUtilities;
|
||||||
|
import javax.swing.Timer;
|
||||||
|
import javax.swing.UIManager;
|
||||||
|
import javax.swing.event.ChangeEvent;
|
||||||
|
import javax.swing.event.ChangeListener;
|
||||||
|
import com.formdev.flatlaf.ui.FlatUIUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Improves usability of submenus by using a
|
||||||
|
* <a href="https://height.app/blog/guide-to-build-context-menus#safe-triangle">safe triangle</a>
|
||||||
|
* to avoid that the submenu closes while the user moves the mouse to it.
|
||||||
|
*
|
||||||
|
* @author Karl Tauber
|
||||||
|
*/
|
||||||
|
class SubMenuUsabilityHelper
|
||||||
|
implements ChangeListener
|
||||||
|
{
|
||||||
|
private static final String KEY_USE_SAFE_TRIANGLE = "Menu.useSafeTriangle";
|
||||||
|
private static final String KEY_SHOW_SAFE_TRIANGLE = "FlatLaf.debug.menu.showSafeTriangle";
|
||||||
|
|
||||||
|
// Using a static field to ensure that there is only one instance in the system.
|
||||||
|
// Multiple instances would freeze the application.
|
||||||
|
// https://github.com/apache/netbeans/issues/4231#issuecomment-1179616607
|
||||||
|
private static SubMenuUsabilityHelper instance;
|
||||||
|
|
||||||
|
private SubMenuEventQueue subMenuEventQueue;
|
||||||
|
private SafeTrianglePainter safeTrianglePainter;
|
||||||
|
private boolean changePending;
|
||||||
|
|
||||||
|
// mouse location in screen coordinates
|
||||||
|
private int mouseX;
|
||||||
|
private int mouseY;
|
||||||
|
|
||||||
|
// target popup bounds in screen coordinates
|
||||||
|
private int targetX;
|
||||||
|
private int targetTopY;
|
||||||
|
private int targetBottomY;
|
||||||
|
|
||||||
|
private Rectangle invokerBounds;
|
||||||
|
|
||||||
|
static synchronized boolean install() {
|
||||||
|
if( instance != null )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
instance = new SubMenuUsabilityHelper();
|
||||||
|
MenuSelectionManager.defaultManager().addChangeListener( instance );
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static synchronized void uninstall() {
|
||||||
|
if( instance == null )
|
||||||
|
return;
|
||||||
|
|
||||||
|
MenuSelectionManager.defaultManager().removeChangeListener( instance );
|
||||||
|
instance.uninstallEventQueue();
|
||||||
|
instance = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void stateChanged( ChangeEvent e ) {
|
||||||
|
if( !FlatUIUtils.getUIBoolean( KEY_USE_SAFE_TRIANGLE, true ))
|
||||||
|
return;
|
||||||
|
|
||||||
|
// handle menu selection change later, but only once in case of temporary changes
|
||||||
|
// e.g. moving mouse from one menu item to another one, fires two events:
|
||||||
|
// 1. old menu item is removed from menu selection
|
||||||
|
// 2. new menu item is added to menu selection
|
||||||
|
synchronized( this ) {
|
||||||
|
if( changePending )
|
||||||
|
return;
|
||||||
|
changePending = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
EventQueue.invokeLater( () -> {
|
||||||
|
synchronized( this ) {
|
||||||
|
changePending = false;
|
||||||
|
}
|
||||||
|
menuSelectionChanged();
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
|
private void menuSelectionChanged() {
|
||||||
|
MenuElement[] path = MenuSelectionManager.defaultManager().getSelectedPath();
|
||||||
|
|
||||||
|
/*debug
|
||||||
|
System.out.println( "--- " + path.length );
|
||||||
|
for( int i = 0; i < path.length; i++ )
|
||||||
|
System.out.println( " " + i + ": " + path[i].getClass().getName() );
|
||||||
|
debug*/
|
||||||
|
|
||||||
|
// find submenu in menu selection
|
||||||
|
int subMenuIndex = findSubMenu( path );
|
||||||
|
|
||||||
|
// uninstall if there is no submenu in selection
|
||||||
|
if( subMenuIndex < 0 || subMenuIndex != path.length - 1 ) {
|
||||||
|
uninstallEventQueue();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// get current mouse location
|
||||||
|
PointerInfo pointerInfo = MouseInfo.getPointerInfo();
|
||||||
|
Point mouseLocation = (pointerInfo != null) ? pointerInfo.getLocation() : new Point();
|
||||||
|
mouseX = mouseLocation.x;
|
||||||
|
mouseY = mouseLocation.y;
|
||||||
|
|
||||||
|
// check whether popup is showing, which is e.g. not the case if it is empty
|
||||||
|
JPopupMenu popup = (JPopupMenu) path[subMenuIndex];
|
||||||
|
if( !popup.isShowing() ) {
|
||||||
|
uninstallEventQueue();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// get invoker screen bounds
|
||||||
|
Component invoker = popup.getInvoker();
|
||||||
|
invokerBounds = (invoker != null)
|
||||||
|
? new Rectangle( invoker.getLocationOnScreen(), invoker.getSize() )
|
||||||
|
: null;
|
||||||
|
|
||||||
|
// check whether mouse location is within invoker
|
||||||
|
if( invokerBounds != null && !invokerBounds.contains( mouseX, mouseY ) ) {
|
||||||
|
uninstallEventQueue();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// compute top/bottom target locations
|
||||||
|
Point popupLocation = popup.getLocationOnScreen();
|
||||||
|
Dimension popupSize = popup.getSize();
|
||||||
|
targetX = (mouseX < popupLocation.x + (popupSize.width / 2))
|
||||||
|
? popupLocation.x
|
||||||
|
: popupLocation.x + popupSize.width;
|
||||||
|
targetTopY = popupLocation.y;
|
||||||
|
targetBottomY = popupLocation.y + popupSize.height;
|
||||||
|
|
||||||
|
// install own event queue to supress mouse events when mouse is moved within safe triangle
|
||||||
|
if( subMenuEventQueue == null )
|
||||||
|
subMenuEventQueue = new SubMenuEventQueue();
|
||||||
|
|
||||||
|
// create safe triangle painter
|
||||||
|
if( safeTrianglePainter == null && UIManager.getBoolean( KEY_SHOW_SAFE_TRIANGLE ) )
|
||||||
|
safeTrianglePainter = new SafeTrianglePainter( popup );
|
||||||
|
}
|
||||||
|
|
||||||
|
private void uninstallEventQueue() {
|
||||||
|
if( subMenuEventQueue != null ) {
|
||||||
|
subMenuEventQueue.uninstall();
|
||||||
|
subMenuEventQueue = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( safeTrianglePainter != null ) {
|
||||||
|
safeTrianglePainter.uninstall();
|
||||||
|
safeTrianglePainter = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private int findSubMenu( MenuElement[] path ) {
|
||||||
|
for( int i = path.length - 1; i >= 1; i-- ) {
|
||||||
|
if( path[i] instanceof JPopupMenu &&
|
||||||
|
path[i - 1] instanceof JMenu &&
|
||||||
|
!((JMenu)path[i - 1]).isTopLevelMenu() )
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Polygon createSafeTriangle() {
|
||||||
|
return new Polygon(
|
||||||
|
new int[] { mouseX, targetX, targetX },
|
||||||
|
new int[] { mouseY, targetTopY, targetBottomY },
|
||||||
|
3 );
|
||||||
|
}
|
||||||
|
|
||||||
|
//---- class SubMenuEventQueue --------------------------------------------
|
||||||
|
|
||||||
|
private class SubMenuEventQueue
|
||||||
|
extends EventQueue
|
||||||
|
{
|
||||||
|
private Timer mouseUpdateTimer;
|
||||||
|
private Timer timeoutTimer;
|
||||||
|
|
||||||
|
private int newMouseX;
|
||||||
|
private int newMouseY;
|
||||||
|
private AWTEvent lastMouseEvent;
|
||||||
|
|
||||||
|
SubMenuEventQueue() {
|
||||||
|
// timer used to slightly delay update of mouse location used for safe triangle
|
||||||
|
mouseUpdateTimer = new Timer( 50, e -> {
|
||||||
|
mouseX = newMouseX;
|
||||||
|
mouseY = newMouseY;
|
||||||
|
|
||||||
|
if( safeTrianglePainter != null )
|
||||||
|
safeTrianglePainter.repaint();
|
||||||
|
} );
|
||||||
|
mouseUpdateTimer.setRepeats( false );
|
||||||
|
|
||||||
|
// timer used to timeout safe triangle when mouse stops moving
|
||||||
|
timeoutTimer = new Timer( 200, e -> {
|
||||||
|
if( invokerBounds != null && !invokerBounds.contains( newMouseX, newMouseY ) ) {
|
||||||
|
// post last mouse event, which selects menu item at mouse location
|
||||||
|
if( lastMouseEvent != null ) {
|
||||||
|
postEvent( lastMouseEvent );
|
||||||
|
lastMouseEvent = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
uninstallEventQueue();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
timeoutTimer.setRepeats( false );
|
||||||
|
|
||||||
|
Toolkit.getDefaultToolkit().getSystemEventQueue().push( this );
|
||||||
|
}
|
||||||
|
|
||||||
|
void uninstall() {
|
||||||
|
mouseUpdateTimer.stop();
|
||||||
|
mouseUpdateTimer = null;
|
||||||
|
|
||||||
|
timeoutTimer.stop();
|
||||||
|
timeoutTimer = null;
|
||||||
|
|
||||||
|
lastMouseEvent = null;
|
||||||
|
|
||||||
|
super.pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void dispatchEvent( AWTEvent e ) {
|
||||||
|
int id = e.getID();
|
||||||
|
|
||||||
|
if( e instanceof MouseEvent &&
|
||||||
|
(id == MouseEvent.MOUSE_MOVED || id == MouseEvent.MOUSE_DRAGGED) )
|
||||||
|
{
|
||||||
|
newMouseX = ((MouseEvent)e).getXOnScreen();
|
||||||
|
newMouseY = ((MouseEvent)e).getYOnScreen();
|
||||||
|
|
||||||
|
if( safeTrianglePainter != null )
|
||||||
|
safeTrianglePainter.repaint();
|
||||||
|
|
||||||
|
mouseUpdateTimer.stop();
|
||||||
|
timeoutTimer.stop();
|
||||||
|
|
||||||
|
// check whether mouse moved within safe triangle
|
||||||
|
if( createSafeTriangle().contains( newMouseX, newMouseY ) ) {
|
||||||
|
// update mouse location delayed (this changes the safe triangle)
|
||||||
|
mouseUpdateTimer.start();
|
||||||
|
|
||||||
|
timeoutTimer.start();
|
||||||
|
|
||||||
|
// remember last mouse event, which will be posted if the mouse stops moving
|
||||||
|
lastMouseEvent = e;
|
||||||
|
|
||||||
|
// ignore mouse event
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// update mouse location immediately (this changes the safe triangle)
|
||||||
|
mouseX = newMouseX;
|
||||||
|
mouseY = newMouseY;
|
||||||
|
}
|
||||||
|
|
||||||
|
super.dispatchEvent( e );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//---- class SafeTrianglePainter ------------------------------------------
|
||||||
|
|
||||||
|
private class SafeTrianglePainter
|
||||||
|
extends JComponent
|
||||||
|
{
|
||||||
|
SafeTrianglePainter( JPopupMenu popup ) {
|
||||||
|
Window window = SwingUtilities.windowForComponent( popup.getInvoker() );
|
||||||
|
if( window instanceof RootPaneContainer ) {
|
||||||
|
JLayeredPane layeredPane = ((RootPaneContainer)window).getLayeredPane();
|
||||||
|
setSize( layeredPane.getSize() );
|
||||||
|
layeredPane.add( this, Integer.valueOf( JLayeredPane.POPUP_LAYER + 1 ) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void uninstall() {
|
||||||
|
Container parent = getParent();
|
||||||
|
if( parent != null ) {
|
||||||
|
parent.remove( this );
|
||||||
|
parent.repaint();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void paintComponent( Graphics g ) {
|
||||||
|
Point locationOnScreen = getLocationOnScreen();
|
||||||
|
g.translate( -locationOnScreen.x, -locationOnScreen.y );
|
||||||
|
|
||||||
|
g.setColor( Color.red );
|
||||||
|
((Graphics2D)g).draw( createSafeTriangle() );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -350,7 +350,7 @@ class UIDefaultsLoader
|
|||||||
enum ValueType { UNKNOWN, STRING, BOOLEAN, CHARACTER, INTEGER, INTEGERORFLOAT, FLOAT, BORDER, ICON, INSETS, DIMENSION, COLOR, FONT,
|
enum ValueType { UNKNOWN, STRING, BOOLEAN, CHARACTER, INTEGER, INTEGERORFLOAT, FLOAT, BORDER, ICON, INSETS, DIMENSION, COLOR, FONT,
|
||||||
SCALEDINTEGER, SCALEDFLOAT, SCALEDINSETS, SCALEDDIMENSION, INSTANCE, CLASS, GRAYFILTER, NULL, LAZY }
|
SCALEDINTEGER, SCALEDFLOAT, SCALEDINSETS, SCALEDDIMENSION, INSTANCE, CLASS, GRAYFILTER, NULL, LAZY }
|
||||||
|
|
||||||
private static ValueType[] tempResultValueType = new ValueType[1];
|
private static final ValueType[] tempResultValueType = new ValueType[1];
|
||||||
private static Map<Class<?>, ValueType> javaValueTypes;
|
private static Map<Class<?>, ValueType> javaValueTypes;
|
||||||
private static Map<String, ValueType> knownValueTypes;
|
private static Map<String, ValueType> knownValueTypes;
|
||||||
|
|
||||||
@@ -466,6 +466,10 @@ class UIDefaultsLoader
|
|||||||
if( knownValueTypes == null ) {
|
if( knownValueTypes == null ) {
|
||||||
// create lazy
|
// create lazy
|
||||||
knownValueTypes = new HashMap<>();
|
knownValueTypes = new HashMap<>();
|
||||||
|
// system colors
|
||||||
|
knownValueTypes.put( "activeCaptionBorder", ValueType.COLOR );
|
||||||
|
knownValueTypes.put( "inactiveCaptionBorder", ValueType.COLOR );
|
||||||
|
knownValueTypes.put( "windowBorder", ValueType.COLOR );
|
||||||
// SplitPane
|
// SplitPane
|
||||||
knownValueTypes.put( "SplitPane.dividerSize", ValueType.INTEGER );
|
knownValueTypes.put( "SplitPane.dividerSize", ValueType.INTEGER );
|
||||||
knownValueTypes.put( "SplitPaneDivider.gripDotSize", ValueType.INTEGER );
|
knownValueTypes.put( "SplitPaneDivider.gripDotSize", ValueType.INTEGER );
|
||||||
@@ -780,6 +784,7 @@ class UIDefaultsLoader
|
|||||||
case "tint": return parseColorMix( "#fff", params, resolver, reportError );
|
case "tint": return parseColorMix( "#fff", params, resolver, reportError );
|
||||||
case "shade": return parseColorMix( "#000", params, resolver, reportError );
|
case "shade": return parseColorMix( "#000", params, resolver, reportError );
|
||||||
case "contrast": return parseColorContrast( params, resolver, reportError );
|
case "contrast": return parseColorContrast( params, resolver, reportError );
|
||||||
|
case "over": return parseColorOver( params, resolver, reportError );
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
parseColorDepth--;
|
parseColorDepth--;
|
||||||
@@ -792,7 +797,7 @@ class UIDefaultsLoader
|
|||||||
* Syntax: if(condition,trueValue,falseValue)
|
* Syntax: if(condition,trueValue,falseValue)
|
||||||
* <p>
|
* <p>
|
||||||
* This "if" function is only used if the "if" is passed as parameter to another
|
* This "if" function is only used if the "if" is passed as parameter to another
|
||||||
* color function. Otherwise the general "if" function is used.
|
* color function. Otherwise, the general "if" function is used.
|
||||||
*/
|
*/
|
||||||
private static Object parseColorIf( String value, List<String> params, Function<String, String> resolver, boolean reportError ) {
|
private static Object parseColorIf( String value, List<String> params, Function<String, String> resolver, boolean reportError ) {
|
||||||
if( params.size() != 3 )
|
if( params.size() != 3 )
|
||||||
@@ -847,7 +852,7 @@ class UIDefaultsLoader
|
|||||||
int lightness = parsePercentage( params.get( 2 ) );
|
int lightness = parsePercentage( params.get( 2 ) );
|
||||||
int alpha = hasAlpha ? parsePercentage( params.get( 3 ) ) : 100;
|
int alpha = hasAlpha ? parsePercentage( params.get( 3 ) ) : 100;
|
||||||
|
|
||||||
float[] hsl = new float[] { hue, saturation, lightness };
|
float[] hsl = { hue, saturation, lightness };
|
||||||
return new ColorUIResource( HSLColor.toRGB( hsl, alpha / 100f ) );
|
return new ColorUIResource( HSLColor.toRGB( hsl, alpha / 100f ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1041,6 +1046,34 @@ class UIDefaultsLoader
|
|||||||
return parseColorOrFunction( resolver.apply( darkOrLightColor ), resolver, reportError );
|
return parseColorOrFunction( resolver.apply( darkOrLightColor ), resolver, reportError );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Syntax: over(foreground,background)
|
||||||
|
* - foreground: a foreground color (e.g. #f00) or a color function;
|
||||||
|
* the alpha of this color is used as weight to mix the two colors
|
||||||
|
* - background: a background color (e.g. #f00) or a color function
|
||||||
|
*/
|
||||||
|
private static ColorUIResource parseColorOver( List<String> params, Function<String, String> resolver, boolean reportError ) {
|
||||||
|
String foregroundStr = params.get( 0 );
|
||||||
|
String backgroundStr = params.get( 1 );
|
||||||
|
|
||||||
|
// parse foreground color
|
||||||
|
ColorUIResource foreground = (ColorUIResource) parseColorOrFunction( resolver.apply( foregroundStr ), resolver, reportError );
|
||||||
|
if( foreground == null || foreground.getAlpha() == 255 )
|
||||||
|
return foreground;
|
||||||
|
|
||||||
|
// foreground color without alpha
|
||||||
|
ColorUIResource foreground2 = new ColorUIResource( foreground.getRGB() );
|
||||||
|
|
||||||
|
// parse background color
|
||||||
|
ColorUIResource background = (ColorUIResource) parseColorOrFunction( resolver.apply( backgroundStr ), resolver, reportError );
|
||||||
|
if( background == null )
|
||||||
|
return foreground2;
|
||||||
|
|
||||||
|
// create new color
|
||||||
|
float weight = foreground.getAlpha() / 255f;
|
||||||
|
return new ColorUIResource( ColorFunctions.mix( foreground2, background, weight ) );
|
||||||
|
}
|
||||||
|
|
||||||
private static Object parseFunctionBaseColor( String colorStr, ColorFunction function,
|
private static Object parseFunctionBaseColor( String colorStr, ColorFunction function,
|
||||||
boolean derived, Function<String, String> resolver, boolean reportError )
|
boolean derived, Function<String, String> resolver, boolean reportError )
|
||||||
{
|
{
|
||||||
@@ -1209,7 +1242,7 @@ class UIDefaultsLoader
|
|||||||
}
|
}
|
||||||
|
|
||||||
Integer integer = parseInteger( value, true );
|
Integer integer = parseInteger( value, true );
|
||||||
if( integer.intValue() < min || integer.intValue() > max )
|
if( integer < min || integer > max )
|
||||||
throw new NumberFormatException( "integer '" + value + "' out of range (" + min + '-' + max + ')' );
|
throw new NumberFormatException( "integer '" + value + "' out of range (" + min + '-' + max + ')' );
|
||||||
return integer;
|
return integer;
|
||||||
}
|
}
|
||||||
@@ -1250,28 +1283,28 @@ class UIDefaultsLoader
|
|||||||
|
|
||||||
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 t -> {
|
||||||
return UIScale.scale( val );
|
return UIScale.scale( val );
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ActiveValue parseScaledFloat( String value ) {
|
private static ActiveValue parseScaledFloat( String value ) {
|
||||||
float val = parseFloat( value, true );
|
float val = parseFloat( value, true );
|
||||||
return (ActiveValue) t -> {
|
return t -> {
|
||||||
return UIScale.scale( val );
|
return UIScale.scale( val );
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ActiveValue parseScaledInsets( String value ) {
|
private static ActiveValue parseScaledInsets( String value ) {
|
||||||
Insets insets = parseInsets( value );
|
Insets insets = parseInsets( value );
|
||||||
return (ActiveValue) t -> {
|
return t -> {
|
||||||
return UIScale.scale( insets );
|
return UIScale.scale( insets );
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ActiveValue parseScaledDimension( String value ) {
|
private static ActiveValue parseScaledDimension( String value ) {
|
||||||
Dimension dimension = parseDimension( value );
|
Dimension dimension = parseDimension( value );
|
||||||
return (ActiveValue) t -> {
|
return t -> {
|
||||||
return UIScale.scale( dimension );
|
return UIScale.scale( dimension );
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,13 +23,13 @@ import java.awt.Graphics2D;
|
|||||||
import com.formdev.flatlaf.util.AnimatedIcon;
|
import com.formdev.flatlaf.util.AnimatedIcon;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Base class for animated icons that scales width and height, creates and initializes
|
* Base class for animated icons that scale width and height, creates and initializes
|
||||||
* a scaled graphics context for icon painting.
|
* a scaled graphics context for icon painting.
|
||||||
* <p>
|
* <p>
|
||||||
* Subclasses do not need to scale icon painting.
|
* Subclasses do not need to scale icon painting.
|
||||||
* <p>
|
* <p>
|
||||||
* This class does not store any state information (needed for animation) in its instance.
|
* This class does not store any state information (needed for animation) in its instance.
|
||||||
* Instead a client property is set on the painted component.
|
* Instead, a client property is set on the painted component.
|
||||||
* This makes it possible to use a share icon instance for multiple components.
|
* This makes it possible to use a share icon instance for multiple components.
|
||||||
*
|
*
|
||||||
* @author Karl Tauber
|
* @author Karl Tauber
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ public class FlatAscendingSortIcon
|
|||||||
boolean chevron = this.chevron;
|
boolean chevron = this.chevron;
|
||||||
Color sortIconColor = this.sortIconColor;
|
Color sortIconColor = this.sortIconColor;
|
||||||
|
|
||||||
// Because this icons are always shared for all table headers,
|
// Because this icon is always shared for all table headers,
|
||||||
// get icon specific style from FlatTableHeaderUI.
|
// get icon specific style from FlatTableHeaderUI.
|
||||||
JTableHeader tableHeader = (JTableHeader) SwingUtilities.getAncestorOfClass( JTableHeader.class, c );
|
JTableHeader tableHeader = (JTableHeader) SwingUtilities.getAncestorOfClass( JTableHeader.class, c );
|
||||||
if( tableHeader != null ) {
|
if( tableHeader != null ) {
|
||||||
|
|||||||
@@ -49,6 +49,14 @@ public class FlatCapsLockIcon
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @since 2.5 */
|
||||||
|
public Object getStyleableValue( String key ) {
|
||||||
|
switch( key ) {
|
||||||
|
case "capsLockIconColor": return color;
|
||||||
|
default: return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void paintIcon( Component c, Graphics2D g ) {
|
protected void paintIcon( Component c, Graphics2D g ) {
|
||||||
/*
|
/*
|
||||||
|
|||||||
@@ -172,6 +172,11 @@ public class FlatCheckBoxIcon
|
|||||||
return FlatStylingSupport.getAnnotatedStyleableInfos( this );
|
return FlatStylingSupport.getAnnotatedStyleableInfos( this );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @since 2.5 */
|
||||||
|
public Object getStyleableValue( String key ) {
|
||||||
|
return FlatStylingSupport.getAnnotatedStyleableValue( this, key );
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void paintIcon( Component c, Graphics2D g ) {
|
protected void paintIcon( Component c, Graphics2D g ) {
|
||||||
boolean indeterminate = isIndeterminate( c );
|
boolean indeterminate = isIndeterminate( c );
|
||||||
|
|||||||
@@ -59,6 +59,11 @@ public class FlatCheckBoxMenuItemIcon
|
|||||||
return FlatStylingSupport.getAnnotatedStyleableInfos( this );
|
return FlatStylingSupport.getAnnotatedStyleableInfos( this );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @since 2.5 */
|
||||||
|
public Object getStyleableValue( String key ) {
|
||||||
|
return FlatStylingSupport.getAnnotatedStyleableValue( this, key );
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void paintIcon( Component c, Graphics2D g2 ) {
|
protected void paintIcon( Component c, Graphics2D g2 ) {
|
||||||
boolean selected = (c instanceof AbstractButton) && ((AbstractButton)c).isSelected();
|
boolean selected = (c instanceof AbstractButton) && ((AbstractButton)c).isSelected();
|
||||||
|
|||||||
@@ -69,6 +69,11 @@ public class FlatClearIcon
|
|||||||
return FlatStylingSupport.getAnnotatedStyleableInfos( this );
|
return FlatStylingSupport.getAnnotatedStyleableInfos( this );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @since 2.5 */
|
||||||
|
public Object getStyleableValue( String key ) {
|
||||||
|
return FlatStylingSupport.getAnnotatedStyleableValue( this, key );
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void paintIcon( Component c, Graphics2D g ) {
|
protected void paintIcon( Component c, Graphics2D g ) {
|
||||||
if( !ignoreButtonState && c instanceof AbstractButton ) {
|
if( !ignoreButtonState && c instanceof AbstractButton ) {
|
||||||
|
|||||||
@@ -84,6 +84,11 @@ public class FlatHelpButtonIcon
|
|||||||
return FlatStylingSupport.getAnnotatedStyleableInfos( this );
|
return FlatStylingSupport.getAnnotatedStyleableInfos( this );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @since 2.5 */
|
||||||
|
public Object getStyleableValue( String key ) {
|
||||||
|
return FlatStylingSupport.getAnnotatedStyleableValue( this, key );
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void paintIcon( Component c, Graphics2D g2 ) {
|
protected void paintIcon( Component c, Graphics2D g2 ) {
|
||||||
/*
|
/*
|
||||||
@@ -96,8 +101,8 @@ public class FlatHelpButtonIcon
|
|||||||
</svg>
|
</svg>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
boolean enabled = c.isEnabled();
|
boolean enabled = c == null || c.isEnabled();
|
||||||
boolean focused = FlatUIUtils.isPermanentFocusOwner( c );
|
boolean focused = c != null && FlatUIUtils.isPermanentFocusOwner( c );
|
||||||
|
|
||||||
float xy = 0.5f;
|
float xy = 0.5f;
|
||||||
float wh = iconSize() - 1;
|
float wh = iconSize() - 1;
|
||||||
|
|||||||
@@ -61,9 +61,14 @@ public class FlatMenuArrowIcon
|
|||||||
return FlatStylingSupport.getAnnotatedStyleableInfos( this );
|
return FlatStylingSupport.getAnnotatedStyleableInfos( this );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @since 2.5 */
|
||||||
|
public Object getStyleableValue( String key ) {
|
||||||
|
return FlatStylingSupport.getAnnotatedStyleableValue( this, key );
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void paintIcon( Component c, Graphics2D g ) {
|
protected void paintIcon( Component c, Graphics2D g ) {
|
||||||
if( !c.getComponentOrientation().isLeftToRight() )
|
if( c != null && !c.getComponentOrientation().isLeftToRight() )
|
||||||
g.rotate( Math.toRadians( 180 ), width / 2., height / 2. );
|
g.rotate( Math.toRadians( 180 ), width / 2., height / 2. );
|
||||||
|
|
||||||
g.setColor( getArrowColor( c ) );
|
g.setColor( getArrowColor( c ) );
|
||||||
@@ -82,7 +87,7 @@ public class FlatMenuArrowIcon
|
|||||||
if( c instanceof JMenu && ((JMenu)c).isSelected() && !isUnderlineSelection() )
|
if( c instanceof JMenu && ((JMenu)c).isSelected() && !isUnderlineSelection() )
|
||||||
return selectionForeground;
|
return selectionForeground;
|
||||||
|
|
||||||
return c.isEnabled() ? arrowColor : disabledArrowColor;
|
return c == null || c.isEnabled() ? arrowColor : disabledArrowColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected boolean isUnderlineSelection() {
|
protected boolean isUnderlineSelection() {
|
||||||
|
|||||||
@@ -67,6 +67,11 @@ public class FlatSearchIcon
|
|||||||
return FlatStylingSupport.getAnnotatedStyleableInfos( this );
|
return FlatStylingSupport.getAnnotatedStyleableInfos( this );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @since 2.5 */
|
||||||
|
public Object getStyleableValue( String key ) {
|
||||||
|
return FlatStylingSupport.getAnnotatedStyleableValue( this, key );
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void paintIcon( Component c, Graphics2D g ) {
|
protected void paintIcon( Component c, Graphics2D g ) {
|
||||||
/*
|
/*
|
||||||
|
|||||||
@@ -76,6 +76,11 @@ public class FlatTabbedPaneCloseIcon
|
|||||||
return FlatStylingSupport.getAnnotatedStyleableInfos( this );
|
return FlatStylingSupport.getAnnotatedStyleableInfos( this );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @since 2.5 */
|
||||||
|
public Object getStyleableValue( String key ) {
|
||||||
|
return FlatStylingSupport.getAnnotatedStyleableValue( this, key );
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void paintIcon( Component c, Graphics2D g ) {
|
protected void paintIcon( Component c, Graphics2D g ) {
|
||||||
// paint background
|
// paint background
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ public class FlatTreeCollapsedIcon
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Because this icons are always shared for all trees,
|
* Because this icon is always shared for all trees,
|
||||||
* get icon specific style from FlatTreeUI.
|
* get icon specific style from FlatTreeUI.
|
||||||
*/
|
*/
|
||||||
static <T> T getStyleFromTreeUI( Component c, Function<FlatTreeUI, T> f ) {
|
static <T> T getStyleFromTreeUI( Component c, Function<FlatTreeUI, T> f ) {
|
||||||
|
|||||||
@@ -18,8 +18,8 @@ package com.formdev.flatlaf.resources;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* The only purpose of this file is to add a .class file to this package to make it non-empty.
|
* The only purpose of this file is to add a .class file to this package to make it non-empty.
|
||||||
* Otherwise the compiler outputs a warning because this package is opend in module-info.java.
|
* Otherwise, the compiler outputs a warning because this package is opened in module-info.java.
|
||||||
* Also when using --patch-module (e.g. from an IDE), an error would occur for empty packages.
|
* Also, when using --patch-module (e.g. from an IDE), an error would occur for empty packages.
|
||||||
*
|
*
|
||||||
* @author Karl Tauber
|
* @author Karl Tauber
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ public class FlatArrowButton
|
|||||||
extends BasicArrowButton
|
extends BasicArrowButton
|
||||||
implements UIResource
|
implements UIResource
|
||||||
{
|
{
|
||||||
public static final int DEFAULT_ARROW_WIDTH = 8;
|
public static final int DEFAULT_ARROW_WIDTH = 9;
|
||||||
|
|
||||||
protected boolean chevron;
|
protected boolean chevron;
|
||||||
protected Color foreground;
|
protected Color foreground;
|
||||||
@@ -211,6 +211,6 @@ public class FlatArrowButton
|
|||||||
if( vert && parent instanceof JComponent && FlatUIUtils.hasRoundBorder( (JComponent) parent ) )
|
if( vert && parent instanceof JComponent && FlatUIUtils.hasRoundBorder( (JComponent) parent ) )
|
||||||
x -= scale( parent.getComponentOrientation().isLeftToRight() ? 1 : -1 );
|
x -= scale( parent.getComponentOrientation().isLeftToRight() ? 1 : -1 );
|
||||||
|
|
||||||
FlatUIUtils.paintArrow( g, x, 0, getWidth(), getHeight(), getDirection(), chevron, arrowWidth, xOffset, yOffset );
|
FlatUIUtils.paintArrow( g, x, 0, getWidth(), getHeight(), getDirection(), chevron, getArrowWidth(), getXOffset(), getYOffset() );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -101,6 +101,12 @@ public class FlatBorder
|
|||||||
return FlatStylingSupport.getAnnotatedStyleableInfos( this );
|
return FlatStylingSupport.getAnnotatedStyleableInfos( this );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @since 2.5 */
|
||||||
|
@Override
|
||||||
|
public Object getStyleableValue( String key ) {
|
||||||
|
return FlatStylingSupport.getAnnotatedStyleableValue( this, key );
|
||||||
|
}
|
||||||
|
|
||||||
@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();
|
||||||
|
|||||||
@@ -159,7 +159,7 @@ public class FlatButtonBorder
|
|||||||
public Insets getBorderInsets( Component c, Insets insets ) {
|
public Insets getBorderInsets( Component c, Insets insets ) {
|
||||||
if( FlatButtonUI.isToolBarButton( c ) ) {
|
if( FlatButtonUI.isToolBarButton( c ) ) {
|
||||||
// In toolbars, use button margin only if explicitly set.
|
// In toolbars, use button margin only if explicitly set.
|
||||||
// Otherwise use toolbar margin specified in UI defaults.
|
// Otherwise, use toolbar margin specified in UI defaults.
|
||||||
Insets margin = (c instanceof AbstractButton)
|
Insets margin = (c instanceof AbstractButton)
|
||||||
? ((AbstractButton)c).getMargin()
|
? ((AbstractButton)c).getMargin()
|
||||||
: null;
|
: null;
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ import javax.swing.JTextField;
|
|||||||
import javax.swing.JToggleButton;
|
import javax.swing.JToggleButton;
|
||||||
import javax.swing.JToolBar;
|
import javax.swing.JToolBar;
|
||||||
import javax.swing.LookAndFeel;
|
import javax.swing.LookAndFeel;
|
||||||
|
import javax.swing.SwingConstants;
|
||||||
import javax.swing.SwingUtilities;
|
import javax.swing.SwingUtilities;
|
||||||
import javax.swing.UIManager;
|
import javax.swing.UIManager;
|
||||||
import javax.swing.plaf.ButtonUI;
|
import javax.swing.plaf.ButtonUI;
|
||||||
@@ -77,20 +78,27 @@ import com.formdev.flatlaf.util.UIScale;
|
|||||||
* @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
|
||||||
* @uiDefault Button.endBackground Color optional; if set, a gradient paint is used
|
* @uiDefault Button.endBackground Color optional; if set, a gradient paint is used
|
||||||
* @uiDefault Button.focusedBackground Color optional
|
* @uiDefault Button.focusedBackground Color optional
|
||||||
|
* @uiDefault Button.focusedForeground Color optional
|
||||||
* @uiDefault Button.hoverBackground Color optional
|
* @uiDefault Button.hoverBackground Color optional
|
||||||
|
* @uiDefault Button.hoverForeground Color optional
|
||||||
* @uiDefault Button.pressedBackground Color optional
|
* @uiDefault Button.pressedBackground Color optional
|
||||||
|
* @uiDefault Button.pressedForeground Color optional
|
||||||
* @uiDefault Button.selectedBackground Color
|
* @uiDefault Button.selectedBackground Color
|
||||||
* @uiDefault Button.selectedForeground Color
|
* @uiDefault Button.selectedForeground Color
|
||||||
* @uiDefault Button.disabledBackground Color optional
|
* @uiDefault Button.disabledBackground Color optional
|
||||||
* @uiDefault Button.disabledText Color
|
* @uiDefault Button.disabledText Color
|
||||||
* @uiDefault Button.disabledSelectedBackground Color
|
* @uiDefault Button.disabledSelectedBackground Color
|
||||||
|
* @uiDefault Button.disabledSelectedForeground Color optional
|
||||||
* @uiDefault Button.default.background Color
|
* @uiDefault Button.default.background Color
|
||||||
* @uiDefault Button.default.startBackground Color optional; if set, a gradient paint is used and Button.default.background is ignored
|
* @uiDefault Button.default.startBackground Color optional; if set, a gradient paint is used and Button.default.background is ignored
|
||||||
* @uiDefault Button.default.endBackground Color optional; if set, a gradient paint is used
|
* @uiDefault Button.default.endBackground Color optional; if set, a gradient paint is used
|
||||||
* @uiDefault Button.default.foreground Color
|
* @uiDefault Button.default.foreground Color
|
||||||
* @uiDefault Button.default.focusedBackground Color optional
|
* @uiDefault Button.default.focusedBackground Color optional
|
||||||
|
* @uiDefault Button.default.focusedForeground Color optional
|
||||||
* @uiDefault Button.default.hoverBackground Color optional
|
* @uiDefault Button.default.hoverBackground Color optional
|
||||||
|
* @uiDefault Button.default.hoverForeground Color optional
|
||||||
* @uiDefault Button.default.pressedBackground Color optional
|
* @uiDefault Button.default.pressedBackground Color optional
|
||||||
|
* @uiDefault Button.default.pressedForeground Color optional
|
||||||
* @uiDefault Button.default.boldText boolean
|
* @uiDefault Button.default.boldText boolean
|
||||||
* @uiDefault Button.paintShadow boolean default is false
|
* @uiDefault Button.paintShadow boolean default is false
|
||||||
* @uiDefault Button.shadowWidth int default is 2
|
* @uiDefault Button.shadowWidth int default is 2
|
||||||
@@ -98,8 +106,13 @@ import com.formdev.flatlaf.util.UIScale;
|
|||||||
* @uiDefault Button.default.shadowColor Color optional
|
* @uiDefault Button.default.shadowColor Color optional
|
||||||
* @uiDefault Button.toolbar.spacingInsets Insets
|
* @uiDefault Button.toolbar.spacingInsets Insets
|
||||||
* @uiDefault Button.toolbar.hoverBackground Color
|
* @uiDefault Button.toolbar.hoverBackground Color
|
||||||
|
* @uiDefault Button.toolbar.hoverForeground Color optional
|
||||||
* @uiDefault Button.toolbar.pressedBackground Color
|
* @uiDefault Button.toolbar.pressedBackground Color
|
||||||
|
* @uiDefault Button.toolbar.pressedForeground Color optional
|
||||||
* @uiDefault Button.toolbar.selectedBackground Color
|
* @uiDefault Button.toolbar.selectedBackground Color
|
||||||
|
* @uiDefault Button.toolbar.selectedForeground Color optional
|
||||||
|
* @uiDefault Button.toolbar.disabledSelectedBackground Color optional
|
||||||
|
* @uiDefault Button.toolbar.disabledSelectedForeground Color optional
|
||||||
*
|
*
|
||||||
* @author Karl Tauber
|
* @author Karl Tauber
|
||||||
*/
|
*/
|
||||||
@@ -116,20 +129,27 @@ public class FlatButtonUI
|
|||||||
protected Color startBackground;
|
protected Color startBackground;
|
||||||
protected Color endBackground;
|
protected Color endBackground;
|
||||||
@Styleable protected Color focusedBackground;
|
@Styleable protected Color focusedBackground;
|
||||||
|
/** @since 2.3 */ @Styleable protected Color focusedForeground;
|
||||||
@Styleable protected Color hoverBackground;
|
@Styleable protected Color hoverBackground;
|
||||||
|
/** @since 2.3 */ @Styleable protected Color hoverForeground;
|
||||||
@Styleable protected Color pressedBackground;
|
@Styleable protected Color pressedBackground;
|
||||||
|
/** @since 2.3 */ @Styleable protected Color pressedForeground;
|
||||||
@Styleable protected Color selectedBackground;
|
@Styleable protected Color selectedBackground;
|
||||||
@Styleable protected Color selectedForeground;
|
@Styleable protected Color selectedForeground;
|
||||||
@Styleable protected Color disabledBackground;
|
@Styleable protected Color disabledBackground;
|
||||||
@Styleable protected Color disabledText;
|
@Styleable protected Color disabledText;
|
||||||
@Styleable protected Color disabledSelectedBackground;
|
@Styleable protected Color disabledSelectedBackground;
|
||||||
|
/** @since 2.3 */ @Styleable protected Color disabledSelectedForeground;
|
||||||
|
|
||||||
@Styleable(dot=true) protected Color defaultBackground;
|
@Styleable(dot=true) protected Color defaultBackground;
|
||||||
protected Color defaultEndBackground;
|
protected Color defaultEndBackground;
|
||||||
@Styleable(dot=true) protected Color defaultForeground;
|
@Styleable(dot=true) protected Color defaultForeground;
|
||||||
@Styleable(dot=true) protected Color defaultFocusedBackground;
|
@Styleable(dot=true) protected Color defaultFocusedBackground;
|
||||||
|
/** @since 2.3 */ @Styleable(dot=true) protected Color defaultFocusedForeground;
|
||||||
@Styleable(dot=true) protected Color defaultHoverBackground;
|
@Styleable(dot=true) protected Color defaultHoverBackground;
|
||||||
|
/** @since 2.3 */ @Styleable(dot=true) protected Color defaultHoverForeground;
|
||||||
@Styleable(dot=true) protected Color defaultPressedBackground;
|
@Styleable(dot=true) protected Color defaultPressedBackground;
|
||||||
|
/** @since 2.3 */ @Styleable(dot=true) protected Color defaultPressedForeground;
|
||||||
@Styleable(dot=true) protected boolean defaultBoldText;
|
@Styleable(dot=true) protected boolean defaultBoldText;
|
||||||
|
|
||||||
@Styleable protected boolean paintShadow;
|
@Styleable protected boolean paintShadow;
|
||||||
@@ -138,8 +158,13 @@ public class FlatButtonUI
|
|||||||
@Styleable(dot=true) protected Color defaultShadowColor;
|
@Styleable(dot=true) protected Color defaultShadowColor;
|
||||||
|
|
||||||
@Styleable(dot=true) protected Color toolbarHoverBackground;
|
@Styleable(dot=true) protected Color toolbarHoverBackground;
|
||||||
|
/** @since 2.3 */ @Styleable(dot=true) protected Color toolbarHoverForeground;
|
||||||
@Styleable(dot=true) protected Color toolbarPressedBackground;
|
@Styleable(dot=true) protected Color toolbarPressedBackground;
|
||||||
|
/** @since 2.3 */ @Styleable(dot=true) protected Color toolbarPressedForeground;
|
||||||
@Styleable(dot=true) protected Color toolbarSelectedBackground;
|
@Styleable(dot=true) protected Color toolbarSelectedBackground;
|
||||||
|
/** @since 2.3 */ @Styleable(dot=true) protected Color toolbarSelectedForeground;
|
||||||
|
/** @since 2.3 */ @Styleable(dot=true) protected Color toolbarDisabledSelectedBackground;
|
||||||
|
/** @since 2.3 */ @Styleable(dot=true) protected Color toolbarDisabledSelectedForeground;
|
||||||
|
|
||||||
// only used via styling (not in UI defaults, but has likewise client properties)
|
// only used via styling (not in UI defaults, but has likewise client properties)
|
||||||
/** @since 2 */ @Styleable protected String buttonType;
|
/** @since 2 */ @Styleable protected String buttonType;
|
||||||
@@ -156,7 +181,7 @@ public class FlatButtonUI
|
|||||||
private AtomicBoolean borderShared;
|
private AtomicBoolean borderShared;
|
||||||
|
|
||||||
public static ComponentUI createUI( JComponent c ) {
|
public static ComponentUI createUI( JComponent c ) {
|
||||||
return FlatUIUtils.canUseSharedUI( c )
|
return FlatUIUtils.canUseSharedUI( c ) && !FlatUIUtils.needsLightAWTPeer( c )
|
||||||
? FlatUIUtils.createSharedUI( FlatButtonUI.class, () -> new FlatButtonUI( true ) )
|
? FlatUIUtils.createSharedUI( FlatButtonUI.class, () -> new FlatButtonUI( true ) )
|
||||||
: new FlatButtonUI( false );
|
: new FlatButtonUI( false );
|
||||||
}
|
}
|
||||||
@@ -168,6 +193,13 @@ public class FlatButtonUI
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void installUI( JComponent c ) {
|
public void installUI( JComponent c ) {
|
||||||
|
if( FlatUIUtils.needsLightAWTPeer( c ) )
|
||||||
|
FlatUIUtils.runWithLightAWTPeerUIDefaults( () -> installUIImpl( c ) );
|
||||||
|
else
|
||||||
|
installUIImpl( c );
|
||||||
|
}
|
||||||
|
|
||||||
|
private void installUIImpl( JComponent c ) {
|
||||||
super.installUI( c );
|
super.installUI( c );
|
||||||
|
|
||||||
installStyle( (AbstractButton) c );
|
installStyle( (AbstractButton) c );
|
||||||
@@ -189,20 +221,27 @@ public class FlatButtonUI
|
|||||||
startBackground = UIManager.getColor( prefix + "startBackground" );
|
startBackground = UIManager.getColor( prefix + "startBackground" );
|
||||||
endBackground = UIManager.getColor( prefix + "endBackground" );
|
endBackground = UIManager.getColor( prefix + "endBackground" );
|
||||||
focusedBackground = UIManager.getColor( prefix + "focusedBackground" );
|
focusedBackground = UIManager.getColor( prefix + "focusedBackground" );
|
||||||
|
focusedForeground = UIManager.getColor( prefix + "focusedForeground" );
|
||||||
hoverBackground = UIManager.getColor( prefix + "hoverBackground" );
|
hoverBackground = UIManager.getColor( prefix + "hoverBackground" );
|
||||||
|
hoverForeground = UIManager.getColor( prefix + "hoverForeground" );
|
||||||
pressedBackground = UIManager.getColor( prefix + "pressedBackground" );
|
pressedBackground = UIManager.getColor( prefix + "pressedBackground" );
|
||||||
|
pressedForeground = UIManager.getColor( prefix + "pressedForeground" );
|
||||||
selectedBackground = UIManager.getColor( prefix + "selectedBackground" );
|
selectedBackground = UIManager.getColor( prefix + "selectedBackground" );
|
||||||
selectedForeground = UIManager.getColor( prefix + "selectedForeground" );
|
selectedForeground = UIManager.getColor( prefix + "selectedForeground" );
|
||||||
disabledBackground = UIManager.getColor( prefix + "disabledBackground" );
|
disabledBackground = UIManager.getColor( prefix + "disabledBackground" );
|
||||||
disabledText = UIManager.getColor( prefix + "disabledText" );
|
disabledText = UIManager.getColor( prefix + "disabledText" );
|
||||||
disabledSelectedBackground = UIManager.getColor( prefix + "disabledSelectedBackground" );
|
disabledSelectedBackground = UIManager.getColor( prefix + "disabledSelectedBackground" );
|
||||||
|
disabledSelectedForeground = UIManager.getColor( prefix + "disabledSelectedForeground" );
|
||||||
|
|
||||||
defaultBackground = FlatUIUtils.getUIColor( "Button.default.startBackground", "Button.default.background" );
|
defaultBackground = FlatUIUtils.getUIColor( "Button.default.startBackground", "Button.default.background" );
|
||||||
defaultEndBackground = UIManager.getColor( "Button.default.endBackground" );
|
defaultEndBackground = UIManager.getColor( "Button.default.endBackground" );
|
||||||
defaultForeground = UIManager.getColor( "Button.default.foreground" );
|
defaultForeground = UIManager.getColor( "Button.default.foreground" );
|
||||||
defaultFocusedBackground = UIManager.getColor( "Button.default.focusedBackground" );
|
defaultFocusedBackground = UIManager.getColor( "Button.default.focusedBackground" );
|
||||||
|
defaultFocusedForeground = UIManager.getColor( "Button.default.focusedForeground" );
|
||||||
defaultHoverBackground = UIManager.getColor( "Button.default.hoverBackground" );
|
defaultHoverBackground = UIManager.getColor( "Button.default.hoverBackground" );
|
||||||
|
defaultHoverForeground = UIManager.getColor( "Button.default.hoverForeground" );
|
||||||
defaultPressedBackground = UIManager.getColor( "Button.default.pressedBackground" );
|
defaultPressedBackground = UIManager.getColor( "Button.default.pressedBackground" );
|
||||||
|
defaultPressedForeground = UIManager.getColor( "Button.default.pressedForeground" );
|
||||||
defaultBoldText = UIManager.getBoolean( "Button.default.boldText" );
|
defaultBoldText = UIManager.getBoolean( "Button.default.boldText" );
|
||||||
|
|
||||||
paintShadow = UIManager.getBoolean( "Button.paintShadow" );
|
paintShadow = UIManager.getBoolean( "Button.paintShadow" );
|
||||||
@@ -211,8 +250,13 @@ public class FlatButtonUI
|
|||||||
defaultShadowColor = UIManager.getColor( "Button.default.shadowColor" );
|
defaultShadowColor = UIManager.getColor( "Button.default.shadowColor" );
|
||||||
|
|
||||||
toolbarHoverBackground = UIManager.getColor( prefix + "toolbar.hoverBackground" );
|
toolbarHoverBackground = UIManager.getColor( prefix + "toolbar.hoverBackground" );
|
||||||
|
toolbarHoverForeground = UIManager.getColor( prefix + "toolbar.hoverForeground" );
|
||||||
toolbarPressedBackground = UIManager.getColor( prefix + "toolbar.pressedBackground" );
|
toolbarPressedBackground = UIManager.getColor( prefix + "toolbar.pressedBackground" );
|
||||||
|
toolbarPressedForeground = UIManager.getColor( prefix + "toolbar.pressedForeground" );
|
||||||
toolbarSelectedBackground = UIManager.getColor( prefix + "toolbar.selectedBackground" );
|
toolbarSelectedBackground = UIManager.getColor( prefix + "toolbar.selectedBackground" );
|
||||||
|
toolbarSelectedForeground = UIManager.getColor( prefix + "toolbar.selectedForeground" );
|
||||||
|
toolbarDisabledSelectedBackground = UIManager.getColor( prefix + "toolbar.disabledSelectedBackground" );
|
||||||
|
toolbarDisabledSelectedForeground = UIManager.getColor( prefix + "toolbar.disabledSelectedForeground" );
|
||||||
|
|
||||||
helpButtonIcon = UIManager.getIcon( "HelpButton.icon" );
|
helpButtonIcon = UIManager.getIcon( "HelpButton.icon" );
|
||||||
defaultMargin = UIManager.getInsets( prefix + "margin" );
|
defaultMargin = UIManager.getInsets( prefix + "margin" );
|
||||||
@@ -262,6 +306,10 @@ public class FlatButtonUI
|
|||||||
b.repaint();
|
b.repaint();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case OUTLINE:
|
||||||
|
b.repaint();
|
||||||
|
break;
|
||||||
|
|
||||||
case STYLE:
|
case STYLE:
|
||||||
case STYLE_CLASS:
|
case STYLE_CLASS:
|
||||||
if( shared && FlatStylingSupport.hasStyleProperty( b ) ) {
|
if( shared && FlatStylingSupport.hasStyleProperty( b ) ) {
|
||||||
@@ -325,6 +373,18 @@ public class FlatButtonUI
|
|||||||
return infos;
|
return infos;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @since 2.5 */
|
||||||
|
@Override
|
||||||
|
public Object getStyleableValue( JComponent c, String key ) {
|
||||||
|
if( key.startsWith( "help." ) ) {
|
||||||
|
return (helpButtonIcon instanceof FlatHelpButtonIcon)
|
||||||
|
? ((FlatHelpButtonIcon)helpButtonIcon).getStyleableValue( key.substring( "help.".length() ) )
|
||||||
|
: null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return FlatStylingSupport.getAnnotatedStyleableValue( this, c.getBorder(), key );
|
||||||
|
}
|
||||||
|
|
||||||
static boolean isContentAreaFilled( Component c ) {
|
static boolean isContentAreaFilled( Component c ) {
|
||||||
return !(c instanceof AbstractButton) || ((AbstractButton)c).isContentAreaFilled();
|
return !(c instanceof AbstractButton) || ((AbstractButton)c).isContentAreaFilled();
|
||||||
}
|
}
|
||||||
@@ -339,7 +399,7 @@ public class FlatButtonUI
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if the button has an icon but no text,
|
* 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.
|
* or it does not have an icon and the text is either "..." or one character.
|
||||||
*/
|
*/
|
||||||
static boolean isIconOnlyOrSingleCharacterButton( Component c ) {
|
static boolean isIconOnlyOrSingleCharacterButton( Component c ) {
|
||||||
if( !(c instanceof JButton) && !(c instanceof JToggleButton) )
|
if( !(c instanceof JButton) && !(c instanceof JToggleButton) )
|
||||||
@@ -490,6 +550,23 @@ public class FlatButtonUI
|
|||||||
super.paint( FlatLabelUI.createGraphicsHTMLTextYCorrection( g, c ), c );
|
super.paint( FlatLabelUI.createGraphicsHTMLTextYCorrection( g, c ), c );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void paintIcon( Graphics g, JComponent c, Rectangle iconRect ) {
|
||||||
|
// correct icon location when using bold font for default button
|
||||||
|
int xOffset = defaultBoldPlainWidthDiff( c ) / 2;
|
||||||
|
if( xOffset > 0 ) {
|
||||||
|
boolean ltr = c.getComponentOrientation().isLeftToRight();
|
||||||
|
switch( ((AbstractButton)c).getHorizontalTextPosition() ) {
|
||||||
|
case SwingConstants.RIGHT: iconRect.x -= xOffset; break;
|
||||||
|
case SwingConstants.LEFT: iconRect.x += xOffset; break;
|
||||||
|
case SwingConstants.TRAILING: iconRect.x -= ltr ? xOffset : -xOffset; break;
|
||||||
|
case SwingConstants.LEADING: iconRect.x += ltr ? xOffset : -xOffset; break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
super.paintIcon( g, c, iconRect );
|
||||||
|
}
|
||||||
|
|
||||||
@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 ) )
|
||||||
@@ -510,6 +587,8 @@ public class FlatButtonUI
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static void paintText( Graphics g, AbstractButton b, Rectangle textRect, String text, Color foreground ) {
|
public static void paintText( Graphics g, AbstractButton b, Rectangle textRect, String text, Color foreground ) {
|
||||||
|
if(foreground == null)
|
||||||
|
foreground=Color.red;
|
||||||
FontMetrics fm = b.getFontMetrics( b.getFont() );
|
FontMetrics fm = b.getFontMetrics( b.getFont() );
|
||||||
int mnemonicIndex = FlatLaf.isShowMnemonics() ? b.getDisplayedMnemonicIndex() : -1;
|
int mnemonicIndex = FlatLaf.isShowMnemonics() ? b.getDisplayedMnemonicIndex() : -1;
|
||||||
|
|
||||||
@@ -523,11 +602,14 @@ public class FlatButtonUI
|
|||||||
|
|
||||||
// selected state
|
// selected state
|
||||||
if( ((AbstractButton)c).isSelected() ) {
|
if( ((AbstractButton)c).isSelected() ) {
|
||||||
// in toolbar use same background colors for disabled and enabled because
|
// in toolbar, if toolbarDisabledSelectedBackground is null,
|
||||||
|
// use same background colors for disabled and enabled because
|
||||||
// we assume that toolbar icon is shown disabled
|
// we assume that toolbar icon is shown disabled
|
||||||
return buttonStateColor( c,
|
return buttonStateColor( c,
|
||||||
toolBarButton ? toolbarSelectedBackground : selectedBackground,
|
toolBarButton ? toolbarSelectedBackground : selectedBackground,
|
||||||
toolBarButton ? toolbarSelectedBackground : disabledSelectedBackground,
|
toolBarButton
|
||||||
|
? (toolbarDisabledSelectedBackground != null ? toolbarDisabledSelectedBackground : toolbarSelectedBackground)
|
||||||
|
: disabledSelectedBackground,
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
toolBarButton ? toolbarPressedBackground : pressedBackground );
|
toolBarButton ? toolbarPressedBackground : pressedBackground );
|
||||||
@@ -554,6 +636,9 @@ public class FlatButtonUI
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected Color getBackgroundBase( JComponent c, boolean def ) {
|
protected Color getBackgroundBase( JComponent c, boolean def ) {
|
||||||
|
if( FlatUIUtils.isAWTPeer( c ) )
|
||||||
|
return background;
|
||||||
|
|
||||||
// use component background if explicitly set
|
// use component background if explicitly set
|
||||||
Color bg = c.getBackground();
|
Color bg = c.getBackground();
|
||||||
if( isCustomBackground( bg ) )
|
if( isCustomBackground( bg ) )
|
||||||
@@ -569,6 +654,9 @@ public class FlatButtonUI
|
|||||||
public static Color buttonStateColor( Component c, Color enabledColor, Color disabledColor,
|
public static Color buttonStateColor( Component c, Color enabledColor, Color disabledColor,
|
||||||
Color focusedColor, Color hoverColor, Color pressedColor )
|
Color focusedColor, Color hoverColor, Color pressedColor )
|
||||||
{
|
{
|
||||||
|
if( c == null )
|
||||||
|
return enabledColor;
|
||||||
|
|
||||||
if( !c.isEnabled() )
|
if( !c.isEnabled() )
|
||||||
return disabledColor;
|
return disabledColor;
|
||||||
|
|
||||||
@@ -589,18 +677,48 @@ public class FlatButtonUI
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected Color getForeground( JComponent c ) {
|
protected Color getForeground( JComponent c ) {
|
||||||
if( !c.isEnabled() )
|
boolean toolBarButton = isToolBarButton( c ) || isBorderlessButton( c );
|
||||||
return disabledText;
|
|
||||||
|
|
||||||
if( ((AbstractButton)c).isSelected() && !(isToolBarButton( c ) || isBorderlessButton( c )) )
|
// selected state
|
||||||
return selectedForeground;
|
if( ((AbstractButton)c).isSelected() ) {
|
||||||
|
return buttonStateColor( c,
|
||||||
|
toolBarButton
|
||||||
|
? (toolbarSelectedForeground != null ? toolbarSelectedForeground : c.getForeground())
|
||||||
|
: selectedForeground,
|
||||||
|
toolBarButton
|
||||||
|
? (toolbarDisabledSelectedForeground != null ? toolbarDisabledSelectedForeground : disabledText)
|
||||||
|
: (disabledSelectedForeground != null ? disabledSelectedForeground : disabledText),
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
toolBarButton ? toolbarPressedForeground : pressedForeground );
|
||||||
|
}
|
||||||
|
|
||||||
|
// toolbar button
|
||||||
|
if( toolBarButton ) {
|
||||||
|
return buttonStateColor( c,
|
||||||
|
c.getForeground(),
|
||||||
|
disabledText,
|
||||||
|
null,
|
||||||
|
toolbarHoverForeground,
|
||||||
|
toolbarPressedForeground );
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean def = isDefaultButton( c );
|
||||||
|
return buttonStateColor( c,
|
||||||
|
getForegroundBase( c, def ),
|
||||||
|
disabledText,
|
||||||
|
isCustomForeground( c.getForeground() ) ? null : (def ? defaultFocusedForeground : focusedForeground),
|
||||||
|
def ? defaultHoverForeground : hoverForeground,
|
||||||
|
def ? defaultPressedForeground : pressedForeground );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2.3 */
|
||||||
|
protected Color getForegroundBase( JComponent c, boolean def ) {
|
||||||
// use component foreground if explicitly set
|
// use component foreground if explicitly set
|
||||||
Color fg = c.getForeground();
|
Color fg = c.getForeground();
|
||||||
if( isCustomForeground( fg ) )
|
if( isCustomForeground( fg ) )
|
||||||
return fg;
|
return fg;
|
||||||
|
|
||||||
boolean def = isDefaultButton( c );
|
|
||||||
return def ? defaultForeground : fg;
|
return def ? defaultForeground : fg;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -617,6 +735,9 @@ public class FlatButtonUI
|
|||||||
if( prefSize == null )
|
if( prefSize == null )
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
|
// increase width when using bold font for default button
|
||||||
|
prefSize.width += defaultBoldPlainWidthDiff( c );
|
||||||
|
|
||||||
// make square or apply minimum width/height
|
// make square or apply minimum width/height
|
||||||
boolean isIconOnlyOrSingleCharacter = isIconOnlyOrSingleCharacterButton( c );
|
boolean isIconOnlyOrSingleCharacter = isIconOnlyOrSingleCharacterButton( c );
|
||||||
if( clientPropertyBoolean( c, SQUARE_SIZE, squareSize ) ) {
|
if( clientPropertyBoolean( c, SQUARE_SIZE, squareSize ) ) {
|
||||||
@@ -637,6 +758,23 @@ public class FlatButtonUI
|
|||||||
return prefSize;
|
return prefSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private int defaultBoldPlainWidthDiff( JComponent c ) {
|
||||||
|
if( defaultBoldText && isDefaultButton( c ) && c.getFont() instanceof UIResource ) {
|
||||||
|
String text = ((AbstractButton)c).getText();
|
||||||
|
if( text == null || text.isEmpty() )
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
Font font = c.getFont();
|
||||||
|
Font boldFont = font.deriveFont( Font.BOLD );
|
||||||
|
int boldWidth = c.getFontMetrics( boldFont ).stringWidth( text );
|
||||||
|
int plainWidth = c.getFontMetrics( font ).stringWidth( text );
|
||||||
|
if( boldWidth > plainWidth )
|
||||||
|
return boldWidth - plainWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
private boolean hasDefaultMargins( JComponent c ) {
|
private boolean hasDefaultMargins( JComponent c ) {
|
||||||
Insets margin = ((AbstractButton)c).getMargin();
|
Insets margin = ((AbstractButton)c).getMargin();
|
||||||
return margin instanceof UIResource && Objects.equals( margin, defaultMargin );
|
return margin instanceof UIResource && Objects.equals( margin, defaultMargin );
|
||||||
|
|||||||
@@ -96,7 +96,7 @@ public class FlatCaret
|
|||||||
|
|
||||||
// if text component is focused, then caret and selection are visible,
|
// if text component is focused, then caret and selection are visible,
|
||||||
// but when switching theme, the component does not yet have
|
// but when switching theme, the component does not yet have
|
||||||
// an highlighter and the selection is not painted
|
// a highlighter and the selection is not painted
|
||||||
// --> make selection temporary invisible later, then the caret
|
// --> make selection temporary invisible later, then the caret
|
||||||
// adds selection highlights to the text component highlighter
|
// adds selection highlights to the text component highlighter
|
||||||
if( isSelectionVisible() ) {
|
if( isSelectionVisible() ) {
|
||||||
@@ -236,7 +236,7 @@ public class FlatCaret
|
|||||||
if( selectAllOnFocusPolicy == null )
|
if( selectAllOnFocusPolicy == null )
|
||||||
selectAllOnFocusPolicy = this.selectAllOnFocusPolicy;
|
selectAllOnFocusPolicy = this.selectAllOnFocusPolicy;
|
||||||
|
|
||||||
if( SELECT_ALL_ON_FOCUS_POLICY_NEVER.equals( selectAllOnFocusPolicy ) )
|
if( selectAllOnFocusPolicy == null || SELECT_ALL_ON_FOCUS_POLICY_NEVER.equals( selectAllOnFocusPolicy ) )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if( !SELECT_ALL_ON_FOCUS_POLICY_ALWAYS.equals( selectAllOnFocusPolicy ) ) {
|
if( !SELECT_ALL_ON_FOCUS_POLICY_ALWAYS.equals( selectAllOnFocusPolicy ) ) {
|
||||||
|
|||||||
@@ -16,18 +16,20 @@
|
|||||||
|
|
||||||
package com.formdev.flatlaf.ui;
|
package com.formdev.flatlaf.ui;
|
||||||
|
|
||||||
import java.awt.Color;
|
|
||||||
import java.awt.Dimension;
|
import java.awt.Dimension;
|
||||||
import java.awt.Graphics;
|
import java.awt.Graphics;
|
||||||
import java.beans.PropertyChangeListener;
|
import java.beans.PropertyChangeListener;
|
||||||
|
import java.lang.invoke.MethodHandles;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import javax.swing.Icon;
|
import javax.swing.Icon;
|
||||||
import javax.swing.JComponent;
|
import javax.swing.JComponent;
|
||||||
import javax.swing.LookAndFeel;
|
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;
|
||||||
|
import javax.swing.plaf.basic.BasicMenuItemUI;
|
||||||
|
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableField;
|
||||||
|
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableLookupProvider;
|
||||||
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
|
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
|
||||||
import com.formdev.flatlaf.ui.FlatStylingSupport.UnknownStyleException;
|
|
||||||
import com.formdev.flatlaf.util.LoggingFacade;
|
import com.formdev.flatlaf.util.LoggingFacade;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -58,9 +60,15 @@ import com.formdev.flatlaf.util.LoggingFacade;
|
|||||||
*
|
*
|
||||||
* @author Karl Tauber
|
* @author Karl Tauber
|
||||||
*/
|
*/
|
||||||
|
@StyleableField( cls=BasicMenuItemUI.class, key="selectionBackground" )
|
||||||
|
@StyleableField( cls=BasicMenuItemUI.class, key="selectionForeground" )
|
||||||
|
@StyleableField( cls=BasicMenuItemUI.class, key="disabledForeground" )
|
||||||
|
@StyleableField( cls=BasicMenuItemUI.class, key="acceleratorForeground" )
|
||||||
|
@StyleableField( cls=BasicMenuItemUI.class, key="acceleratorSelectionForeground" )
|
||||||
|
|
||||||
public class FlatCheckBoxMenuItemUI
|
public class FlatCheckBoxMenuItemUI
|
||||||
extends BasicCheckBoxMenuItemUI
|
extends BasicCheckBoxMenuItemUI
|
||||||
implements StyleableUI
|
implements StyleableUI, StyleableLookupProvider
|
||||||
{
|
{
|
||||||
private FlatMenuItemRenderer renderer;
|
private FlatMenuItemRenderer renderer;
|
||||||
private Map<String, Object> oldStyleValues;
|
private Map<String, Object> oldStyleValues;
|
||||||
@@ -119,29 +127,27 @@ public class FlatCheckBoxMenuItemUI
|
|||||||
|
|
||||||
/** @since 2 */
|
/** @since 2 */
|
||||||
protected Object applyStyleProperty( String key, Object value ) {
|
protected Object applyStyleProperty( String key, Object value ) {
|
||||||
try {
|
return FlatMenuItemUI.applyStyleProperty( menuItem, this, renderer, key, value );
|
||||||
return renderer.applyStyleProperty( key, value );
|
|
||||||
} catch ( UnknownStyleException ex ) {
|
|
||||||
// ignore
|
|
||||||
}
|
|
||||||
|
|
||||||
Object oldValue;
|
|
||||||
switch( key ) {
|
|
||||||
// BasicMenuItemUI
|
|
||||||
case "selectionBackground": oldValue = selectionBackground; selectionBackground = (Color) value; return oldValue;
|
|
||||||
case "selectionForeground": oldValue = selectionForeground; selectionForeground = (Color) value; return oldValue;
|
|
||||||
case "disabledForeground": oldValue = disabledForeground; disabledForeground = (Color) value; return oldValue;
|
|
||||||
case "acceleratorForeground": oldValue = acceleratorForeground; acceleratorForeground = (Color) value; return oldValue;
|
|
||||||
case "acceleratorSelectionForeground": oldValue = acceleratorSelectionForeground; acceleratorSelectionForeground = (Color) value; return oldValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
return FlatStylingSupport.applyToAnnotatedObjectOrComponent( this, menuItem, key, value );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @since 2 */
|
/** @since 2 */
|
||||||
@Override
|
@Override
|
||||||
public Map<String, Class<?>> getStyleableInfos( JComponent c ) {
|
public Map<String, Class<?>> getStyleableInfos( JComponent c ) {
|
||||||
return FlatMenuItemUI.getStyleableInfos( renderer );
|
return FlatMenuItemUI.getStyleableInfos( this, renderer );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2.5 */
|
||||||
|
@Override
|
||||||
|
public Object getStyleableValue( JComponent c, String key ) {
|
||||||
|
return FlatMenuItemUI.getStyleableValue( this, renderer, key );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2.5 */
|
||||||
|
@Override
|
||||||
|
public MethodHandles.Lookup getLookupForStyling() {
|
||||||
|
// MethodHandles.lookup() is caller sensitive and must be invoked in this class,
|
||||||
|
// otherwise it is not possible to access protected fields in JRE superclass
|
||||||
|
return MethodHandles.lookup();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ public class FlatCheckBoxUI
|
|||||||
extends FlatRadioButtonUI
|
extends FlatRadioButtonUI
|
||||||
{
|
{
|
||||||
public static ComponentUI createUI( JComponent c ) {
|
public static ComponentUI createUI( JComponent c ) {
|
||||||
return FlatUIUtils.canUseSharedUI( c )
|
return FlatUIUtils.canUseSharedUI( c ) && !FlatUIUtils.needsLightAWTPeer( c )
|
||||||
? FlatUIUtils.createSharedUI( FlatCheckBoxUI.class, () -> new FlatCheckBoxUI( true ) )
|
? FlatUIUtils.createSharedUI( FlatCheckBoxUI.class, () -> new FlatCheckBoxUI( true ) )
|
||||||
: new FlatCheckBoxUI( false );
|
: new FlatCheckBoxUI( false );
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ import java.awt.event.MouseEvent;
|
|||||||
import java.awt.event.MouseListener;
|
import java.awt.event.MouseListener;
|
||||||
import java.awt.geom.Rectangle2D;
|
import java.awt.geom.Rectangle2D;
|
||||||
import java.beans.PropertyChangeListener;
|
import java.beans.PropertyChangeListener;
|
||||||
|
import java.lang.invoke.MethodHandles;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
import javax.swing.AbstractAction;
|
import javax.swing.AbstractAction;
|
||||||
@@ -70,6 +71,8 @@ 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.ui.FlatStylingSupport.Styleable;
|
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
|
||||||
|
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableField;
|
||||||
|
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableLookupProvider;
|
||||||
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
|
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
|
||||||
import com.formdev.flatlaf.util.LoggingFacade;
|
import com.formdev.flatlaf.util.LoggingFacade;
|
||||||
import com.formdev.flatlaf.util.SystemInfo;
|
import com.formdev.flatlaf.util.SystemInfo;
|
||||||
@@ -86,6 +89,11 @@ import com.formdev.flatlaf.util.SystemInfo;
|
|||||||
* @uiDefault ComboBox.padding Insets
|
* @uiDefault ComboBox.padding Insets
|
||||||
* @uiDefault ComboBox.squareButton boolean default is true
|
* @uiDefault ComboBox.squareButton boolean default is true
|
||||||
*
|
*
|
||||||
|
* <!-- BasicComboPopup -->
|
||||||
|
*
|
||||||
|
* @uiDefault ComboBox.selectionBackground Color
|
||||||
|
* @uiDefault ComboBox.selectionForeground Color
|
||||||
|
*
|
||||||
* <!-- FlatComboBoxUI -->
|
* <!-- FlatComboBoxUI -->
|
||||||
*
|
*
|
||||||
* @uiDefault ComboBox.minimumWidth int
|
* @uiDefault ComboBox.minimumWidth int
|
||||||
@@ -112,9 +120,11 @@ import com.formdev.flatlaf.util.SystemInfo;
|
|||||||
*
|
*
|
||||||
* @author Karl Tauber
|
* @author Karl Tauber
|
||||||
*/
|
*/
|
||||||
|
@StyleableField( cls=BasicComboBoxUI.class, key="padding" )
|
||||||
|
|
||||||
public class FlatComboBoxUI
|
public class FlatComboBoxUI
|
||||||
extends BasicComboBoxUI
|
extends BasicComboBoxUI
|
||||||
implements StyleableUI
|
implements StyleableUI, StyleableLookupProvider
|
||||||
{
|
{
|
||||||
@Styleable protected int minimumWidth;
|
@Styleable protected int minimumWidth;
|
||||||
@Styleable protected int editorColumns;
|
@Styleable protected int editorColumns;
|
||||||
@@ -122,6 +132,7 @@ public class FlatComboBoxUI
|
|||||||
@Styleable protected String arrowType;
|
@Styleable protected String arrowType;
|
||||||
protected boolean isIntelliJTheme;
|
protected boolean isIntelliJTheme;
|
||||||
|
|
||||||
|
private Color background;
|
||||||
@Styleable protected Color editableBackground;
|
@Styleable protected Color editableBackground;
|
||||||
@Styleable protected Color focusedBackground;
|
@Styleable protected Color focusedBackground;
|
||||||
@Styleable protected Color disabledBackground;
|
@Styleable protected Color disabledBackground;
|
||||||
@@ -155,6 +166,13 @@ public class FlatComboBoxUI
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void installUI( JComponent c ) {
|
public void installUI( JComponent c ) {
|
||||||
|
if( FlatUIUtils.needsLightAWTPeer( c ) )
|
||||||
|
FlatUIUtils.runWithLightAWTPeerUIDefaults( () -> installUIImpl( c ) );
|
||||||
|
else
|
||||||
|
installUIImpl( c );
|
||||||
|
}
|
||||||
|
|
||||||
|
private void installUIImpl( JComponent c ) {
|
||||||
super.installUI( c );
|
super.installUI( c );
|
||||||
|
|
||||||
installStyle();
|
installStyle();
|
||||||
@@ -217,6 +235,7 @@ public class FlatComboBoxUI
|
|||||||
arrowType = UIManager.getString( "Component.arrowType" );
|
arrowType = UIManager.getString( "Component.arrowType" );
|
||||||
isIntelliJTheme = UIManager.getBoolean( "Component.isIntelliJTheme" );
|
isIntelliJTheme = UIManager.getBoolean( "Component.isIntelliJTheme" );
|
||||||
|
|
||||||
|
background = UIManager.getColor( "ComboBox.background" );
|
||||||
editableBackground = UIManager.getColor( "ComboBox.editableBackground" );
|
editableBackground = UIManager.getColor( "ComboBox.editableBackground" );
|
||||||
focusedBackground = UIManager.getColor( "ComboBox.focusedBackground" );
|
focusedBackground = UIManager.getColor( "ComboBox.focusedBackground" );
|
||||||
disabledBackground = UIManager.getColor( "ComboBox.disabledBackground" );
|
disabledBackground = UIManager.getColor( "ComboBox.disabledBackground" );
|
||||||
@@ -249,6 +268,7 @@ public class FlatComboBoxUI
|
|||||||
protected void uninstallDefaults() {
|
protected void uninstallDefaults() {
|
||||||
super.uninstallDefaults();
|
super.uninstallDefaults();
|
||||||
|
|
||||||
|
background = null;
|
||||||
editableBackground = null;
|
editableBackground = null;
|
||||||
focusedBackground = null;
|
focusedBackground = null;
|
||||||
disabledBackground = null;
|
disabledBackground = null;
|
||||||
@@ -354,6 +374,7 @@ public class FlatComboBoxUI
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case COMPONENT_ROUND_RECT:
|
case COMPONENT_ROUND_RECT:
|
||||||
|
case OUTLINE:
|
||||||
comboBox.repaint();
|
comboBox.repaint();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -485,13 +506,6 @@ public class FlatComboBoxUI
|
|||||||
|
|
||||||
/** @since 2 */
|
/** @since 2 */
|
||||||
protected Object applyStyleProperty( String key, Object value ) {
|
protected Object applyStyleProperty( String key, Object value ) {
|
||||||
// BasicComboBoxUI
|
|
||||||
if( key.equals( "padding" ) ) {
|
|
||||||
Object oldValue = padding;
|
|
||||||
padding = (Insets) value;
|
|
||||||
return oldValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if( borderShared == null )
|
if( borderShared == null )
|
||||||
borderShared = new AtomicBoolean( true );
|
borderShared = new AtomicBoolean( true );
|
||||||
return FlatStylingSupport.applyToAnnotatedObjectOrBorder( this, key, value, comboBox, borderShared );
|
return FlatStylingSupport.applyToAnnotatedObjectOrBorder( this, key, value, comboBox, borderShared );
|
||||||
@@ -500,11 +514,21 @@ public class FlatComboBoxUI
|
|||||||
/** @since 2 */
|
/** @since 2 */
|
||||||
@Override
|
@Override
|
||||||
public Map<String, Class<?>> getStyleableInfos( JComponent c ) {
|
public Map<String, Class<?>> getStyleableInfos( JComponent c ) {
|
||||||
Map<String, Class<?>> infos = new FlatStylingSupport.StyleableInfosMap<>();
|
return FlatStylingSupport.getAnnotatedStyleableInfos( this, comboBox.getBorder() );
|
||||||
infos.put( "padding", Insets.class );
|
}
|
||||||
FlatStylingSupport.collectAnnotatedStyleableInfos( this, infos );
|
|
||||||
FlatStylingSupport.collectStyleableInfos( comboBox.getBorder(), infos );
|
/** @since 2.5 */
|
||||||
return infos;
|
@Override
|
||||||
|
public Object getStyleableValue( JComponent c, String key ) {
|
||||||
|
return FlatStylingSupport.getAnnotatedStyleableValue( this, comboBox.getBorder(), key );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2.5 */
|
||||||
|
@Override
|
||||||
|
public MethodHandles.Lookup getLookupForStyling() {
|
||||||
|
// MethodHandles.lookup() is caller sensitive and must be invoked in this class,
|
||||||
|
// otherwise it is not possible to access protected fields in JRE superclass
|
||||||
|
return MethodHandles.lookup();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -602,7 +626,7 @@ public class FlatComboBoxUI
|
|||||||
|
|
||||||
boolean shouldValidate = (c instanceof JPanel);
|
boolean shouldValidate = (c instanceof JPanel);
|
||||||
|
|
||||||
paddingBorder.install( c );
|
paddingBorder.install( c, 0 );
|
||||||
currentValuePane.paintComponent( g, c, comboBox, bounds.x, bounds.y, bounds.width, bounds.height, shouldValidate );
|
currentValuePane.paintComponent( g, c, comboBox, bounds.x, bounds.y, bounds.width, bounds.height, shouldValidate );
|
||||||
paddingBorder.uninstall();
|
paddingBorder.uninstall();
|
||||||
|
|
||||||
@@ -617,6 +641,9 @@ public class FlatComboBoxUI
|
|||||||
|
|
||||||
protected Color getBackground( boolean enabled ) {
|
protected Color getBackground( boolean enabled ) {
|
||||||
if( enabled ) {
|
if( enabled ) {
|
||||||
|
if( FlatUIUtils.isAWTPeer( comboBox ) )
|
||||||
|
return background;
|
||||||
|
|
||||||
Color background = comboBox.getBackground();
|
Color background = comboBox.getBackground();
|
||||||
|
|
||||||
// always use explicitly set color
|
// always use explicitly set color
|
||||||
@@ -676,7 +703,7 @@ public class FlatComboBoxUI
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Dimension getSizeForComponent( Component comp ) {
|
protected Dimension getSizeForComponent( Component comp ) {
|
||||||
paddingBorder.install( comp );
|
paddingBorder.install( comp, 0 );
|
||||||
Dimension size = super.getSizeForComponent( comp );
|
Dimension size = super.getSizeForComponent( comp );
|
||||||
paddingBorder.uninstall();
|
paddingBorder.uninstall();
|
||||||
return size;
|
return size;
|
||||||
@@ -699,7 +726,7 @@ public class FlatComboBoxUI
|
|||||||
return true;
|
return true;
|
||||||
|
|
||||||
Component editorComponent = comboBox.getEditor().getEditorComponent();
|
Component editorComponent = comboBox.getEditor().getEditorComponent();
|
||||||
return (editorComponent != null) ? FlatUIUtils.isPermanentFocusOwner( editorComponent ) : false;
|
return editorComponent != null && FlatUIUtils.isPermanentFocusOwner( editorComponent );
|
||||||
} else
|
} else
|
||||||
return FlatUIUtils.isPermanentFocusOwner( comboBox );
|
return FlatUIUtils.isPermanentFocusOwner( comboBox );
|
||||||
}
|
}
|
||||||
@@ -894,7 +921,7 @@ public class FlatComboBoxUI
|
|||||||
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() );
|
||||||
|
|
||||||
paddingBorder.install( c );
|
paddingBorder.install( c, Math.round( FlatUIUtils.getBorderFocusWidth( comboBox ) ) );
|
||||||
|
|
||||||
return c;
|
return c;
|
||||||
}
|
}
|
||||||
@@ -917,6 +944,7 @@ public class FlatComboBoxUI
|
|||||||
private Insets padding;
|
private Insets padding;
|
||||||
private JComponent rendererComponent;
|
private JComponent rendererComponent;
|
||||||
private Border rendererBorder;
|
private Border rendererBorder;
|
||||||
|
private int focusWidth;
|
||||||
|
|
||||||
CellPaddingBorder( Insets padding ) {
|
CellPaddingBorder( Insets padding ) {
|
||||||
this.padding = padding;
|
this.padding = padding;
|
||||||
@@ -924,10 +952,12 @@ public class FlatComboBoxUI
|
|||||||
|
|
||||||
// using synchronized to avoid problems with code that modifies combo box
|
// using synchronized to avoid problems with code that modifies combo box
|
||||||
// (model, selection, etc) not on AWT thread (which should be not done)
|
// (model, selection, etc) not on AWT thread (which should be not done)
|
||||||
synchronized void install( Component c ) {
|
synchronized void install( Component c, int focusWidth ) {
|
||||||
if( !(c instanceof JComponent) )
|
if( !(c instanceof JComponent) )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
this.focusWidth = focusWidth;
|
||||||
|
|
||||||
JComponent jc = (JComponent) c;
|
JComponent jc = (JComponent) c;
|
||||||
Border oldBorder = jc.getBorder();
|
Border oldBorder = jc.getBorder();
|
||||||
if( oldBorder == this )
|
if( oldBorder == this )
|
||||||
@@ -981,6 +1011,12 @@ public class FlatComboBoxUI
|
|||||||
insets.bottom = padding.bottom;
|
insets.bottom = padding.bottom;
|
||||||
insets.right = padding.right;
|
insets.right = padding.right;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// if used in popup list, add focus width for exact vertical alignment
|
||||||
|
// of text in popup list with text in combobox
|
||||||
|
insets.left += focusWidth;
|
||||||
|
insets.right += focusWidth;
|
||||||
|
|
||||||
return insets;
|
return insets;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -107,6 +107,12 @@ public class FlatDropShadowBorder
|
|||||||
return FlatStylingSupport.getAnnotatedStyleableInfos( this );
|
return FlatStylingSupport.getAnnotatedStyleableInfos( this );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @since 2.5 */
|
||||||
|
@Override
|
||||||
|
public Object getStyleableValue( String key ) {
|
||||||
|
return FlatStylingSupport.getAnnotatedStyleableValue( this, key );
|
||||||
|
}
|
||||||
|
|
||||||
@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( shadowSize <= 0 )
|
if( shadowSize <= 0 )
|
||||||
|
|||||||
@@ -209,6 +209,12 @@ public class FlatEditorPaneUI
|
|||||||
return FlatStylingSupport.getAnnotatedStyleableInfos( this );
|
return FlatStylingSupport.getAnnotatedStyleableInfos( this );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @since 2.5 */
|
||||||
|
@Override
|
||||||
|
public Object getStyleableValue( JComponent c, String key ) {
|
||||||
|
return FlatStylingSupport.getAnnotatedStyleableValue( this, key );
|
||||||
|
}
|
||||||
|
|
||||||
private void updateBackground() {
|
private void updateBackground() {
|
||||||
FlatTextFieldUI.updateBackground( getComponent(), background,
|
FlatTextFieldUI.updateBackground( getComponent(), background,
|
||||||
disabledBackground, inactiveBackground,
|
disabledBackground, inactiveBackground,
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ public class FlatEmptyBorder
|
|||||||
protected static Insets scaleInsets( Component c, Insets insets,
|
protected static Insets scaleInsets( Component c, Insets insets,
|
||||||
int top, int left, int bottom, int right )
|
int top, int left, int bottom, int right )
|
||||||
{
|
{
|
||||||
boolean leftToRight = left == right || c.getComponentOrientation().isLeftToRight();
|
boolean leftToRight = left == right || c == null || c.getComponentOrientation().isLeftToRight();
|
||||||
insets.left = scale( leftToRight ? left : right );
|
insets.left = scale( leftToRight ? left : right );
|
||||||
insets.top = scale( top );
|
insets.top = scale( top );
|
||||||
insets.right = scale( leftToRight ? right : left );
|
insets.right = scale( leftToRight ? right : left );
|
||||||
@@ -76,4 +76,9 @@ public class FlatEmptyBorder
|
|||||||
right = insets.right;
|
right = insets.right;
|
||||||
return oldInsets;
|
return oldInsets;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @since 2.5 */
|
||||||
|
public Insets getStyleableValue() {
|
||||||
|
return new Insets( top, left, bottom, right );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,11 +19,21 @@ package com.formdev.flatlaf.ui;
|
|||||||
import java.awt.BorderLayout;
|
import java.awt.BorderLayout;
|
||||||
import java.awt.Component;
|
import java.awt.Component;
|
||||||
import java.awt.Dimension;
|
import java.awt.Dimension;
|
||||||
|
import java.awt.Graphics;
|
||||||
|
import java.awt.Graphics2D;
|
||||||
|
import java.awt.Image;
|
||||||
import java.awt.Insets;
|
import java.awt.Insets;
|
||||||
|
import java.awt.LayoutManager;
|
||||||
|
import java.awt.RenderingHints;
|
||||||
|
import java.beans.PropertyChangeEvent;
|
||||||
|
import java.beans.PropertyChangeListener;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.function.Function;
|
||||||
import javax.swing.AbstractButton;
|
import javax.swing.AbstractButton;
|
||||||
import javax.swing.Box;
|
import javax.swing.Box;
|
||||||
import javax.swing.BoxLayout;
|
import javax.swing.BoxLayout;
|
||||||
|
import javax.swing.ButtonGroup;
|
||||||
import javax.swing.Icon;
|
import javax.swing.Icon;
|
||||||
import javax.swing.ImageIcon;
|
import javax.swing.ImageIcon;
|
||||||
import javax.swing.JButton;
|
import javax.swing.JButton;
|
||||||
@@ -34,12 +44,16 @@ import javax.swing.JPanel;
|
|||||||
import javax.swing.JScrollPane;
|
import javax.swing.JScrollPane;
|
||||||
import javax.swing.JTable;
|
import javax.swing.JTable;
|
||||||
import javax.swing.JToggleButton;
|
import javax.swing.JToggleButton;
|
||||||
|
import javax.swing.JToolBar;
|
||||||
|
import javax.swing.SwingConstants;
|
||||||
import javax.swing.UIManager;
|
import javax.swing.UIManager;
|
||||||
|
import javax.swing.filechooser.FileSystemView;
|
||||||
import javax.swing.filechooser.FileView;
|
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 javax.swing.table.TableCellRenderer;
|
import javax.swing.table.TableCellRenderer;
|
||||||
import com.formdev.flatlaf.FlatClientProperties;
|
import com.formdev.flatlaf.FlatClientProperties;
|
||||||
|
import com.formdev.flatlaf.util.LoggingFacade;
|
||||||
import com.formdev.flatlaf.util.ScaledImageIcon;
|
import com.formdev.flatlaf.util.ScaledImageIcon;
|
||||||
import com.formdev.flatlaf.util.SystemInfo;
|
import com.formdev.flatlaf.util.SystemInfo;
|
||||||
import com.formdev.flatlaf.util.UIScale;
|
import com.formdev.flatlaf.util.UIScale;
|
||||||
@@ -133,12 +147,21 @@ import com.formdev.flatlaf.util.UIScale;
|
|||||||
* @uiDefault FileChooser.listViewActionLabelText String
|
* @uiDefault FileChooser.listViewActionLabelText String
|
||||||
* @uiDefault FileChooser.detailsViewActionLabelText String
|
* @uiDefault FileChooser.detailsViewActionLabelText String
|
||||||
*
|
*
|
||||||
|
* <!-- FlatFileChooserUI -->
|
||||||
|
*
|
||||||
|
* @uiDefault FileChooser.shortcuts.buttonSize Dimension optional; default is 84,64
|
||||||
|
* @uiDefault FileChooser.shortcuts.iconSize Dimension optional; default is 32,32
|
||||||
|
* @uiDefault FileChooser.shortcuts.filesFunction Function<File[], File[]>
|
||||||
|
* @uiDefault FileChooser.shortcuts.displayNameFunction Function<File, String>
|
||||||
|
* @uiDefault FileChooser.shortcuts.iconFunction Function<File, Icon>
|
||||||
|
*
|
||||||
* @author Karl Tauber
|
* @author Karl Tauber
|
||||||
*/
|
*/
|
||||||
public class FlatFileChooserUI
|
public class FlatFileChooserUI
|
||||||
extends MetalFileChooserUI
|
extends MetalFileChooserUI
|
||||||
{
|
{
|
||||||
private final FlatFileView fileView = new FlatFileView();
|
private final FlatFileView fileView = new FlatFileView();
|
||||||
|
private FlatShortcutsPanel shortcutsPanel;
|
||||||
|
|
||||||
public static ComponentUI createUI( JComponent c ) {
|
public static ComponentUI createUI( JComponent c ) {
|
||||||
return new FlatFileChooserUI( (JFileChooser) c );
|
return new FlatFileChooserUI( (JFileChooser) c );
|
||||||
@@ -153,6 +176,25 @@ public class FlatFileChooserUI
|
|||||||
super.installComponents( fc );
|
super.installComponents( fc );
|
||||||
|
|
||||||
patchUI( fc );
|
patchUI( fc );
|
||||||
|
|
||||||
|
if( !UIManager.getBoolean( "FileChooser.noPlacesBar" ) ) { // same as in Windows L&F
|
||||||
|
FlatShortcutsPanel panel = createShortcutsPanel( fc );
|
||||||
|
if( panel.getComponentCount() > 0 ) {
|
||||||
|
shortcutsPanel = panel;
|
||||||
|
fc.add( shortcutsPanel, BorderLayout.LINE_START );
|
||||||
|
fc.addPropertyChangeListener( shortcutsPanel );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void uninstallComponents( JFileChooser fc ) {
|
||||||
|
super.uninstallComponents( fc );
|
||||||
|
|
||||||
|
if( shortcutsPanel != null ) {
|
||||||
|
fc.removePropertyChangeListener( shortcutsPanel );
|
||||||
|
shortcutsPanel = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void patchUI( JFileChooser fc ) {
|
private void patchUI( JFileChooser fc ) {
|
||||||
@@ -192,6 +234,25 @@ public class FlatFileChooserUI
|
|||||||
} catch( ArrayIndexOutOfBoundsException ex ) {
|
} catch( ArrayIndexOutOfBoundsException ex ) {
|
||||||
// ignore
|
// ignore
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// put north, center and south components into a new panel so that
|
||||||
|
// the shortcuts panel (at west) gets full height
|
||||||
|
LayoutManager layout = fc.getLayout();
|
||||||
|
if( layout instanceof BorderLayout ) {
|
||||||
|
BorderLayout borderLayout = (BorderLayout) layout;
|
||||||
|
borderLayout.setHgap( 8 );
|
||||||
|
|
||||||
|
Component north = borderLayout.getLayoutComponent( BorderLayout.NORTH );
|
||||||
|
Component center = borderLayout.getLayoutComponent( BorderLayout.CENTER );
|
||||||
|
Component south = borderLayout.getLayoutComponent( BorderLayout.SOUTH );
|
||||||
|
if( north != null && center != null && south != null ) {
|
||||||
|
JPanel p = new JPanel( new BorderLayout( 0, 11 ) );
|
||||||
|
p.add( north, BorderLayout.NORTH );
|
||||||
|
p.add( center, BorderLayout.CENTER );
|
||||||
|
p.add( south, BorderLayout.SOUTH );
|
||||||
|
fc.add( p, BorderLayout.CENTER );
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -250,9 +311,19 @@ public class FlatFileChooserUI
|
|||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @since 2.3 */
|
||||||
|
protected FlatShortcutsPanel createShortcutsPanel( JFileChooser fc ) {
|
||||||
|
return new FlatShortcutsPanel( fc );
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Dimension getPreferredSize( JComponent c ) {
|
public Dimension getPreferredSize( JComponent c ) {
|
||||||
return UIScale.scale( super.getPreferredSize( c ) );
|
Dimension prefSize = super.getPreferredSize( c );
|
||||||
|
Dimension minSize = getMinimumSize( c );
|
||||||
|
int shortcutsPanelWidth = (shortcutsPanel != null) ? shortcutsPanel.getPreferredSize().width : 0;
|
||||||
|
return new Dimension(
|
||||||
|
Math.max( prefSize.width, minSize.width + shortcutsPanelWidth ),
|
||||||
|
Math.max( prefSize.height, minSize.height ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -316,4 +387,234 @@ public class FlatFileChooserUI
|
|||||||
return icon;
|
return icon;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//---- class FlatShortcutsPanel -------------------------------------------
|
||||||
|
|
||||||
|
/** @since 2.3 */
|
||||||
|
public static class FlatShortcutsPanel
|
||||||
|
extends JToolBar
|
||||||
|
implements PropertyChangeListener
|
||||||
|
{
|
||||||
|
private final JFileChooser fc;
|
||||||
|
|
||||||
|
private final Dimension buttonSize;
|
||||||
|
private final Dimension iconSize;
|
||||||
|
private final Function<File[], File[]> filesFunction;
|
||||||
|
private final Function<File, String> displayNameFunction;
|
||||||
|
private final Function<File, Icon> iconFunction;
|
||||||
|
|
||||||
|
protected final File[] files;
|
||||||
|
protected final JToggleButton[] buttons;
|
||||||
|
protected final ButtonGroup buttonGroup;
|
||||||
|
|
||||||
|
@SuppressWarnings( "unchecked" )
|
||||||
|
public FlatShortcutsPanel( JFileChooser fc ) {
|
||||||
|
super( JToolBar.VERTICAL );
|
||||||
|
this.fc = fc;
|
||||||
|
setFloatable( false );
|
||||||
|
|
||||||
|
buttonSize = UIScale.scale( getUIDimension( "FileChooser.shortcuts.buttonSize", 84, 64 ) );
|
||||||
|
iconSize = getUIDimension( "FileChooser.shortcuts.iconSize", 32, 32 );
|
||||||
|
|
||||||
|
filesFunction = (Function<File[], File[]>) UIManager.get( "FileChooser.shortcuts.filesFunction" );
|
||||||
|
displayNameFunction = (Function<File, String>) UIManager.get( "FileChooser.shortcuts.displayNameFunction" );
|
||||||
|
iconFunction = (Function<File, Icon>) UIManager.get( "FileChooser.shortcuts.iconFunction" );
|
||||||
|
|
||||||
|
FileSystemView fsv = fc.getFileSystemView();
|
||||||
|
File[] files = getChooserShortcutPanelFiles( fsv );
|
||||||
|
if( filesFunction != null )
|
||||||
|
files = filesFunction.apply( files );
|
||||||
|
this.files = files;
|
||||||
|
|
||||||
|
// create toolbar buttons
|
||||||
|
buttons = new JToggleButton[files.length];
|
||||||
|
buttonGroup = new ButtonGroup();
|
||||||
|
for( int i = 0; i < files.length; i++ ) {
|
||||||
|
// wrap drive path
|
||||||
|
if( fsv.isFileSystemRoot( files[i] ) )
|
||||||
|
files[i] = fsv.createFileObject( files[i].getAbsolutePath() );
|
||||||
|
|
||||||
|
File file = files[i];
|
||||||
|
String name = getDisplayName( fsv, file );
|
||||||
|
Icon icon = getIcon( fsv, file );
|
||||||
|
|
||||||
|
// remove path from name
|
||||||
|
int lastSepIndex = name.lastIndexOf( File.separatorChar );
|
||||||
|
if( lastSepIndex >= 0 && lastSepIndex < name.length() - 1 )
|
||||||
|
name = name.substring( lastSepIndex + 1 );
|
||||||
|
|
||||||
|
// scale icon (if necessary)
|
||||||
|
if( icon instanceof ImageIcon )
|
||||||
|
icon = new ScaledImageIcon( (ImageIcon) icon, iconSize.width, iconSize.height );
|
||||||
|
else if( icon != null )
|
||||||
|
icon = new ShortcutIcon( icon, iconSize.width, iconSize.height );
|
||||||
|
|
||||||
|
// create button
|
||||||
|
JToggleButton button = createButton( name, icon );
|
||||||
|
button.addActionListener( e -> {
|
||||||
|
fc.setCurrentDirectory( file );
|
||||||
|
} );
|
||||||
|
|
||||||
|
add( button );
|
||||||
|
buttonGroup.add( button );
|
||||||
|
buttons[i] = button;
|
||||||
|
}
|
||||||
|
|
||||||
|
directoryChanged( fc.getCurrentDirectory() );
|
||||||
|
}
|
||||||
|
|
||||||
|
private Dimension getUIDimension( String key, int defaultWidth, int defaultHeight ) {
|
||||||
|
Dimension size = UIManager.getDimension( key );
|
||||||
|
if( size == null )
|
||||||
|
size = new Dimension( defaultWidth, defaultHeight );
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected JToggleButton createButton( String name, Icon icon ) {
|
||||||
|
JToggleButton button = new JToggleButton( name, icon );
|
||||||
|
button.setVerticalTextPosition( SwingConstants.BOTTOM );
|
||||||
|
button.setHorizontalTextPosition( SwingConstants.CENTER );
|
||||||
|
button.setAlignmentX( Component.CENTER_ALIGNMENT );
|
||||||
|
button.setIconTextGap( 0 );
|
||||||
|
button.setPreferredSize( buttonSize );
|
||||||
|
button.setMaximumSize( buttonSize );
|
||||||
|
return button;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected File[] getChooserShortcutPanelFiles( FileSystemView fsv ) {
|
||||||
|
try {
|
||||||
|
if( SystemInfo.isJava_12_orLater ) {
|
||||||
|
Method m = fsv.getClass().getMethod( "getChooserShortcutPanelFiles" );
|
||||||
|
File[] files = (File[]) m.invoke( fsv );
|
||||||
|
|
||||||
|
// on macOS and Linux, files consists only of the user home directory
|
||||||
|
if( files.length == 1 && files[0].equals( new File( System.getProperty( "user.home" ) ) ) )
|
||||||
|
files = new File[0];
|
||||||
|
|
||||||
|
return files;
|
||||||
|
} else if( SystemInfo.isWindows ) {
|
||||||
|
Class<?> cls = Class.forName( "sun.awt.shell.ShellFolder" );
|
||||||
|
Method m = cls.getMethod( "get", String.class );
|
||||||
|
return (File[]) m.invoke( null, "fileChooserShortcutPanelFolders" );
|
||||||
|
}
|
||||||
|
} catch( IllegalAccessException ex ) {
|
||||||
|
// do not log because access may be denied via VM option '--illegal-access=deny'
|
||||||
|
} catch( Exception ex ) {
|
||||||
|
LoggingFacade.INSTANCE.logSevere( null, ex );
|
||||||
|
}
|
||||||
|
|
||||||
|
// fallback
|
||||||
|
return new File[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
protected String getDisplayName( FileSystemView fsv, File file ) {
|
||||||
|
if( displayNameFunction != null ) {
|
||||||
|
String name = displayNameFunction.apply( file );
|
||||||
|
if( name != null )
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
return fsv.getSystemDisplayName( file );
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Icon getIcon( FileSystemView fsv, File file ) {
|
||||||
|
if( iconFunction != null ) {
|
||||||
|
Icon icon = iconFunction.apply( file );
|
||||||
|
if( icon != null )
|
||||||
|
return icon;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Java 17+ supports getting larger system icons
|
||||||
|
try {
|
||||||
|
if( SystemInfo.isJava_17_orLater ) {
|
||||||
|
Method m = fsv.getClass().getMethod( "getSystemIcon", File.class, int.class, int.class );
|
||||||
|
return (Icon) m.invoke( fsv, file, iconSize.width, iconSize.height );
|
||||||
|
} else if( iconSize.width > 16 || iconSize.height > 16 ) {
|
||||||
|
Class<?> cls = Class.forName( "sun.awt.shell.ShellFolder" );
|
||||||
|
if( cls.isInstance( file ) ) {
|
||||||
|
Method m = file.getClass().getMethod( "getIcon", boolean.class );
|
||||||
|
m.setAccessible( true );
|
||||||
|
Image image = (Image) m.invoke( file, true );
|
||||||
|
if( image != null )
|
||||||
|
return new ImageIcon( image );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch( IllegalAccessException ex ) {
|
||||||
|
// do not log because access may be denied via VM option '--illegal-access=deny'
|
||||||
|
} catch( Exception ex ) {
|
||||||
|
LoggingFacade.INSTANCE.logSevere( null, ex );
|
||||||
|
}
|
||||||
|
|
||||||
|
// get system icon in default size 16x16
|
||||||
|
return fsv.getSystemIcon( file );
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void directoryChanged( File file ) {
|
||||||
|
if( file != null ) {
|
||||||
|
String absolutePath = file.getAbsolutePath();
|
||||||
|
for( int i = 0; i < files.length; i++ ) {
|
||||||
|
// also compare path because otherwise selecting "Documents"
|
||||||
|
// in "Look in" combobox would not select "Documents" shortcut item
|
||||||
|
if( files[i].equals( file ) || files[i].getAbsolutePath().equals( absolutePath ) ) {
|
||||||
|
buttons[i].setSelected( true );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
buttonGroup.clearSelection();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void propertyChange( PropertyChangeEvent e ) {
|
||||||
|
switch( e.getPropertyName() ) {
|
||||||
|
case JFileChooser.DIRECTORY_CHANGED_PROPERTY:
|
||||||
|
directoryChanged( fc.getCurrentDirectory() );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//---- class ShortcutIcon -------------------------------------------------
|
||||||
|
|
||||||
|
private static class ShortcutIcon
|
||||||
|
implements Icon
|
||||||
|
{
|
||||||
|
private final Icon icon;
|
||||||
|
private final int iconWidth;
|
||||||
|
private final int iconHeight;
|
||||||
|
|
||||||
|
ShortcutIcon( Icon icon, int iconWidth, int iconHeight ) {
|
||||||
|
this.icon = icon;
|
||||||
|
this.iconWidth = iconWidth;
|
||||||
|
this.iconHeight = iconHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void paintIcon( Component c, Graphics g, int x, int y ) {
|
||||||
|
Graphics2D g2 = (Graphics2D) g.create();
|
||||||
|
try {
|
||||||
|
// set rendering hint for the case that the icon is a bitmap (not used for vector icons)
|
||||||
|
g2.setRenderingHint( RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC );
|
||||||
|
|
||||||
|
double scale = (double) getIconWidth() / (double) icon.getIconWidth();
|
||||||
|
g2.translate( x, y );
|
||||||
|
g2.scale( scale, scale );
|
||||||
|
|
||||||
|
icon.paintIcon( c, g2, 0, 0 );
|
||||||
|
} finally {
|
||||||
|
g2.dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getIconWidth() {
|
||||||
|
return UIScale.scale( iconWidth );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getIconHeight() {
|
||||||
|
return UIScale.scale( iconHeight );
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -179,6 +179,12 @@ public class FlatInternalFrameUI
|
|||||||
return FlatStylingSupport.getAnnotatedStyleableInfos( this, frame.getBorder() );
|
return FlatStylingSupport.getAnnotatedStyleableInfos( this, frame.getBorder() );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @since 2.5 */
|
||||||
|
@Override
|
||||||
|
public Object getStyleableValue( JComponent c, String key ) {
|
||||||
|
return FlatStylingSupport.getAnnotatedStyleableValue( this, frame.getBorder(), key );
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void update( Graphics g, JComponent c ) {
|
public void update( Graphics g, JComponent c ) {
|
||||||
// The internal frame actually should be opaque and fill its background,
|
// The internal frame actually should be opaque and fill its background,
|
||||||
@@ -253,6 +259,23 @@ public class FlatInternalFrameUI
|
|||||||
return infos;
|
return infos;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @since 2.5 */
|
||||||
|
@Override
|
||||||
|
public Object getStyleableValue( String key ) {
|
||||||
|
switch( key ) {
|
||||||
|
case "borderMargins": return getStyleableValue();
|
||||||
|
|
||||||
|
case "activeDropShadowColor": return activeDropShadowBorder.getStyleableValue( "shadowColor" );
|
||||||
|
case "activeDropShadowInsets": return activeDropShadowBorder.getStyleableValue( "shadowInsets" );
|
||||||
|
case "activeDropShadowOpacity": return activeDropShadowBorder.getStyleableValue( "shadowOpacity" );
|
||||||
|
case "inactiveDropShadowColor": return inactiveDropShadowBorder.getStyleableValue( "shadowColor" );
|
||||||
|
case "inactiveDropShadowInsets": return inactiveDropShadowBorder.getStyleableValue( "shadowInsets" );
|
||||||
|
case "inactiveDropShadowOpacity": return inactiveDropShadowBorder.getStyleableValue( "shadowOpacity" );
|
||||||
|
}
|
||||||
|
|
||||||
|
return FlatStylingSupport.getAnnotatedStyleableValue( this, key );
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Insets getBorderInsets( Component c, Insets insets ) {
|
public Insets getBorderInsets( Component c, Insets insets ) {
|
||||||
if( c instanceof JInternalFrame && ((JInternalFrame)c).isMaximum() ) {
|
if( c instanceof JInternalFrame && ((JInternalFrame)c).isMaximum() ) {
|
||||||
|
|||||||
@@ -158,6 +158,12 @@ public class FlatLabelUI
|
|||||||
return FlatStylingSupport.getAnnotatedStyleableInfos( this );
|
return FlatStylingSupport.getAnnotatedStyleableInfos( this );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @since 2.5 */
|
||||||
|
@Override
|
||||||
|
public Object getStyleableValue( JComponent c, String key ) {
|
||||||
|
return FlatStylingSupport.getAnnotatedStyleableValue( this, key );
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks whether text contains HTML tags that use "absolute-size" keywords
|
* Checks whether text contains HTML tags that use "absolute-size" keywords
|
||||||
* (e.g. "x-large") for font-size in default style sheet
|
* (e.g. "x-large") for font-size in default style sheet
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ public class FlatListCellBorder
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Because this borders are always shared for all lists,
|
* Because this border is always shared for all lists,
|
||||||
* get border specific style from FlatListUI.
|
* get border specific style from FlatListUI.
|
||||||
*/
|
*/
|
||||||
static <T> T getStyleFromListUI( Component c, Function<FlatListUI, T> f ) {
|
static <T> T getStyleFromListUI( Component c, Function<FlatListUI, T> f ) {
|
||||||
|
|||||||
@@ -90,6 +90,13 @@ public class FlatListUI
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void installUI( JComponent c ) {
|
public void installUI( JComponent c ) {
|
||||||
|
if( FlatUIUtils.needsLightAWTPeer( c ) )
|
||||||
|
FlatUIUtils.runWithLightAWTPeerUIDefaults( () -> installUIImpl( c ) );
|
||||||
|
else
|
||||||
|
installUIImpl( c );
|
||||||
|
}
|
||||||
|
|
||||||
|
private void installUIImpl( JComponent c ) {
|
||||||
super.installUI( c );
|
super.installUI( c );
|
||||||
|
|
||||||
installStyle();
|
installStyle();
|
||||||
@@ -209,10 +216,16 @@ public class FlatListUI
|
|||||||
return FlatStylingSupport.getAnnotatedStyleableInfos( this );
|
return FlatStylingSupport.getAnnotatedStyleableInfos( this );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @since 2.5 */
|
||||||
|
@Override
|
||||||
|
public Object getStyleableValue( JComponent c, String key ) {
|
||||||
|
return FlatStylingSupport.getAnnotatedStyleableValue( this, key );
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Toggle selection colors from focused to inactive and vice versa.
|
* Toggle selection colors from focused to inactive and vice versa.
|
||||||
*
|
*
|
||||||
* This is not a optimal solution but much easier than rewriting the whole paint methods.
|
* This is not an optimal solution but much easier than rewriting the whole paint methods.
|
||||||
*
|
*
|
||||||
* Using a LaF specific renderer was avoided because often a custom renderer is
|
* Using a LaF specific renderer was avoided because often a custom renderer is
|
||||||
* already used in applications. Then either the inactive colors are not used,
|
* already used in applications. Then either the inactive colors are not used,
|
||||||
|
|||||||
@@ -51,6 +51,12 @@ public class FlatMenuBarBorder
|
|||||||
return FlatStylingSupport.getAnnotatedStyleableInfos( this );
|
return FlatStylingSupport.getAnnotatedStyleableInfos( this );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @since 2.5 */
|
||||||
|
@Override
|
||||||
|
public Object getStyleableValue( String key ) {
|
||||||
|
return FlatStylingSupport.getAnnotatedStyleableValue( this, key );
|
||||||
|
}
|
||||||
|
|
||||||
@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( !showBottomSeparator( c ) )
|
if( !showBottomSeparator( c ) )
|
||||||
|
|||||||
@@ -18,8 +18,10 @@ package com.formdev.flatlaf.ui;
|
|||||||
|
|
||||||
import java.awt.Color;
|
import java.awt.Color;
|
||||||
import java.awt.Component;
|
import java.awt.Component;
|
||||||
|
import java.awt.Container;
|
||||||
import java.awt.Graphics;
|
import java.awt.Graphics;
|
||||||
import java.awt.Insets;
|
import java.awt.Insets;
|
||||||
|
import java.awt.LayoutManager;
|
||||||
import java.awt.Window;
|
import java.awt.Window;
|
||||||
import java.awt.event.ActionEvent;
|
import java.awt.event.ActionEvent;
|
||||||
import java.beans.PropertyChangeListener;
|
import java.beans.PropertyChangeListener;
|
||||||
@@ -27,6 +29,7 @@ import java.util.Map;
|
|||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
import javax.swing.AbstractAction;
|
import javax.swing.AbstractAction;
|
||||||
import javax.swing.ActionMap;
|
import javax.swing.ActionMap;
|
||||||
|
import javax.swing.BoxLayout;
|
||||||
import javax.swing.JComponent;
|
import javax.swing.JComponent;
|
||||||
import javax.swing.JMenu;
|
import javax.swing.JMenu;
|
||||||
import javax.swing.JMenuBar;
|
import javax.swing.JMenuBar;
|
||||||
@@ -40,11 +43,13 @@ import javax.swing.plaf.ActionMapUIResource;
|
|||||||
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.BasicMenuBarUI;
|
import javax.swing.plaf.basic.BasicMenuBarUI;
|
||||||
|
import javax.swing.plaf.basic.DefaultMenuLayout;
|
||||||
import com.formdev.flatlaf.FlatLaf;
|
import com.formdev.flatlaf.FlatLaf;
|
||||||
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
|
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
|
||||||
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
|
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
|
||||||
import com.formdev.flatlaf.util.LoggingFacade;
|
import com.formdev.flatlaf.util.LoggingFacade;
|
||||||
import com.formdev.flatlaf.util.SystemInfo;
|
import com.formdev.flatlaf.util.SystemInfo;
|
||||||
|
import com.formdev.flatlaf.util.UIScale;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides the Flat LaF UI delegate for {@link javax.swing.JMenuBar}.
|
* Provides the Flat LaF UI delegate for {@link javax.swing.JMenuBar}.
|
||||||
@@ -71,6 +76,8 @@ public class FlatMenuBarUI
|
|||||||
|
|
||||||
// used in FlatMenuUI
|
// used in FlatMenuUI
|
||||||
/** @since 2 */ @Styleable protected Color hoverBackground;
|
/** @since 2 */ @Styleable protected Color hoverBackground;
|
||||||
|
/** @since 2.5 */ @Styleable protected Color selectionBackground;
|
||||||
|
/** @since 2.5 */ @Styleable protected Color selectionForeground;
|
||||||
/** @since 2 */ @Styleable protected Color underlineSelectionBackground;
|
/** @since 2 */ @Styleable protected Color underlineSelectionBackground;
|
||||||
/** @since 2 */ @Styleable protected Color underlineSelectionColor;
|
/** @since 2 */ @Styleable protected Color underlineSelectionColor;
|
||||||
/** @since 2 */ @Styleable protected int underlineSelectionHeight = -1;
|
/** @since 2 */ @Styleable protected int underlineSelectionHeight = -1;
|
||||||
@@ -100,6 +107,10 @@ public class FlatMenuBarUI
|
|||||||
super.installDefaults();
|
super.installDefaults();
|
||||||
|
|
||||||
LookAndFeel.installProperty( menuBar, "opaque", false );
|
LookAndFeel.installProperty( menuBar, "opaque", false );
|
||||||
|
|
||||||
|
LayoutManager layout = menuBar.getLayout();
|
||||||
|
if( layout == null || layout instanceof UIResource )
|
||||||
|
menuBar.setLayout( new FlatMenuBarLayout( menuBar ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -165,6 +176,12 @@ public class FlatMenuBarUI
|
|||||||
return FlatStylingSupport.getAnnotatedStyleableInfos( this, menuBar.getBorder() );
|
return FlatStylingSupport.getAnnotatedStyleableInfos( this, menuBar.getBorder() );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @since 2.5 */
|
||||||
|
@Override
|
||||||
|
public Object getStyleableValue( JComponent c, String key ) {
|
||||||
|
return FlatStylingSupport.getAnnotatedStyleableValue( this, menuBar.getBorder(), key );
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void update( Graphics g, JComponent c ) {
|
public void update( Graphics g, JComponent c ) {
|
||||||
// paint background
|
// paint background
|
||||||
@@ -210,7 +227,7 @@ public class FlatMenuBarUI
|
|||||||
// check whether:
|
// check whether:
|
||||||
// - TitlePane.unifiedBackground is true and
|
// - TitlePane.unifiedBackground is true and
|
||||||
// - menu bar is the "main" menu bar and
|
// - menu bar is the "main" menu bar and
|
||||||
// - window has custom decorations enabled
|
// - window root pane has custom decoration style
|
||||||
|
|
||||||
JRootPane rootPane;
|
JRootPane rootPane;
|
||||||
// (not storing value of "TitlePane.unifiedBackground" in class to allow changing at runtime)
|
// (not storing value of "TitlePane.unifiedBackground" in class to allow changing at runtime)
|
||||||
@@ -218,7 +235,131 @@ public class FlatMenuBarUI
|
|||||||
(rootPane = SwingUtilities.getRootPane( c )) != null &&
|
(rootPane = SwingUtilities.getRootPane( c )) != null &&
|
||||||
rootPane.getParent() instanceof Window &&
|
rootPane.getParent() instanceof Window &&
|
||||||
rootPane.getJMenuBar() == c &&
|
rootPane.getJMenuBar() == c &&
|
||||||
FlatNativeWindowBorder.hasCustomDecoration( (Window) rootPane.getParent() );
|
rootPane.getWindowDecorationStyle() != JRootPane.NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
//---- class FlatMenuBarLayout --------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @since 2.4
|
||||||
|
*/
|
||||||
|
protected static class FlatMenuBarLayout
|
||||||
|
extends DefaultMenuLayout
|
||||||
|
{
|
||||||
|
public FlatMenuBarLayout( Container target ) {
|
||||||
|
super( target, BoxLayout.LINE_AXIS );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void layoutContainer( Container target ) {
|
||||||
|
super.layoutContainer( target );
|
||||||
|
|
||||||
|
|
||||||
|
// The only purpose of the code below is to make sure that a horizontal glue,
|
||||||
|
// which can be used to move window and displays the window title in embedded menu bar,
|
||||||
|
// is always visible within the menu bar bounds and has a minimum width.
|
||||||
|
// If this is not the case, the horizontal glue is made larger and
|
||||||
|
// components that are on the left side of the glue are made smaller.
|
||||||
|
|
||||||
|
|
||||||
|
// get root pane and check whether this menu bar is the root pane menu bar
|
||||||
|
JRootPane rootPane = SwingUtilities.getRootPane( target );
|
||||||
|
if( rootPane == null || rootPane.getJMenuBar() != target )
|
||||||
|
return;
|
||||||
|
|
||||||
|
// get title pane and check whether menu bar is embedded
|
||||||
|
FlatTitlePane titlePane = FlatRootPaneUI.getTitlePane( rootPane );
|
||||||
|
if( titlePane == null || !titlePane.isMenuBarEmbedded() )
|
||||||
|
return;
|
||||||
|
|
||||||
|
// check whether there is a horizontal glue (used for window title in embedded menu bar)
|
||||||
|
// and check minimum width of horizontal glue
|
||||||
|
Component horizontalGlue = titlePane.findHorizontalGlue( (JMenuBar) target );
|
||||||
|
int minTitleWidth = UIScale.scale( titlePane.titleMinimumWidth );
|
||||||
|
if( horizontalGlue != null && horizontalGlue.getWidth() < minTitleWidth ) {
|
||||||
|
// get index of glue component
|
||||||
|
int glueIndex = -1;
|
||||||
|
Component[] components = target.getComponents();
|
||||||
|
for( int i = components.length - 1; i >= 0; i-- ) {
|
||||||
|
if( components[i] == horizontalGlue ) {
|
||||||
|
glueIndex = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if( glueIndex < 0 )
|
||||||
|
return; // should never happen
|
||||||
|
|
||||||
|
if( target.getComponentOrientation().isLeftToRight() ) {
|
||||||
|
// left-to-right
|
||||||
|
|
||||||
|
// make horizontal glue wider (minimum title width)
|
||||||
|
int offset = minTitleWidth - horizontalGlue.getWidth();
|
||||||
|
horizontalGlue.setSize( minTitleWidth, horizontalGlue.getHeight() );
|
||||||
|
|
||||||
|
// check whether glue is fully visible
|
||||||
|
int minGlueX = target.getWidth() - target.getInsets().right - minTitleWidth;
|
||||||
|
if( minGlueX < horizontalGlue.getX() ) {
|
||||||
|
// move glue to the left to make it fully visible
|
||||||
|
offset -= (horizontalGlue.getX() - minGlueX);
|
||||||
|
horizontalGlue.setLocation( minGlueX, horizontalGlue.getY() );
|
||||||
|
|
||||||
|
// shrink and move components that are on the left side of the glue
|
||||||
|
for( int i = glueIndex - 1; i >= 0; i-- ) {
|
||||||
|
Component c = components[i];
|
||||||
|
if( c.getX() > minGlueX ) {
|
||||||
|
// move component and set width to zero
|
||||||
|
c.setBounds( minGlueX, c.getY(), 0, c.getHeight() );
|
||||||
|
} else {
|
||||||
|
// reduce size of component
|
||||||
|
c.setSize( minGlueX - c.getX(), c.getHeight() );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// move components that are on the right side of the glue
|
||||||
|
for( int i = glueIndex + 1; i < components.length; i++ ) {
|
||||||
|
Component c = components[i];
|
||||||
|
c.setLocation( c.getX() + offset, c.getY() );
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// right-to-left
|
||||||
|
|
||||||
|
// make horizontal glue wider (minimum title width)
|
||||||
|
int offset = minTitleWidth - horizontalGlue.getWidth();
|
||||||
|
horizontalGlue.setBounds( horizontalGlue.getX() - offset, horizontalGlue.getY(),
|
||||||
|
minTitleWidth, horizontalGlue.getHeight() );
|
||||||
|
|
||||||
|
// check whether glue is fully visible
|
||||||
|
int minGlueX = target.getInsets().left;
|
||||||
|
if( minGlueX > horizontalGlue.getX() ) {
|
||||||
|
// move glue to the right to make it fully visible
|
||||||
|
offset -= (horizontalGlue.getX() - minGlueX);
|
||||||
|
horizontalGlue.setLocation( minGlueX, horizontalGlue.getY() );
|
||||||
|
|
||||||
|
// shrink and move components that are on the right side of the glue
|
||||||
|
int x = horizontalGlue.getX() + horizontalGlue.getWidth();
|
||||||
|
for( int i = glueIndex - 1; i >= 0; i-- ) {
|
||||||
|
Component c = components[i];
|
||||||
|
if( c.getX() + c.getWidth() < x ) {
|
||||||
|
// move component and set width to zero
|
||||||
|
c.setBounds( x, c.getY(), 0, c.getHeight() );
|
||||||
|
} else {
|
||||||
|
// move component and reduce size
|
||||||
|
c.setBounds( x, c.getY(), c.getWidth() - (x - c.getX()), c.getHeight() );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// move components that are on the left side of the glue
|
||||||
|
for( int i = glueIndex + 1; i < components.length; i++ ) {
|
||||||
|
Component c = components[i];
|
||||||
|
c.setLocation( c.getX() - offset, c.getY() );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//---- class TakeFocus ----------------------------------------------------
|
//---- class TakeFocus ----------------------------------------------------
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ import javax.swing.plaf.MenuBarUI;
|
|||||||
public class FlatMenuItemBorder
|
public class FlatMenuItemBorder
|
||||||
extends FlatMarginBorder
|
extends FlatMarginBorder
|
||||||
{
|
{
|
||||||
// only used if parent menubar is not a instance of FlatMenuBarUI
|
// only used if parent menubar is not an instance of FlatMenuBarUI
|
||||||
private final Insets menuBarItemMargins = UIManager.getInsets( "MenuBar.itemMargins" );
|
private final Insets menuBarItemMargins = UIManager.getInsets( "MenuBar.itemMargins" );
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -97,6 +97,7 @@ public class FlatMenuItemRenderer
|
|||||||
@Styleable protected int underlineSelectionHeight = UIManager.getInt( "MenuItem.underlineSelectionHeight" );
|
@Styleable protected int underlineSelectionHeight = UIManager.getInt( "MenuItem.underlineSelectionHeight" );
|
||||||
|
|
||||||
private boolean iconsShared = true;
|
private boolean iconsShared = true;
|
||||||
|
private final Font menuFont = UIManager.getFont( "Menu.font" );
|
||||||
|
|
||||||
protected FlatMenuItemRenderer( JMenuItem menuItem, Icon checkIcon, Icon arrowIcon,
|
protected FlatMenuItemRenderer( JMenuItem menuItem, Icon checkIcon, Icon arrowIcon,
|
||||||
Font acceleratorFont, String acceleratorDelimiter )
|
Font acceleratorFont, String acceleratorDelimiter )
|
||||||
@@ -169,6 +170,19 @@ public class FlatMenuItemRenderer
|
|||||||
return infos;
|
return infos;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @since 2.5 */
|
||||||
|
public Object getStyleableValue( String key ) {
|
||||||
|
if( key.startsWith( "icon." ) ) {
|
||||||
|
String key2 = key.substring( "icon.".length() );
|
||||||
|
if( checkIcon instanceof FlatCheckBoxMenuItemIcon )
|
||||||
|
return ((FlatCheckBoxMenuItemIcon)checkIcon).getStyleableValue( key2 );
|
||||||
|
if( arrowIcon instanceof FlatMenuArrowIcon )
|
||||||
|
return ((FlatMenuArrowIcon)arrowIcon).getStyleableValue( key2 );
|
||||||
|
}
|
||||||
|
|
||||||
|
return FlatStylingSupport.getAnnotatedStyleableValue( this, key );
|
||||||
|
}
|
||||||
|
|
||||||
protected Dimension getPreferredMenuItemSize() {
|
protected Dimension getPreferredMenuItemSize() {
|
||||||
int width = 0;
|
int width = 0;
|
||||||
int height = 0;
|
int height = 0;
|
||||||
@@ -180,7 +194,8 @@ public class FlatMenuItemRenderer
|
|||||||
|
|
||||||
// layout icon and text
|
// layout icon and text
|
||||||
SwingUtilities.layoutCompoundLabel( menuItem,
|
SwingUtilities.layoutCompoundLabel( menuItem,
|
||||||
menuItem.getFontMetrics( menuItem.getFont() ), menuItem.getText(), getIconForLayout(),
|
menuItem.getFontMetrics( isTopLevelMenu ? getTopLevelFont() : menuItem.getFont() ),
|
||||||
|
menuItem.getText(), getIconForLayout(),
|
||||||
menuItem.getVerticalAlignment(), menuItem.getHorizontalAlignment(),
|
menuItem.getVerticalAlignment(), menuItem.getHorizontalAlignment(),
|
||||||
menuItem.getVerticalTextPosition(), menuItem.getHorizontalTextPosition(),
|
menuItem.getVerticalTextPosition(), menuItem.getHorizontalTextPosition(),
|
||||||
viewRect, iconRect, textRect, scale( menuItem.getIconTextGap() ) );
|
viewRect, iconRect, textRect, scale( menuItem.getIconTextGap() ) );
|
||||||
@@ -282,7 +297,8 @@ public class FlatMenuItemRenderer
|
|||||||
|
|
||||||
// layout icon and text
|
// layout icon and text
|
||||||
SwingUtilities.layoutCompoundLabel( menuItem,
|
SwingUtilities.layoutCompoundLabel( menuItem,
|
||||||
menuItem.getFontMetrics( menuItem.getFont() ), menuItem.getText(), getIconForLayout(),
|
menuItem.getFontMetrics( isTopLevelMenu ? getTopLevelFont() : menuItem.getFont() ),
|
||||||
|
menuItem.getText(), getIconForLayout(),
|
||||||
menuItem.getVerticalAlignment(), menuItem.getHorizontalAlignment(),
|
menuItem.getVerticalAlignment(), menuItem.getHorizontalAlignment(),
|
||||||
menuItem.getVerticalTextPosition(), menuItem.getHorizontalTextPosition(),
|
menuItem.getVerticalTextPosition(), menuItem.getHorizontalTextPosition(),
|
||||||
labelRect, iconRect, textRect, scale( menuItem.getIconTextGap() ) );
|
labelRect, iconRect, textRect, scale( menuItem.getIconTextGap() ) );
|
||||||
@@ -392,9 +408,10 @@ debug*/
|
|||||||
}
|
}
|
||||||
|
|
||||||
int mnemonicIndex = FlatLaf.isShowMnemonics() ? menuItem.getDisplayedMnemonicIndex() : -1;
|
int mnemonicIndex = FlatLaf.isShowMnemonics() ? menuItem.getDisplayedMnemonicIndex() : -1;
|
||||||
Color foreground = (isTopLevelMenu( menuItem ) ? menuItem.getParent() : menuItem).getForeground();
|
boolean isTopLevelMenu = isTopLevelMenu( menuItem );
|
||||||
|
Color foreground = (isTopLevelMenu ? menuItem.getParent() : menuItem).getForeground();
|
||||||
|
|
||||||
paintText( g, menuItem, textRect, text, mnemonicIndex, menuItem.getFont(),
|
paintText( g, menuItem, textRect, text, mnemonicIndex, isTopLevelMenu ? getTopLevelFont() : menuItem.getFont(),
|
||||||
foreground, isUnderlineSelection() ? foreground : selectionForeground, disabledForeground );
|
foreground, isUnderlineSelection() ? foreground : selectionForeground, disabledForeground );
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -446,6 +463,19 @@ debug*/
|
|||||||
protected static void paintHTMLText( Graphics g, JMenuItem menuItem,
|
protected static void paintHTMLText( Graphics g, JMenuItem menuItem,
|
||||||
Rectangle textRect, View htmlView, Color selectionForeground )
|
Rectangle textRect, View htmlView, Color selectionForeground )
|
||||||
{
|
{
|
||||||
|
// On Windows, using Segoe UI font, Java 15 or older and scaling greater than 1,
|
||||||
|
// the width of the HTML view may be initially too small (because component
|
||||||
|
// is not connected to a GraphicsConfiguration when getPreferredSize() is invoked).
|
||||||
|
// Since Java 16, this seems to be fixed somehow by doing popup menu layout twice.
|
||||||
|
//
|
||||||
|
// If using a too small width for htmlView.paint(), the view would rearrange
|
||||||
|
// its children and wrap them to two lines. To avoid this, use view preferred X span
|
||||||
|
// for painting. Core Lafs actually do the same in class MenuItemLayoutHelper.
|
||||||
|
//
|
||||||
|
// Example HTML text that causes the problem: "<html>some <b>HTML</b> <i>text</i></html>"
|
||||||
|
textRect = new Rectangle( textRect );
|
||||||
|
textRect.width = (int) htmlView.getPreferredSpan( View.X_AXIS );
|
||||||
|
|
||||||
if( isArmedOrSelected( menuItem ) && selectionForeground != null )
|
if( isArmedOrSelected( menuItem ) && selectionForeground != null )
|
||||||
g = new GraphicsProxyWithTextColor( (Graphics2D) g, selectionForeground );
|
g = new GraphicsProxyWithTextColor( (Graphics2D) g, selectionForeground );
|
||||||
|
|
||||||
@@ -468,6 +498,11 @@ debug*/
|
|||||||
return "underline".equals( UIManager.getString( "MenuItem.selectionType" ) );
|
return "underline".equals( UIManager.getString( "MenuItem.selectionType" ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Font getTopLevelFont() {
|
||||||
|
Font font = menuItem.getFont();
|
||||||
|
return (font != menuFont) ? font : menuItem.getParent().getFont();
|
||||||
|
}
|
||||||
|
|
||||||
private Icon getIconForPainting() {
|
private Icon getIconForPainting() {
|
||||||
Icon icon = menuItem.getIcon();
|
Icon icon = menuItem.getIcon();
|
||||||
|
|
||||||
|
|||||||
@@ -16,16 +16,19 @@
|
|||||||
|
|
||||||
package com.formdev.flatlaf.ui;
|
package com.formdev.flatlaf.ui;
|
||||||
|
|
||||||
import java.awt.Color;
|
|
||||||
import java.awt.Dimension;
|
import java.awt.Dimension;
|
||||||
import java.awt.Graphics;
|
import java.awt.Graphics;
|
||||||
import java.beans.PropertyChangeListener;
|
import java.beans.PropertyChangeListener;
|
||||||
|
import java.lang.invoke.MethodHandles;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import javax.swing.Icon;
|
import javax.swing.Icon;
|
||||||
import javax.swing.JComponent;
|
import javax.swing.JComponent;
|
||||||
|
import javax.swing.JMenuItem;
|
||||||
import javax.swing.LookAndFeel;
|
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;
|
||||||
|
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableField;
|
||||||
|
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableLookupProvider;
|
||||||
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
|
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
|
||||||
import com.formdev.flatlaf.ui.FlatStylingSupport.UnknownStyleException;
|
import com.formdev.flatlaf.ui.FlatStylingSupport.UnknownStyleException;
|
||||||
import com.formdev.flatlaf.util.LoggingFacade;
|
import com.formdev.flatlaf.util.LoggingFacade;
|
||||||
@@ -58,9 +61,15 @@ import com.formdev.flatlaf.util.LoggingFacade;
|
|||||||
*
|
*
|
||||||
* @author Karl Tauber
|
* @author Karl Tauber
|
||||||
*/
|
*/
|
||||||
|
@StyleableField( cls=BasicMenuItemUI.class, key="selectionBackground" )
|
||||||
|
@StyleableField( cls=BasicMenuItemUI.class, key="selectionForeground" )
|
||||||
|
@StyleableField( cls=BasicMenuItemUI.class, key="disabledForeground" )
|
||||||
|
@StyleableField( cls=BasicMenuItemUI.class, key="acceleratorForeground" )
|
||||||
|
@StyleableField( cls=BasicMenuItemUI.class, key="acceleratorSelectionForeground" )
|
||||||
|
|
||||||
public class FlatMenuItemUI
|
public class FlatMenuItemUI
|
||||||
extends BasicMenuItemUI
|
extends BasicMenuItemUI
|
||||||
implements StyleableUI
|
implements StyleableUI, StyleableLookupProvider
|
||||||
{
|
{
|
||||||
private FlatMenuItemRenderer renderer;
|
private FlatMenuItemRenderer renderer;
|
||||||
private Map<String, Object> oldStyleValues;
|
private Map<String, Object> oldStyleValues;
|
||||||
@@ -119,42 +128,54 @@ public class FlatMenuItemUI
|
|||||||
|
|
||||||
/** @since 2 */
|
/** @since 2 */
|
||||||
protected Object applyStyleProperty( String key, Object value ) {
|
protected Object applyStyleProperty( String key, Object value ) {
|
||||||
|
return applyStyleProperty( menuItem, this, renderer, key, value );
|
||||||
|
}
|
||||||
|
|
||||||
|
static Object applyStyleProperty( JMenuItem menuItem, BasicMenuItemUI ui,
|
||||||
|
FlatMenuItemRenderer renderer, String key, Object value )
|
||||||
|
{
|
||||||
try {
|
try {
|
||||||
return renderer.applyStyleProperty( key, value );
|
return renderer.applyStyleProperty( key, value );
|
||||||
} catch ( UnknownStyleException ex ) {
|
} catch ( UnknownStyleException ex ) {
|
||||||
// ignore
|
// ignore
|
||||||
}
|
}
|
||||||
|
|
||||||
Object oldValue;
|
return FlatStylingSupport.applyToAnnotatedObjectOrComponent( ui, menuItem, key, value );
|
||||||
switch( key ) {
|
|
||||||
// BasicMenuItemUI
|
|
||||||
case "selectionBackground": oldValue = selectionBackground; selectionBackground = (Color) value; return oldValue;
|
|
||||||
case "selectionForeground": oldValue = selectionForeground; selectionForeground = (Color) value; return oldValue;
|
|
||||||
case "disabledForeground": oldValue = disabledForeground; disabledForeground = (Color) value; return oldValue;
|
|
||||||
case "acceleratorForeground": oldValue = acceleratorForeground; acceleratorForeground = (Color) value; return oldValue;
|
|
||||||
case "acceleratorSelectionForeground": oldValue = acceleratorSelectionForeground; acceleratorSelectionForeground = (Color) value; return oldValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
return FlatStylingSupport.applyToAnnotatedObjectOrComponent( this, menuItem, key, value );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @since 2 */
|
/** @since 2 */
|
||||||
@Override
|
@Override
|
||||||
public Map<String, Class<?>> getStyleableInfos( JComponent c ) {
|
public Map<String, Class<?>> getStyleableInfos( JComponent c ) {
|
||||||
return getStyleableInfos( renderer );
|
return getStyleableInfos( this, renderer );
|
||||||
}
|
}
|
||||||
|
|
||||||
static Map<String, Class<?>> getStyleableInfos( FlatMenuItemRenderer renderer ) {
|
static Map<String, Class<?>> getStyleableInfos( BasicMenuItemUI ui, FlatMenuItemRenderer renderer ) {
|
||||||
Map<String, Class<?>> infos = new FlatStylingSupport.StyleableInfosMap<>();
|
Map<String, Class<?>> infos = FlatStylingSupport.getAnnotatedStyleableInfos( ui );
|
||||||
infos.put( "selectionBackground", Color.class );
|
|
||||||
infos.put( "selectionForeground", Color.class );
|
|
||||||
infos.put( "disabledForeground", Color.class );
|
|
||||||
infos.put( "acceleratorForeground", Color.class );
|
|
||||||
infos.put( "acceleratorSelectionForeground", Color.class );
|
|
||||||
infos.putAll( renderer.getStyleableInfos() );
|
infos.putAll( renderer.getStyleableInfos() );
|
||||||
return infos;
|
return infos;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @since 2.5 */
|
||||||
|
@Override
|
||||||
|
public Object getStyleableValue( JComponent c, String key ) {
|
||||||
|
return getStyleableValue( this, renderer, key );
|
||||||
|
}
|
||||||
|
|
||||||
|
static Object getStyleableValue( BasicMenuItemUI ui, FlatMenuItemRenderer renderer, String key ) {
|
||||||
|
Object value = renderer.getStyleableValue( key );
|
||||||
|
if( value == null )
|
||||||
|
value = FlatStylingSupport.getAnnotatedStyleableValue( ui, key );
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2.5 */
|
||||||
|
@Override
|
||||||
|
public MethodHandles.Lookup getLookupForStyling() {
|
||||||
|
// MethodHandles.lookup() is caller sensitive and must be invoked in this class,
|
||||||
|
// otherwise it is not possible to access protected fields in JRE superclass
|
||||||
|
return MethodHandles.lookup();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Dimension getPreferredMenuItemSize( JComponent c, Icon checkIcon, Icon arrowIcon, int defaultTextIconGap ) {
|
protected Dimension getPreferredMenuItemSize( JComponent c, Icon checkIcon, Icon arrowIcon, int defaultTextIconGap ) {
|
||||||
return renderer.getPreferredMenuItemSize();
|
return renderer.getPreferredMenuItemSize();
|
||||||
|
|||||||
@@ -20,8 +20,10 @@ import java.awt.Color;
|
|||||||
import java.awt.Dimension;
|
import java.awt.Dimension;
|
||||||
import java.awt.Font;
|
import java.awt.Font;
|
||||||
import java.awt.Graphics;
|
import java.awt.Graphics;
|
||||||
|
import java.awt.Rectangle;
|
||||||
import java.awt.event.MouseEvent;
|
import java.awt.event.MouseEvent;
|
||||||
import java.beans.PropertyChangeListener;
|
import java.beans.PropertyChangeListener;
|
||||||
|
import java.lang.invoke.MethodHandles;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import javax.swing.ButtonModel;
|
import javax.swing.ButtonModel;
|
||||||
@@ -35,9 +37,11 @@ import javax.swing.UIManager;
|
|||||||
import javax.swing.event.MouseInputListener;
|
import javax.swing.event.MouseInputListener;
|
||||||
import javax.swing.plaf.ComponentUI;
|
import javax.swing.plaf.ComponentUI;
|
||||||
import javax.swing.plaf.MenuBarUI;
|
import javax.swing.plaf.MenuBarUI;
|
||||||
|
import javax.swing.plaf.basic.BasicMenuItemUI;
|
||||||
import javax.swing.plaf.basic.BasicMenuUI;
|
import javax.swing.plaf.basic.BasicMenuUI;
|
||||||
|
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableField;
|
||||||
|
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableLookupProvider;
|
||||||
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
|
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
|
||||||
import com.formdev.flatlaf.ui.FlatStylingSupport.UnknownStyleException;
|
|
||||||
import com.formdev.flatlaf.util.LoggingFacade;
|
import com.formdev.flatlaf.util.LoggingFacade;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -72,15 +76,23 @@ import com.formdev.flatlaf.util.LoggingFacade;
|
|||||||
* <!-- FlatMenuRenderer -->
|
* <!-- FlatMenuRenderer -->
|
||||||
*
|
*
|
||||||
* @uiDefault MenuBar.hoverBackground Color
|
* @uiDefault MenuBar.hoverBackground Color
|
||||||
|
* @uiDefault MenuBar.selectionBackground Color
|
||||||
|
* @uiDefault MenuBar.selectionForeground Color
|
||||||
* @uiDefault MenuBar.underlineSelectionBackground Color
|
* @uiDefault MenuBar.underlineSelectionBackground Color
|
||||||
* @uiDefault MenuBar.underlineSelectionColor Color
|
* @uiDefault MenuBar.underlineSelectionColor Color
|
||||||
* @uiDefault MenuBar.underlineSelectionHeight int
|
* @uiDefault MenuBar.underlineSelectionHeight int
|
||||||
*
|
*
|
||||||
* @author Karl Tauber
|
* @author Karl Tauber
|
||||||
*/
|
*/
|
||||||
|
@StyleableField( cls=BasicMenuItemUI.class, key="selectionBackground" )
|
||||||
|
@StyleableField( cls=BasicMenuItemUI.class, key="selectionForeground" )
|
||||||
|
@StyleableField( cls=BasicMenuItemUI.class, key="disabledForeground" )
|
||||||
|
@StyleableField( cls=BasicMenuItemUI.class, key="acceleratorForeground" )
|
||||||
|
@StyleableField( cls=BasicMenuItemUI.class, key="acceleratorSelectionForeground" )
|
||||||
|
|
||||||
public class FlatMenuUI
|
public class FlatMenuUI
|
||||||
extends BasicMenuUI
|
extends BasicMenuUI
|
||||||
implements StyleableUI
|
implements StyleableUI, StyleableLookupProvider
|
||||||
{
|
{
|
||||||
private FlatMenuItemRenderer renderer;
|
private FlatMenuItemRenderer renderer;
|
||||||
private Map<String, Object> oldStyleValues;
|
private Map<String, Object> oldStyleValues;
|
||||||
@@ -166,29 +178,27 @@ public class FlatMenuUI
|
|||||||
|
|
||||||
/** @since 2 */
|
/** @since 2 */
|
||||||
protected Object applyStyleProperty( String key, Object value ) {
|
protected Object applyStyleProperty( String key, Object value ) {
|
||||||
try {
|
return FlatMenuItemUI.applyStyleProperty( menuItem, this, renderer, key, value );
|
||||||
return renderer.applyStyleProperty( key, value );
|
|
||||||
} catch ( UnknownStyleException ex ) {
|
|
||||||
// ignore
|
|
||||||
}
|
|
||||||
|
|
||||||
Object oldValue;
|
|
||||||
switch( key ) {
|
|
||||||
// BasicMenuItemUI
|
|
||||||
case "selectionBackground": oldValue = selectionBackground; selectionBackground = (Color) value; return oldValue;
|
|
||||||
case "selectionForeground": oldValue = selectionForeground; selectionForeground = (Color) value; return oldValue;
|
|
||||||
case "disabledForeground": oldValue = disabledForeground; disabledForeground = (Color) value; return oldValue;
|
|
||||||
case "acceleratorForeground": oldValue = acceleratorForeground; acceleratorForeground = (Color) value; return oldValue;
|
|
||||||
case "acceleratorSelectionForeground": oldValue = acceleratorSelectionForeground; acceleratorSelectionForeground = (Color) value; return oldValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
return FlatStylingSupport.applyToAnnotatedObjectOrComponent( this, menuItem, key, value );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @since 2 */
|
/** @since 2 */
|
||||||
@Override
|
@Override
|
||||||
public Map<String, Class<?>> getStyleableInfos( JComponent c ) {
|
public Map<String, Class<?>> getStyleableInfos( JComponent c ) {
|
||||||
return FlatMenuItemUI.getStyleableInfos( renderer );
|
return FlatMenuItemUI.getStyleableInfos( this, renderer );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2.5 */
|
||||||
|
@Override
|
||||||
|
public Object getStyleableValue( JComponent c, String key ) {
|
||||||
|
return FlatMenuItemUI.getStyleableValue( this, renderer, key );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2.5 */
|
||||||
|
@Override
|
||||||
|
public MethodHandles.Lookup getLookupForStyling() {
|
||||||
|
// MethodHandles.lookup() is caller sensitive and must be invoked in this class,
|
||||||
|
// otherwise it is not possible to access protected fields in JRE superclass
|
||||||
|
return MethodHandles.lookup();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -216,6 +226,8 @@ public class FlatMenuUI
|
|||||||
extends FlatMenuItemRenderer
|
extends FlatMenuItemRenderer
|
||||||
{
|
{
|
||||||
protected Color hoverBackground = UIManager.getColor( "MenuBar.hoverBackground" );
|
protected Color hoverBackground = UIManager.getColor( "MenuBar.hoverBackground" );
|
||||||
|
protected Color menuBarSelectionBackground = UIManager.getColor( "MenuBar.selectionBackground" );
|
||||||
|
protected Color menuBarSelectionForeground = UIManager.getColor( "MenuBar.selectionForeground" );
|
||||||
protected Color menuBarUnderlineSelectionBackground = FlatUIUtils.getUIColor( "MenuBar.underlineSelectionBackground", underlineSelectionBackground );
|
protected Color menuBarUnderlineSelectionBackground = FlatUIUtils.getUIColor( "MenuBar.underlineSelectionBackground", underlineSelectionBackground );
|
||||||
protected Color menuBarUnderlineSelectionColor = FlatUIUtils.getUIColor( "MenuBar.underlineSelectionColor", underlineSelectionColor );
|
protected Color menuBarUnderlineSelectionColor = FlatUIUtils.getUIColor( "MenuBar.underlineSelectionColor", underlineSelectionColor );
|
||||||
protected int menuBarUnderlineSelectionHeight = FlatUIUtils.getUIInt( "MenuBar.underlineSelectionHeight", underlineSelectionHeight );
|
protected int menuBarUnderlineSelectionHeight = FlatUIUtils.getUIInt( "MenuBar.underlineSelectionHeight", underlineSelectionHeight );
|
||||||
@@ -231,6 +243,10 @@ public class FlatMenuUI
|
|||||||
if( ((JMenu)menuItem).isTopLevelMenu() ) {
|
if( ((JMenu)menuItem).isTopLevelMenu() ) {
|
||||||
if( isUnderlineSelection() )
|
if( isUnderlineSelection() )
|
||||||
selectionBackground = getStyleFromMenuBarUI( ui -> ui.underlineSelectionBackground, menuBarUnderlineSelectionBackground );
|
selectionBackground = getStyleFromMenuBarUI( ui -> ui.underlineSelectionBackground, menuBarUnderlineSelectionBackground );
|
||||||
|
else {
|
||||||
|
selectionBackground = getStyleFromMenuBarUI( ui -> ui.selectionBackground,
|
||||||
|
menuBarSelectionBackground != null ? menuBarSelectionBackground : selectionBackground );
|
||||||
|
}
|
||||||
|
|
||||||
ButtonModel model = menuItem.getModel();
|
ButtonModel model = menuItem.getModel();
|
||||||
if( model.isRollover() && !model.isArmed() && !model.isSelected() && model.isEnabled() ) {
|
if( model.isRollover() && !model.isArmed() && !model.isSelected() && model.isEnabled() ) {
|
||||||
@@ -243,6 +259,16 @@ public class FlatMenuUI
|
|||||||
super.paintBackground( g, selectionBackground );
|
super.paintBackground( g, selectionBackground );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void paintText( Graphics g, Rectangle textRect, String text, Color selectionForeground, Color disabledForeground ) {
|
||||||
|
if( ((JMenu)menuItem).isTopLevelMenu() && !isUnderlineSelection() ) {
|
||||||
|
selectionForeground = getStyleFromMenuBarUI( ui -> ui.selectionForeground,
|
||||||
|
menuBarSelectionForeground != null ? menuBarSelectionForeground : selectionForeground );
|
||||||
|
}
|
||||||
|
|
||||||
|
super.paintText( g, textRect, text, selectionForeground, disabledForeground );
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void paintUnderlineSelection( Graphics g, Color underlineSelectionColor, int underlineSelectionHeight ) {
|
protected void paintUnderlineSelection( Graphics g, Color underlineSelectionColor, int underlineSelectionHeight ) {
|
||||||
if( ((JMenu)menuItem).isTopLevelMenu() ) {
|
if( ((JMenu)menuItem).isTopLevelMenu() ) {
|
||||||
|
|||||||
@@ -0,0 +1,109 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2022 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.io.File;
|
||||||
|
import com.formdev.flatlaf.FlatSystemProperties;
|
||||||
|
import com.formdev.flatlaf.util.LoggingFacade;
|
||||||
|
import com.formdev.flatlaf.util.NativeLibrary;
|
||||||
|
import com.formdev.flatlaf.util.SystemInfo;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper class to load FlatLaf native library (.dll, .so or .dylib),
|
||||||
|
* if available for current operating system and CPU architecture.
|
||||||
|
*
|
||||||
|
* @author Karl Tauber
|
||||||
|
* @since 2.3
|
||||||
|
*/
|
||||||
|
class FlatNativeLibrary
|
||||||
|
{
|
||||||
|
private static NativeLibrary nativeLibrary;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads native library (if available) and returns whether loaded successfully.
|
||||||
|
* Returns {@code false} if no native library is available.
|
||||||
|
*/
|
||||||
|
static synchronized boolean isLoaded() {
|
||||||
|
initialize();
|
||||||
|
return (nativeLibrary != null) ? nativeLibrary.isLoaded() : false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void initialize() {
|
||||||
|
if( nativeLibrary != null )
|
||||||
|
return;
|
||||||
|
|
||||||
|
String libraryName;
|
||||||
|
if( SystemInfo.isWindows_10_orLater && (SystemInfo.isX86 || SystemInfo.isX86_64) ) {
|
||||||
|
// Windows: requires Windows 10 (x86 or x86_64)
|
||||||
|
|
||||||
|
libraryName = "flatlaf-windows-x86";
|
||||||
|
if( SystemInfo.isX86_64 )
|
||||||
|
libraryName += "_64";
|
||||||
|
|
||||||
|
// load jawt native library
|
||||||
|
if( !SystemInfo.isJava_9_orLater ) {
|
||||||
|
// In Java 8, load jawt.dll (part of JRE) explicitly because it
|
||||||
|
// is not found when running application with <jdk>/bin/java.exe.
|
||||||
|
// When using <jdk>/jre/bin/java.exe, it is found.
|
||||||
|
// jawt.dll is located in <jdk>/jre/bin/.
|
||||||
|
// Java 9 and later do not have this problem.
|
||||||
|
loadJAWT();
|
||||||
|
}
|
||||||
|
} else if( SystemInfo.isLinux && SystemInfo.isX86_64 ) {
|
||||||
|
// Linux: requires x86_64
|
||||||
|
|
||||||
|
libraryName = "flatlaf-linux-x86_64";
|
||||||
|
|
||||||
|
// Load jawt.so (part of JRE) explicitly because it is not found
|
||||||
|
// in all Java versions/distributions.
|
||||||
|
// E.g. not found in Java 13 and later from openjdk.java.net.
|
||||||
|
// There seems to be also differences between distributions.
|
||||||
|
// E.g. Adoptium Java 17 does not need this, but Java 17 from openjdk.java.net does.
|
||||||
|
loadJAWT();
|
||||||
|
} else
|
||||||
|
return; // no native library available for current OS or CPU architecture
|
||||||
|
|
||||||
|
// load native library
|
||||||
|
nativeLibrary = createNativeLibrary( libraryName );
|
||||||
|
}
|
||||||
|
|
||||||
|
private static NativeLibrary createNativeLibrary( String libraryName ) {
|
||||||
|
String libraryPath = System.getProperty( FlatSystemProperties.NATIVE_LIBRARY_PATH );
|
||||||
|
if( libraryPath != null ) {
|
||||||
|
File libraryFile = new File( libraryPath, System.mapLibraryName( libraryName ) );
|
||||||
|
if( libraryFile.exists() )
|
||||||
|
return new NativeLibrary( libraryFile, true );
|
||||||
|
else
|
||||||
|
LoggingFacade.INSTANCE.logSevere( "Did not find external library " + libraryFile + ", using extracted library instead", null );
|
||||||
|
}
|
||||||
|
|
||||||
|
return new NativeLibrary( "com/formdev/flatlaf/natives/" + libraryName, null, true );
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void loadJAWT() {
|
||||||
|
try {
|
||||||
|
System.loadLibrary( "jawt" );
|
||||||
|
} catch( UnsatisfiedLinkError ex ) {
|
||||||
|
// log error only if native library jawt.dll not already loaded
|
||||||
|
String message = ex.getMessage();
|
||||||
|
if( message == null || !message.contains( "already loaded in another classloader" ) )
|
||||||
|
LoggingFacade.INSTANCE.logSevere( null, ex );
|
||||||
|
} catch( Exception ex ) {
|
||||||
|
LoggingFacade.INSTANCE.logSevere( null, ex );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,104 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2022 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.Point;
|
||||||
|
import java.awt.Toolkit;
|
||||||
|
import java.awt.Window;
|
||||||
|
import java.awt.event.MouseEvent;
|
||||||
|
import java.awt.geom.AffineTransform;
|
||||||
|
import javax.swing.JDialog;
|
||||||
|
import javax.swing.JFrame;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Native methods for Linux.
|
||||||
|
* <p>
|
||||||
|
* <b>Note</b>: This is private API. Do not use!
|
||||||
|
*
|
||||||
|
* @author Karl Tauber
|
||||||
|
* @since 2.5
|
||||||
|
*/
|
||||||
|
class FlatNativeLinuxLibrary
|
||||||
|
{
|
||||||
|
static boolean isLoaded() {
|
||||||
|
return FlatNativeLibrary.isLoaded();
|
||||||
|
}
|
||||||
|
|
||||||
|
// direction for _NET_WM_MOVERESIZE message
|
||||||
|
// see https://specifications.freedesktop.org/wm-spec/wm-spec-latest.html
|
||||||
|
static final int MOVE = 8;
|
||||||
|
|
||||||
|
private static Boolean isXWindowSystem;
|
||||||
|
|
||||||
|
private static boolean isXWindowSystem() {
|
||||||
|
if( isXWindowSystem == null )
|
||||||
|
isXWindowSystem = Toolkit.getDefaultToolkit().getClass().getName().endsWith( ".XToolkit" );
|
||||||
|
return isXWindowSystem;
|
||||||
|
}
|
||||||
|
|
||||||
|
static boolean isWMUtilsSupported( Window window ) {
|
||||||
|
return hasCustomDecoration( window ) && isXWindowSystem() && isLoaded();
|
||||||
|
}
|
||||||
|
|
||||||
|
static boolean moveOrResizeWindow( Window window, MouseEvent e, int direction ) {
|
||||||
|
Point pt = scale( window, e.getLocationOnScreen() );
|
||||||
|
return xMoveOrResizeWindow( window, pt.x, pt.y, direction );
|
||||||
|
|
||||||
|
/*
|
||||||
|
try {
|
||||||
|
Class<?> cls = Class.forName( "com.formdev.flatlaf.natives.jna.linux.X11WmUtils" );
|
||||||
|
java.lang.reflect.Method m = cls.getMethod( "xMoveOrResizeWindow", Window.class, int.class, int.class, int.class );
|
||||||
|
return (Boolean) m.invoke( null, window, pt.x, pt.y, direction );
|
||||||
|
} catch (Exception ex) {
|
||||||
|
ex.printStackTrace();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
static boolean showWindowMenu( Window window, MouseEvent e ) {
|
||||||
|
Point pt = scale( window, e.getLocationOnScreen() );
|
||||||
|
return xShowWindowMenu( window, pt.x, pt.y );
|
||||||
|
|
||||||
|
/*
|
||||||
|
try {
|
||||||
|
Class<?> cls = Class.forName( "com.formdev.flatlaf.natives.jna.linux.X11WmUtils" );
|
||||||
|
java.lang.reflect.Method m = cls.getMethod( "xShowWindowMenu", Window.class, int.class, int.class );
|
||||||
|
return (Boolean) m.invoke( null, window, pt.x, pt.y );
|
||||||
|
} catch (Exception ex) {
|
||||||
|
ex.printStackTrace();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Point scale( Window window, Point pt ) {
|
||||||
|
AffineTransform transform = window.getGraphicsConfiguration().getDefaultTransform();
|
||||||
|
int x = (int) Math.round( pt.x * transform.getScaleX() );
|
||||||
|
int y = (int) Math.round( pt.y * transform.getScaleY() );
|
||||||
|
return new Point( x, y );
|
||||||
|
}
|
||||||
|
|
||||||
|
// X Window System
|
||||||
|
private static native boolean xMoveOrResizeWindow( Window window, int x, int y, int direction );
|
||||||
|
private static native boolean xShowWindowMenu( Window window, int x, int y );
|
||||||
|
|
||||||
|
private static boolean hasCustomDecoration( Window window ) {
|
||||||
|
return (window instanceof JFrame && JFrame.isDefaultLookAndFeelDecorated() && ((JFrame)window).isUndecorated()) ||
|
||||||
|
(window instanceof JDialog && JDialog.isDefaultLookAndFeelDecorated() && ((JDialog)window).isUndecorated());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -42,11 +42,13 @@ import com.formdev.flatlaf.util.SystemInfo;
|
|||||||
public class FlatNativeWindowBorder
|
public class FlatNativeWindowBorder
|
||||||
{
|
{
|
||||||
// can use window decorations if:
|
// can use window decorations if:
|
||||||
// - on Windows 10
|
// - on Windows 10 or later
|
||||||
|
// - not if system property "sun.java2d.opengl" is true on Windows 10
|
||||||
// - not when running in JetBrains Projector, Webswing or WinPE
|
// - not when running in JetBrains Projector, Webswing or WinPE
|
||||||
// - not disabled via system property
|
// - not disabled via system property
|
||||||
private static final boolean canUseWindowDecorations =
|
private static final boolean canUseWindowDecorations =
|
||||||
SystemInfo.isWindows_10_orLater &&
|
SystemInfo.isWindows_10_orLater &&
|
||||||
|
(SystemInfo.isWindows_11_orLater || !FlatSystemProperties.getBoolean( "sun.java2d.opengl", false )) &&
|
||||||
!SystemInfo.isProjector &&
|
!SystemInfo.isProjector &&
|
||||||
!SystemInfo.isWebswing &&
|
!SystemInfo.isWebswing &&
|
||||||
!SystemInfo.isWinPE &&
|
!SystemInfo.isWinPE &&
|
||||||
|
|||||||
@@ -127,6 +127,12 @@ public class FlatPanelUI
|
|||||||
return FlatStylingSupport.getAnnotatedStyleableInfos( this );
|
return FlatStylingSupport.getAnnotatedStyleableInfos( this );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @since 2.5 */
|
||||||
|
@Override
|
||||||
|
public Object getStyleableValue( JComponent c, String key ) {
|
||||||
|
return FlatStylingSupport.getAnnotatedStyleableValue( this, key );
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void update( Graphics g, JComponent c ) {
|
public void update( Graphics g, JComponent c ) {
|
||||||
// fill background
|
// fill background
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import java.awt.Toolkit;
|
|||||||
import java.awt.event.KeyAdapter;
|
import java.awt.event.KeyAdapter;
|
||||||
import java.awt.event.KeyEvent;
|
import java.awt.event.KeyEvent;
|
||||||
import java.awt.event.KeyListener;
|
import java.awt.event.KeyListener;
|
||||||
|
import java.beans.PropertyChangeEvent;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import javax.swing.Action;
|
import javax.swing.Action;
|
||||||
import javax.swing.ActionMap;
|
import javax.swing.ActionMap;
|
||||||
@@ -233,6 +234,14 @@ public class FlatPasswordFieldUI
|
|||||||
return infos;
|
return infos;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object getStyleableValue( JComponent c, String key ) {
|
||||||
|
if( key.equals( "capsLockIconColor" ) && capsLockIcon instanceof FlatCapsLockIcon )
|
||||||
|
return ((FlatCapsLockIcon)capsLockIcon).getStyleableValue( key );
|
||||||
|
|
||||||
|
return super.getStyleableValue( c, key );
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public View create( Element elem ) {
|
public View create( Element elem ) {
|
||||||
return new PasswordView( elem );
|
return new PasswordView( elem );
|
||||||
@@ -283,6 +292,7 @@ public class FlatPasswordFieldUI
|
|||||||
protected void installRevealButton() {
|
protected void installRevealButton() {
|
||||||
if( showRevealButton ) {
|
if( showRevealButton ) {
|
||||||
revealButton = createRevealButton();
|
revealButton = createRevealButton();
|
||||||
|
updateRevealButton();
|
||||||
installLayout();
|
installLayout();
|
||||||
getComponent().add( revealButton );
|
getComponent().add( revealButton );
|
||||||
}
|
}
|
||||||
@@ -290,28 +300,64 @@ public class FlatPasswordFieldUI
|
|||||||
|
|
||||||
/** @since 2 */
|
/** @since 2 */
|
||||||
protected JToggleButton createRevealButton() {
|
protected JToggleButton createRevealButton() {
|
||||||
JToggleButton button = new JToggleButton( revealIcon );
|
JPasswordField c = (JPasswordField) getComponent();
|
||||||
|
JToggleButton button = new JToggleButton( revealIcon, !c.echoCharIsSet() );
|
||||||
button.setName( "PasswordField.revealButton" );
|
button.setName( "PasswordField.revealButton" );
|
||||||
prepareLeadingOrTrailingComponent( button );
|
prepareLeadingOrTrailingComponent( button );
|
||||||
button.putClientProperty( FlatClientProperties.STYLE_CLASS, "inTextField revealButton" );
|
button.putClientProperty( FlatClientProperties.STYLE_CLASS, "inTextField revealButton" );
|
||||||
if( FlatClientProperties.clientPropertyBoolean( getComponent(), KEY_REVEAL_SELECTED, false ) ) {
|
if( FlatClientProperties.clientPropertyBoolean( c, KEY_REVEAL_SELECTED, false ) ) {
|
||||||
button.setSelected( true );
|
button.setSelected( true );
|
||||||
updateEchoChar( true );
|
updateEchoChar( true );
|
||||||
}
|
}
|
||||||
button.addActionListener( e -> {
|
button.addActionListener( e -> {
|
||||||
boolean selected = button.isSelected();
|
boolean selected = button.isSelected();
|
||||||
updateEchoChar( selected );
|
updateEchoChar( selected );
|
||||||
getComponent().putClientProperty( KEY_REVEAL_SELECTED, selected );
|
c.putClientProperty( KEY_REVEAL_SELECTED, selected );
|
||||||
} );
|
} );
|
||||||
return button;
|
return button;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @since 2.5 */
|
||||||
|
protected void updateRevealButton() {
|
||||||
|
if( revealButton == null )
|
||||||
|
return;
|
||||||
|
|
||||||
|
JTextComponent c = getComponent();
|
||||||
|
boolean visible = c.isEnabled();
|
||||||
|
if( visible != revealButton.isVisible() ) {
|
||||||
|
revealButton.setVisible( visible );
|
||||||
|
c.revalidate();
|
||||||
|
c.repaint();
|
||||||
|
|
||||||
|
if( !visible ) {
|
||||||
|
revealButton.setSelected( false );
|
||||||
|
updateEchoChar( false );
|
||||||
|
getComponent().putClientProperty( KEY_REVEAL_SELECTED, null );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void propertyChange( PropertyChangeEvent e ) {
|
||||||
|
super.propertyChange( e );
|
||||||
|
|
||||||
|
switch( e.getPropertyName() ) {
|
||||||
|
case "enabled":
|
||||||
|
updateRevealButton();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void updateEchoChar( boolean selected ) {
|
private void updateEchoChar( boolean selected ) {
|
||||||
char newEchoChar = selected
|
char newEchoChar = selected
|
||||||
? 0
|
? 0
|
||||||
: (echoChar != null ? echoChar : '*');
|
: (echoChar != null ? echoChar : '*');
|
||||||
|
|
||||||
JPasswordField c = (JPasswordField) getComponent();
|
JPasswordField c = (JPasswordField) getComponent();
|
||||||
|
if( newEchoChar == c.getEchoChar() )
|
||||||
|
return;
|
||||||
|
|
||||||
|
// set echo char
|
||||||
LookAndFeel.installProperty( c, "echoChar", newEchoChar );
|
LookAndFeel.installProperty( c, "echoChar", newEchoChar );
|
||||||
|
|
||||||
// check whether was able to set echo char via LookAndFeel.installProperty()
|
// check whether was able to set echo char via LookAndFeel.installProperty()
|
||||||
|
|||||||
@@ -36,7 +36,9 @@ import java.awt.Window;
|
|||||||
import java.awt.event.ComponentEvent;
|
import java.awt.event.ComponentEvent;
|
||||||
import java.awt.event.ComponentListener;
|
import java.awt.event.ComponentListener;
|
||||||
import java.awt.event.MouseEvent;
|
import java.awt.event.MouseEvent;
|
||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.invoke.MethodHandle;
|
||||||
|
import java.lang.invoke.MethodHandles;
|
||||||
|
import java.lang.invoke.MethodType;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import javax.swing.JComponent;
|
import javax.swing.JComponent;
|
||||||
import javax.swing.JLayeredPane;
|
import javax.swing.JLayeredPane;
|
||||||
@@ -64,8 +66,8 @@ import com.formdev.flatlaf.util.UIScale;
|
|||||||
public class FlatPopupFactory
|
public class FlatPopupFactory
|
||||||
extends PopupFactory
|
extends PopupFactory
|
||||||
{
|
{
|
||||||
private Method java8getPopupMethod;
|
private MethodHandle java8getPopupMethod;
|
||||||
private Method java9getPopupMethod;
|
private MethodHandle java9getPopupMethod;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Popup getPopup( Component owner, Component contents, int x, int y )
|
public Popup getPopup( Component owner, Component contents, int x, int y )
|
||||||
@@ -143,7 +145,7 @@ public class FlatPopupFactory
|
|||||||
* <p>
|
* <p>
|
||||||
* On a dual screen setup, where screens use different scale factors, it may happen
|
* On a dual screen setup, where screens use different scale factors, it may happen
|
||||||
* that the window location changes when showing a heavy weight popup window.
|
* that the window location changes when showing a heavy weight popup window.
|
||||||
* E.g. when opening an dialog on the secondary screen and making combobox popup visible.
|
* E.g. when opening a dialog on the secondary screen and making combobox popup visible.
|
||||||
* <p>
|
* <p>
|
||||||
* This is a workaround for https://bugs.openjdk.java.net/browse/JDK-8224608
|
* This is a workaround for https://bugs.openjdk.java.net/browse/JDK-8224608
|
||||||
*/
|
*/
|
||||||
@@ -192,23 +194,25 @@ public class FlatPopupFactory
|
|||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
if( SystemInfo.isJava_9_orLater ) {
|
if( SystemInfo.isJava_9_orLater ) {
|
||||||
|
// Java 9: protected Popup getPopup( Component owner, Component contents, int x, int y, boolean isHeavyWeightPopup )
|
||||||
if( java9getPopupMethod == null ) {
|
if( java9getPopupMethod == null ) {
|
||||||
java9getPopupMethod = PopupFactory.class.getDeclaredMethod(
|
MethodType mt = MethodType.methodType( Popup.class, Component.class, Component.class, int.class, int.class, boolean.class );
|
||||||
"getPopup", Component.class, Component.class, int.class, int.class, boolean.class );
|
java9getPopupMethod = MethodHandles.lookup().findVirtual( PopupFactory.class, "getPopup", mt );
|
||||||
}
|
}
|
||||||
return (Popup) java9getPopupMethod.invoke( this, owner, contents, x, y, true );
|
return (Popup) java9getPopupMethod.invoke( this, owner, contents, x, y, true );
|
||||||
} else {
|
} else {
|
||||||
// Java 8
|
// Java 8: private Popup getPopup( Component owner, Component contents, int ownerX, int ownerY, int popupType )
|
||||||
if( java8getPopupMethod == null ) {
|
if( java8getPopupMethod == null ) {
|
||||||
java8getPopupMethod = PopupFactory.class.getDeclaredMethod(
|
Method m = PopupFactory.class.getDeclaredMethod(
|
||||||
"getPopup", Component.class, Component.class, int.class, int.class, int.class );
|
"getPopup", Component.class, Component.class, int.class, int.class, int.class );
|
||||||
java8getPopupMethod.setAccessible( true );
|
m.setAccessible( true );
|
||||||
|
java8getPopupMethod = MethodHandles.lookup().unreflect( m );
|
||||||
}
|
}
|
||||||
return (Popup) java8getPopupMethod.invoke( this, owner, contents, x, y, /*HEAVY_WEIGHT_POPUP*/ 2 );
|
return (Popup) java8getPopupMethod.invoke( this, owner, contents, x, y, /*HEAVY_WEIGHT_POPUP*/ 2 );
|
||||||
}
|
}
|
||||||
} catch( NoSuchMethodException | SecurityException | IllegalAccessException | InvocationTargetException ex ) {
|
} catch( Throwable ex ) {
|
||||||
// ignore
|
// fallback
|
||||||
return null;
|
return super.getPopup( owner, contents, x, y );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -66,6 +66,16 @@ public class FlatPopupMenuBorder
|
|||||||
return infos;
|
return infos;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @since 2.5 */
|
||||||
|
@Override
|
||||||
|
public Object getStyleableValue( String key ) {
|
||||||
|
switch( key ) {
|
||||||
|
case "borderInsets": return getStyleableValue();
|
||||||
|
case "borderColor": return borderColor;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Color getLineColor() {
|
public Color getLineColor() {
|
||||||
return (borderColor != null) ? borderColor : super.getLineColor();
|
return (borderColor != null) ? borderColor : super.getLineColor();
|
||||||
|
|||||||
@@ -16,18 +16,58 @@
|
|||||||
|
|
||||||
package com.formdev.flatlaf.ui;
|
package com.formdev.flatlaf.ui;
|
||||||
|
|
||||||
|
import java.awt.BorderLayout;
|
||||||
|
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.EventQueue;
|
||||||
|
import java.awt.Graphics;
|
||||||
|
import java.awt.GraphicsConfiguration;
|
||||||
|
import java.awt.GraphicsDevice;
|
||||||
|
import java.awt.GraphicsEnvironment;
|
||||||
|
import java.awt.Insets;
|
||||||
import java.awt.LayoutManager;
|
import java.awt.LayoutManager;
|
||||||
|
import java.awt.Point;
|
||||||
|
import java.awt.Rectangle;
|
||||||
|
import java.awt.Toolkit;
|
||||||
|
import java.awt.event.ActionEvent;
|
||||||
|
import java.awt.event.ActionListener;
|
||||||
|
import java.awt.event.MouseEvent;
|
||||||
|
import java.awt.event.MouseListener;
|
||||||
|
import java.awt.event.MouseWheelEvent;
|
||||||
|
import java.awt.event.MouseWheelListener;
|
||||||
import java.beans.PropertyChangeListener;
|
import java.beans.PropertyChangeListener;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
import javax.swing.BoxLayout;
|
import javax.swing.BoxLayout;
|
||||||
|
import javax.swing.JButton;
|
||||||
import javax.swing.JComponent;
|
import javax.swing.JComponent;
|
||||||
|
import javax.swing.JMenuItem;
|
||||||
|
import javax.swing.JPanel;
|
||||||
|
import javax.swing.JPopupMenu;
|
||||||
|
import javax.swing.JScrollPane;
|
||||||
|
import javax.swing.JViewport;
|
||||||
|
import javax.swing.MenuElement;
|
||||||
|
import javax.swing.MenuSelectionManager;
|
||||||
|
import javax.swing.Popup;
|
||||||
|
import javax.swing.PopupFactory;
|
||||||
|
import javax.swing.SwingConstants;
|
||||||
|
import javax.swing.SwingUtilities;
|
||||||
|
import javax.swing.Timer;
|
||||||
|
import javax.swing.UIManager;
|
||||||
|
import javax.swing.event.MenuKeyEvent;
|
||||||
|
import javax.swing.event.MenuKeyListener;
|
||||||
|
import javax.swing.event.PopupMenuEvent;
|
||||||
|
import javax.swing.event.PopupMenuListener;
|
||||||
|
import javax.swing.plaf.ButtonUI;
|
||||||
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.BasicComboPopup;
|
||||||
|
import javax.swing.plaf.basic.BasicMenuItemUI;
|
||||||
import javax.swing.plaf.basic.BasicPopupMenuUI;
|
import javax.swing.plaf.basic.BasicPopupMenuUI;
|
||||||
import javax.swing.plaf.basic.DefaultMenuLayout;
|
import javax.swing.plaf.basic.DefaultMenuLayout;
|
||||||
|
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
|
||||||
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
|
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
|
||||||
import com.formdev.flatlaf.util.LoggingFacade;
|
import com.formdev.flatlaf.util.LoggingFacade;
|
||||||
|
|
||||||
@@ -41,12 +81,22 @@ import com.formdev.flatlaf.util.LoggingFacade;
|
|||||||
* @uiDefault PopupMenu.foreground Color
|
* @uiDefault PopupMenu.foreground Color
|
||||||
* @uiDefault PopupMenu.border Border
|
* @uiDefault PopupMenu.border Border
|
||||||
*
|
*
|
||||||
|
* <!-- FlatPopupMenuUI -->
|
||||||
|
*
|
||||||
|
* @uiDefault Component.arrowType String chevron (default) or triangle
|
||||||
|
* @uiDefault PopupMenu.scrollArrowColor Color
|
||||||
|
* @uiDefault PopupMenu.hoverScrollArrowBackground Color optional
|
||||||
|
*
|
||||||
* @author Karl Tauber
|
* @author Karl Tauber
|
||||||
*/
|
*/
|
||||||
public class FlatPopupMenuUI
|
public class FlatPopupMenuUI
|
||||||
extends BasicPopupMenuUI
|
extends BasicPopupMenuUI
|
||||||
implements StyleableUI
|
implements StyleableUI
|
||||||
{
|
{
|
||||||
|
/** @since 2.1 */ @Styleable protected String arrowType;
|
||||||
|
/** @since 2.1 */ @Styleable protected Color scrollArrowColor;
|
||||||
|
/** @since 2.1 */ @Styleable protected Color hoverScrollArrowBackground;
|
||||||
|
|
||||||
private PropertyChangeListener propertyChangeListener;
|
private PropertyChangeListener propertyChangeListener;
|
||||||
private Map<String, Object> oldStyleValues;
|
private Map<String, Object> oldStyleValues;
|
||||||
private AtomicBoolean borderShared;
|
private AtomicBoolean borderShared;
|
||||||
@@ -74,9 +124,21 @@ public class FlatPopupMenuUI
|
|||||||
public void installDefaults() {
|
public void installDefaults() {
|
||||||
super.installDefaults();
|
super.installDefaults();
|
||||||
|
|
||||||
|
arrowType = UIManager.getString( "Component.arrowType" );
|
||||||
|
scrollArrowColor = UIManager.getColor( "PopupMenu.scrollArrowColor" );
|
||||||
|
hoverScrollArrowBackground = UIManager.getColor( "PopupMenu.hoverScrollArrowBackground" );
|
||||||
|
|
||||||
LayoutManager layout = popupMenu.getLayout();
|
LayoutManager layout = popupMenu.getLayout();
|
||||||
if( layout == null || layout instanceof UIResource )
|
if( layout == null || layout instanceof UIResource )
|
||||||
popupMenu.setLayout( new FlatMenuLayout( popupMenu, BoxLayout.Y_AXIS ) );
|
popupMenu.setLayout( new FlatPopupMenuLayout( popupMenu, BoxLayout.Y_AXIS ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void uninstallDefaults() {
|
||||||
|
super.uninstallDefaults();
|
||||||
|
|
||||||
|
scrollArrowColor = null;
|
||||||
|
hoverScrollArrowBackground = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -122,12 +184,67 @@ public class FlatPopupMenuUI
|
|||||||
return FlatStylingSupport.getAnnotatedStyleableInfos( this, popupMenu.getBorder() );
|
return FlatStylingSupport.getAnnotatedStyleableInfos( this, popupMenu.getBorder() );
|
||||||
}
|
}
|
||||||
|
|
||||||
//---- class FlatMenuLayout -----------------------------------------------
|
/** @since 2.5 */
|
||||||
|
@Override
|
||||||
|
public Object getStyleableValue( JComponent c, String key ) {
|
||||||
|
return FlatStylingSupport.getAnnotatedStyleableValue( this, popupMenu.getBorder(), key );
|
||||||
|
}
|
||||||
|
|
||||||
protected static class FlatMenuLayout
|
@Override
|
||||||
|
public Popup getPopup( JPopupMenu popup, int x, int y ) {
|
||||||
|
// do not add scroller to combobox popups or to popups that already have a scroll pane
|
||||||
|
if( popup instanceof BasicComboPopup ||
|
||||||
|
(popup.getComponentCount() > 0 && popup.getComponent( 0 ) instanceof JScrollPane) )
|
||||||
|
return super.getPopup( popup, x, y );
|
||||||
|
|
||||||
|
// do not add scroller if popup fits into screen
|
||||||
|
Dimension prefSize = popup.getPreferredSize();
|
||||||
|
int screenHeight = getScreenHeightAt( x, y );
|
||||||
|
if( prefSize.height <= screenHeight )
|
||||||
|
return super.getPopup( popup, x, y );
|
||||||
|
|
||||||
|
// create scroller
|
||||||
|
FlatPopupScroller scroller = new FlatPopupScroller( popup );
|
||||||
|
scroller.setPreferredSize( new Dimension( prefSize.width, screenHeight ) );
|
||||||
|
|
||||||
|
// create popup
|
||||||
|
PopupFactory popupFactory = PopupFactory.getSharedInstance();
|
||||||
|
return popupFactory.getPopup( popup.getInvoker(), scroller, x, y );
|
||||||
|
}
|
||||||
|
|
||||||
|
private int getScreenHeightAt( int x, int y ) {
|
||||||
|
// find GraphicsConfiguration at popup location (similar to JPopupMenu.getCurrentGraphicsConfiguration())
|
||||||
|
GraphicsConfiguration gc = null;
|
||||||
|
for( GraphicsDevice device : GraphicsEnvironment.getLocalGraphicsEnvironment().getScreenDevices() ) {
|
||||||
|
if( device.getType() == GraphicsDevice.TYPE_RASTER_SCREEN ) {
|
||||||
|
GraphicsConfiguration dgc = device.getDefaultConfiguration();
|
||||||
|
if( dgc.getBounds().contains( x, y ) ) {
|
||||||
|
gc = dgc;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if( gc == null && popupMenu.getInvoker() != null )
|
||||||
|
gc = popupMenu.getInvoker().getGraphicsConfiguration();
|
||||||
|
|
||||||
|
// compute screen height
|
||||||
|
// (always subtract screen insets because there is no API to detect whether
|
||||||
|
// the popup can overlap the taskbar; see JPopupMenu.canPopupOverlapTaskBar())
|
||||||
|
Toolkit toolkit = Toolkit.getDefaultToolkit();
|
||||||
|
Rectangle screenBounds = (gc != null) ? gc.getBounds() : new Rectangle( toolkit.getScreenSize() );
|
||||||
|
Insets screenInsets = Toolkit.getDefaultToolkit().getScreenInsets( gc );
|
||||||
|
return screenBounds.height - screenInsets.top - screenInsets.bottom;
|
||||||
|
}
|
||||||
|
|
||||||
|
//---- class FlatPopupMenuLayout ------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @since 2.4
|
||||||
|
*/
|
||||||
|
protected static class FlatPopupMenuLayout
|
||||||
extends DefaultMenuLayout
|
extends DefaultMenuLayout
|
||||||
{
|
{
|
||||||
public FlatMenuLayout( Container target, int axis ) {
|
public FlatPopupMenuLayout( Container target, int axis ) {
|
||||||
super( target, axis );
|
super( target, axis );
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -138,4 +255,206 @@ public class FlatPopupMenuUI
|
|||||||
return super.preferredLayoutSize( target );
|
return super.preferredLayoutSize( target );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//---- class FlatPopupScroller --------------------------------------------
|
||||||
|
|
||||||
|
private class FlatPopupScroller
|
||||||
|
extends JPanel
|
||||||
|
implements MouseWheelListener, PopupMenuListener, MenuKeyListener
|
||||||
|
{
|
||||||
|
private final JPopupMenu popup;
|
||||||
|
|
||||||
|
private final JScrollPane scrollPane;
|
||||||
|
private final JButton scrollUpButton;
|
||||||
|
private final JButton scrollDownButton;
|
||||||
|
private int unitIncrement;
|
||||||
|
|
||||||
|
FlatPopupScroller( JPopupMenu popup ) {
|
||||||
|
super( new BorderLayout() );
|
||||||
|
this.popup = popup;
|
||||||
|
|
||||||
|
// this panel is required to avoid that JPopupMenu.setLocation() will be invoked
|
||||||
|
// while scrolling, because this would call JPopupMenu.showPopup()
|
||||||
|
JPanel view = new JPanel( new BorderLayout() );
|
||||||
|
view.add( popup, BorderLayout.CENTER );
|
||||||
|
|
||||||
|
scrollPane = new JScrollPane( view, JScrollPane.VERTICAL_SCROLLBAR_NEVER, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER );
|
||||||
|
scrollPane.setBorder( null );
|
||||||
|
|
||||||
|
scrollUpButton = new ArrowButton( SwingConstants.NORTH );
|
||||||
|
scrollDownButton = new ArrowButton( SwingConstants.SOUTH );
|
||||||
|
|
||||||
|
add( scrollPane, BorderLayout.CENTER );
|
||||||
|
add( scrollUpButton, BorderLayout.NORTH );
|
||||||
|
add( scrollDownButton, BorderLayout.SOUTH );
|
||||||
|
|
||||||
|
setBackground( popup.getBackground() );
|
||||||
|
setBorder( popup.getBorder() );
|
||||||
|
popup.setBorder( null );
|
||||||
|
|
||||||
|
popup.addPopupMenuListener( this );
|
||||||
|
popup.addMouseWheelListener( this );
|
||||||
|
popup.addMenuKeyListener( this );
|
||||||
|
|
||||||
|
updateArrowButtons();
|
||||||
|
}
|
||||||
|
|
||||||
|
void scroll( int unitsToScroll ) {
|
||||||
|
if( unitIncrement == 0 )
|
||||||
|
unitIncrement = new JMenuItem( "X" ).getPreferredSize().height;
|
||||||
|
|
||||||
|
JViewport viewport = scrollPane.getViewport();
|
||||||
|
Point viewPosition = viewport.getViewPosition();
|
||||||
|
int newY = viewPosition.y + (unitIncrement * unitsToScroll);
|
||||||
|
if( newY < 0 )
|
||||||
|
newY = 0;
|
||||||
|
else
|
||||||
|
newY = Math.min( newY, viewport.getViewSize().height - viewport.getExtentSize().height );
|
||||||
|
viewport.setViewPosition( new Point( viewPosition.x, newY ) );
|
||||||
|
|
||||||
|
updateArrowButtons();
|
||||||
|
}
|
||||||
|
|
||||||
|
void updateArrowButtons() {
|
||||||
|
JViewport viewport = scrollPane.getViewport();
|
||||||
|
Point viewPosition = viewport.getViewPosition();
|
||||||
|
|
||||||
|
scrollUpButton.setVisible( viewPosition.y > 0 );
|
||||||
|
scrollDownButton.setVisible( viewPosition.y < viewport.getViewSize().height - viewport.getExtentSize().height );
|
||||||
|
}
|
||||||
|
|
||||||
|
//---- interface PopupMenuListener ----
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void popupMenuWillBecomeInvisible( PopupMenuEvent e ) {
|
||||||
|
// restore popup border
|
||||||
|
popup.setBorder( getBorder() );
|
||||||
|
|
||||||
|
popup.removePopupMenuListener( this );
|
||||||
|
popup.removeMouseWheelListener( this );
|
||||||
|
popup.removeMenuKeyListener( this );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override public void popupMenuWillBecomeVisible( PopupMenuEvent e ) {}
|
||||||
|
@Override public void popupMenuCanceled( PopupMenuEvent e ) {}
|
||||||
|
|
||||||
|
//---- interface MouseWheelListener ----
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scroll when user rotates mouse wheel.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void mouseWheelMoved( MouseWheelEvent e ) {
|
||||||
|
// convert mouse location before scrolling
|
||||||
|
Point mouseLocation = SwingUtilities.convertPoint( (Component) e.getSource(), e.getPoint(), this );
|
||||||
|
|
||||||
|
// scroll
|
||||||
|
scroll( e.getUnitsToScroll() );
|
||||||
|
|
||||||
|
// select menu item at mouse location
|
||||||
|
Component c = SwingUtilities.getDeepestComponentAt( this, mouseLocation.x, mouseLocation.y );
|
||||||
|
if( c instanceof JMenuItem ) {
|
||||||
|
ButtonUI ui = ((JMenuItem)c).getUI();
|
||||||
|
if( ui instanceof BasicMenuItemUI )
|
||||||
|
MenuSelectionManager.defaultManager().setSelectedPath( ((BasicMenuItemUI)ui).getPath() );
|
||||||
|
}
|
||||||
|
|
||||||
|
// this avoids that the popup is closed when running on Java 8
|
||||||
|
// https://bugs.openjdk.java.net/browse/JDK-8075063
|
||||||
|
e.consume();
|
||||||
|
}
|
||||||
|
|
||||||
|
//---- interface MenuKeyListener ----
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scroll when user presses Up or Down keys.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void menuKeyPressed( MenuKeyEvent e ) {
|
||||||
|
// use invokeLater() because menu selection is not yet updated because
|
||||||
|
// this listener is invoked before another listener that updates the menu selection
|
||||||
|
EventQueue.invokeLater( () -> {
|
||||||
|
if( !isDisplayable() )
|
||||||
|
return;
|
||||||
|
|
||||||
|
MenuElement[] path = MenuSelectionManager.defaultManager().getSelectedPath();
|
||||||
|
if( path.length == 0 )
|
||||||
|
return;
|
||||||
|
|
||||||
|
// scroll selected menu item to visible area
|
||||||
|
Component c = path[path.length - 1].getComponent();
|
||||||
|
JViewport viewport = scrollPane.getViewport();
|
||||||
|
Point pt = SwingUtilities.convertPoint( c, 0, 0, viewport );
|
||||||
|
viewport.scrollRectToVisible( new Rectangle( pt, c.getSize() ) );
|
||||||
|
|
||||||
|
// update arrow buttons
|
||||||
|
boolean upVisible = scrollUpButton.isVisible();
|
||||||
|
updateArrowButtons();
|
||||||
|
if( !upVisible && scrollUpButton.isVisible() ) {
|
||||||
|
// if "up" button becomes visible, make sure that bottom menu item stays visible
|
||||||
|
Point viewPosition = viewport.getViewPosition();
|
||||||
|
int newY = viewPosition.y + scrollUpButton.getPreferredSize().height;
|
||||||
|
viewport.setViewPosition( new Point( viewPosition.x, newY ) );
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override public void menuKeyTyped( MenuKeyEvent e ) {}
|
||||||
|
@Override public void menuKeyReleased( MenuKeyEvent e ) {}
|
||||||
|
|
||||||
|
//---- class ArrowButton ----------------------------------------------
|
||||||
|
|
||||||
|
private class ArrowButton
|
||||||
|
extends FlatArrowButton
|
||||||
|
implements MouseListener, ActionListener
|
||||||
|
{
|
||||||
|
private Timer timer;
|
||||||
|
|
||||||
|
ArrowButton( int direction ) {
|
||||||
|
super( direction, arrowType, scrollArrowColor, null, null, hoverScrollArrowBackground, null, null );
|
||||||
|
|
||||||
|
addMouseListener( this );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void paint( Graphics g ) {
|
||||||
|
// always fill background to paint over border on HiDPI screens
|
||||||
|
g.setColor( popup.getBackground() );
|
||||||
|
g.fillRect( 0, 0, getWidth(), getHeight() );
|
||||||
|
|
||||||
|
super.paint( g );
|
||||||
|
}
|
||||||
|
|
||||||
|
//---- interface MouseListener ----
|
||||||
|
|
||||||
|
@Override public void mouseClicked( MouseEvent e ) {}
|
||||||
|
@Override public void mousePressed( MouseEvent e ) {}
|
||||||
|
@Override public void mouseReleased( MouseEvent e ) {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void mouseEntered( MouseEvent e ) {
|
||||||
|
if( timer == null )
|
||||||
|
timer = new Timer( 50, this );
|
||||||
|
timer.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void mouseExited( MouseEvent e ) {
|
||||||
|
if( timer != null )
|
||||||
|
timer.stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
//---- interface ActionListener ----
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void actionPerformed( ActionEvent e ) {
|
||||||
|
if( timer != null && !isDisplayable() ) {
|
||||||
|
timer.stop();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
scroll( direction == SwingConstants.NORTH ? -1 : 1 );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -160,6 +160,12 @@ public class FlatProgressBarUI
|
|||||||
return FlatStylingSupport.getAnnotatedStyleableInfos( this );
|
return FlatStylingSupport.getAnnotatedStyleableInfos( this );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @since 2.5 */
|
||||||
|
@Override
|
||||||
|
public Object getStyleableValue( JComponent c, String key ) {
|
||||||
|
return FlatStylingSupport.getAnnotatedStyleableValue( this, key );
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Dimension getPreferredSize( JComponent c ) {
|
public Dimension getPreferredSize( JComponent c ) {
|
||||||
Dimension size = super.getPreferredSize( c );
|
Dimension size = super.getPreferredSize( c );
|
||||||
|
|||||||
@@ -16,18 +16,20 @@
|
|||||||
|
|
||||||
package com.formdev.flatlaf.ui;
|
package com.formdev.flatlaf.ui;
|
||||||
|
|
||||||
import java.awt.Color;
|
|
||||||
import java.awt.Dimension;
|
import java.awt.Dimension;
|
||||||
import java.awt.Graphics;
|
import java.awt.Graphics;
|
||||||
import java.beans.PropertyChangeListener;
|
import java.beans.PropertyChangeListener;
|
||||||
|
import java.lang.invoke.MethodHandles;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import javax.swing.Icon;
|
import javax.swing.Icon;
|
||||||
import javax.swing.JComponent;
|
import javax.swing.JComponent;
|
||||||
import javax.swing.LookAndFeel;
|
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.BasicRadioButtonMenuItemUI;
|
import javax.swing.plaf.basic.BasicRadioButtonMenuItemUI;
|
||||||
|
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableField;
|
||||||
|
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableLookupProvider;
|
||||||
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
|
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
|
||||||
import com.formdev.flatlaf.ui.FlatStylingSupport.UnknownStyleException;
|
|
||||||
import com.formdev.flatlaf.util.LoggingFacade;
|
import com.formdev.flatlaf.util.LoggingFacade;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -58,9 +60,15 @@ import com.formdev.flatlaf.util.LoggingFacade;
|
|||||||
*
|
*
|
||||||
* @author Karl Tauber
|
* @author Karl Tauber
|
||||||
*/
|
*/
|
||||||
|
@StyleableField( cls=BasicMenuItemUI.class, key="selectionBackground" )
|
||||||
|
@StyleableField( cls=BasicMenuItemUI.class, key="selectionForeground" )
|
||||||
|
@StyleableField( cls=BasicMenuItemUI.class, key="disabledForeground" )
|
||||||
|
@StyleableField( cls=BasicMenuItemUI.class, key="acceleratorForeground" )
|
||||||
|
@StyleableField( cls=BasicMenuItemUI.class, key="acceleratorSelectionForeground" )
|
||||||
|
|
||||||
public class FlatRadioButtonMenuItemUI
|
public class FlatRadioButtonMenuItemUI
|
||||||
extends BasicRadioButtonMenuItemUI
|
extends BasicRadioButtonMenuItemUI
|
||||||
implements StyleableUI
|
implements StyleableUI, StyleableLookupProvider
|
||||||
{
|
{
|
||||||
private FlatMenuItemRenderer renderer;
|
private FlatMenuItemRenderer renderer;
|
||||||
private Map<String, Object> oldStyleValues;
|
private Map<String, Object> oldStyleValues;
|
||||||
@@ -119,29 +127,27 @@ public class FlatRadioButtonMenuItemUI
|
|||||||
|
|
||||||
/** @since 2 */
|
/** @since 2 */
|
||||||
protected Object applyStyleProperty( String key, Object value ) {
|
protected Object applyStyleProperty( String key, Object value ) {
|
||||||
try {
|
return FlatMenuItemUI.applyStyleProperty( menuItem, this, renderer, key, value );
|
||||||
return renderer.applyStyleProperty( key, value );
|
|
||||||
} catch ( UnknownStyleException ex ) {
|
|
||||||
// ignore
|
|
||||||
}
|
|
||||||
|
|
||||||
Object oldValue;
|
|
||||||
switch( key ) {
|
|
||||||
// BasicMenuItemUI
|
|
||||||
case "selectionBackground": oldValue = selectionBackground; selectionBackground = (Color) value; return oldValue;
|
|
||||||
case "selectionForeground": oldValue = selectionForeground; selectionForeground = (Color) value; return oldValue;
|
|
||||||
case "disabledForeground": oldValue = disabledForeground; disabledForeground = (Color) value; return oldValue;
|
|
||||||
case "acceleratorForeground": oldValue = acceleratorForeground; acceleratorForeground = (Color) value; return oldValue;
|
|
||||||
case "acceleratorSelectionForeground": oldValue = acceleratorSelectionForeground; acceleratorSelectionForeground = (Color) value; return oldValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
return FlatStylingSupport.applyToAnnotatedObjectOrComponent( this, menuItem, key, value );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @since 2 */
|
/** @since 2 */
|
||||||
@Override
|
@Override
|
||||||
public Map<String, Class<?>> getStyleableInfos( JComponent c ) {
|
public Map<String, Class<?>> getStyleableInfos( JComponent c ) {
|
||||||
return FlatMenuItemUI.getStyleableInfos( renderer );
|
return FlatMenuItemUI.getStyleableInfos( this, renderer );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2.5 */
|
||||||
|
@Override
|
||||||
|
public Object getStyleableValue( JComponent c, String key ) {
|
||||||
|
return FlatMenuItemUI.getStyleableValue( this, renderer, key );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2.5 */
|
||||||
|
@Override
|
||||||
|
public MethodHandles.Lookup getLookupForStyling() {
|
||||||
|
// MethodHandles.lookup() is caller sensitive and must be invoked in this class,
|
||||||
|
// otherwise it is not possible to access protected fields in JRE superclass
|
||||||
|
return MethodHandles.lookup();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -18,12 +18,16 @@ package com.formdev.flatlaf.ui;
|
|||||||
|
|
||||||
import static com.formdev.flatlaf.util.UIScale.scale;
|
import static com.formdev.flatlaf.util.UIScale.scale;
|
||||||
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.Insets;
|
import java.awt.Insets;
|
||||||
import java.awt.Rectangle;
|
import java.awt.Rectangle;
|
||||||
|
import java.awt.event.MouseAdapter;
|
||||||
|
import java.awt.event.MouseEvent;
|
||||||
import java.beans.PropertyChangeEvent;
|
import java.beans.PropertyChangeEvent;
|
||||||
|
import java.beans.PropertyChangeListener;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import javax.swing.AbstractButton;
|
import javax.swing.AbstractButton;
|
||||||
@@ -31,6 +35,7 @@ import javax.swing.CellRendererPane;
|
|||||||
import javax.swing.Icon;
|
import javax.swing.Icon;
|
||||||
import javax.swing.JComponent;
|
import javax.swing.JComponent;
|
||||||
import javax.swing.LookAndFeel;
|
import javax.swing.LookAndFeel;
|
||||||
|
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.BasicButtonListener;
|
import javax.swing.plaf.basic.BasicButtonListener;
|
||||||
@@ -78,7 +83,7 @@ public class FlatRadioButtonUI
|
|||||||
private Map<String, Object> oldStyleValues;
|
private Map<String, Object> oldStyleValues;
|
||||||
|
|
||||||
public static ComponentUI createUI( JComponent c ) {
|
public static ComponentUI createUI( JComponent c ) {
|
||||||
return FlatUIUtils.canUseSharedUI( c )
|
return FlatUIUtils.canUseSharedUI( c ) && !FlatUIUtils.needsLightAWTPeer( c )
|
||||||
? FlatUIUtils.createSharedUI( FlatRadioButtonUI.class, () -> new FlatRadioButtonUI( true ) )
|
? FlatUIUtils.createSharedUI( FlatRadioButtonUI.class, () -> new FlatRadioButtonUI( true ) )
|
||||||
: new FlatRadioButtonUI( false );
|
: new FlatRadioButtonUI( false );
|
||||||
}
|
}
|
||||||
@@ -90,11 +95,29 @@ public class FlatRadioButtonUI
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void installUI( JComponent c ) {
|
public void installUI( JComponent c ) {
|
||||||
|
if( FlatUIUtils.needsLightAWTPeer( c ) )
|
||||||
|
FlatUIUtils.runWithLightAWTPeerUIDefaults( () -> installUIImpl( c ) );
|
||||||
|
else
|
||||||
|
installUIImpl( c );
|
||||||
|
}
|
||||||
|
|
||||||
|
private void installUIImpl( JComponent c ) {
|
||||||
super.installUI( c );
|
super.installUI( c );
|
||||||
|
|
||||||
|
if( FlatUIUtils.isAWTPeer( c ) )
|
||||||
|
AWTPeerMouseExitedFix.install( c );
|
||||||
|
|
||||||
installStyle( (AbstractButton) c );
|
installStyle( (AbstractButton) c );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void uninstallUI( JComponent c ) {
|
||||||
|
super.uninstallUI( c );
|
||||||
|
|
||||||
|
if( FlatUIUtils.isAWTPeer( c ) )
|
||||||
|
AWTPeerMouseExitedFix.uninstall( c );
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void installDefaults( AbstractButton b ) {
|
public void installDefaults( AbstractButton b ) {
|
||||||
super.installDefaults( b );
|
super.installDefaults( b );
|
||||||
@@ -199,7 +222,20 @@ public class FlatRadioButtonUI
|
|||||||
return infos;
|
return infos;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Insets tempInsets = new Insets( 0, 0, 0, 0 );
|
/** @since 2.5 */
|
||||||
|
@Override
|
||||||
|
public Object getStyleableValue( JComponent c, String key ) {
|
||||||
|
// style icon
|
||||||
|
if( key.startsWith( "icon." ) ) {
|
||||||
|
return (icon instanceof FlatCheckBoxIcon)
|
||||||
|
? ((FlatCheckBoxIcon)icon).getStyleableValue( key.substring( "icon.".length() ) )
|
||||||
|
: null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return FlatStylingSupport.getAnnotatedStyleableValue( this, key );
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final Insets tempInsets = new Insets( 0, 0, 0, 0 );
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Dimension getPreferredSize( JComponent c ) {
|
public Dimension getPreferredSize( JComponent c ) {
|
||||||
@@ -212,7 +248,7 @@ public class FlatRadioButtonUI
|
|||||||
if( focusWidth > 0 ) {
|
if( focusWidth > 0 ) {
|
||||||
// Increase preferred width and height if insets were explicitly reduced (e.g. with
|
// Increase preferred width and height if insets were explicitly reduced (e.g. with
|
||||||
// an EmptyBorder) and icon has a focus width, which is not included in icon size.
|
// an EmptyBorder) and icon has a focus width, which is not included in icon size.
|
||||||
// Otherwise the component may be too small and outer focus border may be cut off.
|
// Otherwise, the component may be too small and outer focus border may be cut off.
|
||||||
Insets insets = c.getInsets( tempInsets );
|
Insets insets = c.getInsets( tempInsets );
|
||||||
size.width += Math.max( focusWidth - insets.left, 0 ) + Math.max( focusWidth - insets.right, 0 );
|
size.width += Math.max( focusWidth - insets.left, 0 ) + Math.max( focusWidth - insets.right, 0 );
|
||||||
size.height += Math.max( focusWidth - insets.top, 0 ) + Math.max( focusWidth - insets.bottom, 0 );
|
size.height += Math.max( focusWidth - insets.top, 0 ) + Math.max( focusWidth - insets.bottom, 0 );
|
||||||
@@ -308,4 +344,69 @@ public class FlatRadioButtonUI
|
|||||||
FlatRadioButtonUI.this.propertyChange( b, e );
|
FlatRadioButtonUI.this.propertyChange( b, e );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//---- class AWTPeerMouseExitedFix ----------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hack for missing mouse-exited event for java.awt.Checkbox on macOS (to fix hover effect).
|
||||||
|
*
|
||||||
|
* On macOS, AWT components internally use Swing components.
|
||||||
|
* This is implemented in class sun.lwawt.LWCheckboxPeer, which uses
|
||||||
|
* a container component CheckboxDelegate that has a JCheckBox and a JRadioButton
|
||||||
|
* as children. Only one of them is visible.
|
||||||
|
*
|
||||||
|
* The reason that mouse-exited event is not sent to the JCheckBox or JRadioButton
|
||||||
|
* is that sun.lwawt.LWComponentPeer.createDelegateEvent() uses
|
||||||
|
* SwingUtilities.getDeepestComponentAt() to find the event target,
|
||||||
|
* which finds the container component CheckboxDelegate,
|
||||||
|
* which receives the mouse-exited event.
|
||||||
|
*
|
||||||
|
* This class adds listeners and forwards the mouse-exited event
|
||||||
|
* from CheckboxDelegate to JCheckBox or JRadioButton.
|
||||||
|
*/
|
||||||
|
private static class AWTPeerMouseExitedFix
|
||||||
|
extends MouseAdapter
|
||||||
|
implements PropertyChangeListener
|
||||||
|
{
|
||||||
|
private final JComponent button;
|
||||||
|
|
||||||
|
static void install( JComponent button ) {
|
||||||
|
AWTPeerMouseExitedFix l = new AWTPeerMouseExitedFix( button );
|
||||||
|
button.addPropertyChangeListener( "ancestor", l );
|
||||||
|
Container parent = button.getParent();
|
||||||
|
if( parent != null )
|
||||||
|
parent.addMouseListener( l );
|
||||||
|
}
|
||||||
|
|
||||||
|
static void uninstall( JComponent button ) {
|
||||||
|
for( PropertyChangeListener l : button.getPropertyChangeListeners( "ancestor" ) ) {
|
||||||
|
if( l instanceof AWTPeerMouseExitedFix ) {
|
||||||
|
button.removePropertyChangeListener( "ancestor", l );
|
||||||
|
Container parent = button.getParent();
|
||||||
|
if( parent != null )
|
||||||
|
parent.removeMouseListener( (AWTPeerMouseExitedFix) l );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
AWTPeerMouseExitedFix( JComponent button ) {
|
||||||
|
this.button = button;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void propertyChange( PropertyChangeEvent e ) {
|
||||||
|
if( e.getOldValue() instanceof Component )
|
||||||
|
((Component)e.getOldValue()).removeMouseListener( this );
|
||||||
|
if( e.getNewValue() instanceof Component ) {
|
||||||
|
((Component)e.getNewValue()).removeMouseListener( this ); // avoid duplicate listeners
|
||||||
|
((Component)e.getNewValue()).addMouseListener( this );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void mouseExited( MouseEvent e ) {
|
||||||
|
button.dispatchEvent( SwingUtilities.convertMouseEvent( e.getComponent(), e, button ) );
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -179,8 +179,8 @@ public class FlatRootPaneUI
|
|||||||
super.installListeners( root );
|
super.installListeners( root );
|
||||||
|
|
||||||
if( SystemInfo.isJava_9_orLater ) {
|
if( SystemInfo.isJava_9_orLater ) {
|
||||||
// On HiDPI screens, where scaling is used, there may be white lines at the
|
// On HiDPI screens, where scaling is used, there may be white lines on the
|
||||||
// bottom and at the right side of the window when it is initially shown.
|
// bottom and on the right side of the window when it is initially shown.
|
||||||
// This is very disturbing in dark themes, but hard to notice in light themes.
|
// This is very disturbing in dark themes, but hard to notice in light themes.
|
||||||
// Seems to be a rounding issue when Swing adds dirty region of window
|
// Seems to be a rounding issue when Swing adds dirty region of window
|
||||||
// using RepaintManager.nativeAddDirtyRegion().
|
// using RepaintManager.nativeAddDirtyRegion().
|
||||||
@@ -364,6 +364,12 @@ public class FlatRootPaneUI
|
|||||||
((FlatRootPaneUI)ui).titlePane.isMenuBarEmbedded();
|
((FlatRootPaneUI)ui).titlePane.isMenuBarEmbedded();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @since 2.4 */
|
||||||
|
protected static FlatTitlePane getTitlePane( JRootPane rootPane ) {
|
||||||
|
RootPaneUI ui = rootPane.getUI();
|
||||||
|
return ui instanceof FlatRootPaneUI ? ((FlatRootPaneUI)ui).titlePane : null;
|
||||||
|
}
|
||||||
|
|
||||||
//---- class FlatRootLayout -----------------------------------------------
|
//---- class FlatRootLayout -----------------------------------------------
|
||||||
|
|
||||||
protected class FlatRootLayout
|
protected class FlatRootLayout
|
||||||
@@ -510,7 +516,7 @@ public class FlatRootPaneUI
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
Container parent = c.getParent();
|
Container parent = c.getParent();
|
||||||
boolean active = parent instanceof Window ? ((Window)parent).isActive() : false;
|
boolean active = parent instanceof Window && ((Window)parent).isActive();
|
||||||
|
|
||||||
g.setColor( FlatUIUtils.deriveColor( active ? activeBorderColor : inactiveBorderColor, baseBorderColor ) );
|
g.setColor( FlatUIUtils.deriveColor( active ? activeBorderColor : inactiveBorderColor, baseBorderColor ) );
|
||||||
HiDPIUtils.paintAtScale1x( (Graphics2D) g, x, y, width, height, this::paintImpl );
|
HiDPIUtils.paintAtScale1x( (Graphics2D) g, x, y, width, height, this::paintImpl );
|
||||||
@@ -522,9 +528,7 @@ public class FlatRootPaneUI
|
|||||||
|
|
||||||
protected boolean isWindowMaximized( Component c ) {
|
protected boolean isWindowMaximized( Component c ) {
|
||||||
Container parent = c.getParent();
|
Container parent = c.getParent();
|
||||||
return parent instanceof Frame
|
return parent instanceof Frame && (((Frame)parent).getExtendedState() & Frame.MAXIMIZED_BOTH) != 0;
|
||||||
? (((Frame)parent).getExtendedState() & Frame.MAXIMIZED_BOTH) != 0
|
|
||||||
: false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
package com.formdev.flatlaf.ui;
|
package com.formdev.flatlaf.ui;
|
||||||
|
|
||||||
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.Insets;
|
import java.awt.Insets;
|
||||||
@@ -24,6 +25,7 @@ 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.PropertyChangeListener;
|
import java.beans.PropertyChangeListener;
|
||||||
|
import java.lang.invoke.MethodHandles;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import javax.swing.InputMap;
|
import javax.swing.InputMap;
|
||||||
@@ -36,9 +38,13 @@ 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;
|
||||||
import com.formdev.flatlaf.FlatClientProperties;
|
import com.formdev.flatlaf.FlatClientProperties;
|
||||||
|
import com.formdev.flatlaf.FlatLaf;
|
||||||
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
|
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
|
||||||
|
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableField;
|
||||||
|
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableLookupProvider;
|
||||||
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
|
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
|
||||||
import com.formdev.flatlaf.util.LoggingFacade;
|
import com.formdev.flatlaf.util.LoggingFacade;
|
||||||
|
import com.formdev.flatlaf.util.SystemInfo;
|
||||||
import com.formdev.flatlaf.util.UIScale;
|
import com.formdev.flatlaf.util.UIScale;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -57,6 +63,7 @@ import com.formdev.flatlaf.util.UIScale;
|
|||||||
*
|
*
|
||||||
* <!-- FlatScrollBarUI -->
|
* <!-- FlatScrollBarUI -->
|
||||||
*
|
*
|
||||||
|
* @uiDefault ScrollBar.minimumButtonSize Dimension
|
||||||
* @uiDefault ScrollBar.trackInsets Insets
|
* @uiDefault ScrollBar.trackInsets Insets
|
||||||
* @uiDefault ScrollBar.thumbInsets Insets
|
* @uiDefault ScrollBar.thumbInsets Insets
|
||||||
* @uiDefault ScrollBar.trackArc int
|
* @uiDefault ScrollBar.trackArc int
|
||||||
@@ -76,13 +83,20 @@ import com.formdev.flatlaf.util.UIScale;
|
|||||||
*
|
*
|
||||||
* @author Karl Tauber
|
* @author Karl Tauber
|
||||||
*/
|
*/
|
||||||
|
@StyleableField( cls=BasicScrollBarUI.class, key="track", fieldName="trackColor" )
|
||||||
|
@StyleableField( cls=BasicScrollBarUI.class, key="thumb", fieldName="thumbColor" )
|
||||||
|
@StyleableField( cls=BasicScrollBarUI.class, key="width", fieldName="scrollBarWidth" )
|
||||||
|
@StyleableField( cls=BasicScrollBarUI.class, key="minimumThumbSize" )
|
||||||
|
@StyleableField( cls=BasicScrollBarUI.class, key="maximumThumbSize" )
|
||||||
|
|
||||||
public class FlatScrollBarUI
|
public class FlatScrollBarUI
|
||||||
extends BasicScrollBarUI
|
extends BasicScrollBarUI
|
||||||
implements StyleableUI
|
implements StyleableUI, StyleableLookupProvider
|
||||||
{
|
{
|
||||||
// overrides BasicScrollBarUI.supportsAbsolutePositioning (which is private)
|
// overrides BasicScrollBarUI.supportsAbsolutePositioning (which is private)
|
||||||
@Styleable protected boolean allowsAbsolutePositioning;
|
@Styleable protected boolean allowsAbsolutePositioning;
|
||||||
|
|
||||||
|
/** @since 2.1 */ @Styleable protected Dimension minimumButtonSize;
|
||||||
@Styleable protected Insets trackInsets;
|
@Styleable protected Insets trackInsets;
|
||||||
@Styleable protected Insets thumbInsets;
|
@Styleable protected Insets thumbInsets;
|
||||||
@Styleable protected int trackArc;
|
@Styleable protected int trackArc;
|
||||||
@@ -106,6 +120,7 @@ public class FlatScrollBarUI
|
|||||||
protected boolean hoverThumb;
|
protected boolean hoverThumb;
|
||||||
|
|
||||||
private Map<String, Object> oldStyleValues;
|
private Map<String, Object> oldStyleValues;
|
||||||
|
private boolean isAWTPeer;
|
||||||
|
|
||||||
public static ComponentUI createUI( JComponent c ) {
|
public static ComponentUI createUI( JComponent c ) {
|
||||||
return new FlatScrollBarUI();
|
return new FlatScrollBarUI();
|
||||||
@@ -142,6 +157,7 @@ public class FlatScrollBarUI
|
|||||||
|
|
||||||
allowsAbsolutePositioning = super.getSupportsAbsolutePositioning();
|
allowsAbsolutePositioning = super.getSupportsAbsolutePositioning();
|
||||||
|
|
||||||
|
minimumButtonSize = UIManager.getDimension( "ScrollBar.minimumButtonSize" );
|
||||||
trackInsets = UIManager.getInsets( "ScrollBar.trackInsets" );
|
trackInsets = UIManager.getInsets( "ScrollBar.trackInsets" );
|
||||||
thumbInsets = UIManager.getInsets( "ScrollBar.thumbInsets" );
|
thumbInsets = UIManager.getInsets( "ScrollBar.thumbInsets" );
|
||||||
trackArc = UIManager.getInt( "ScrollBar.trackArc" );
|
trackArc = UIManager.getInt( "ScrollBar.trackArc" );
|
||||||
@@ -171,6 +187,7 @@ public class FlatScrollBarUI
|
|||||||
protected void uninstallDefaults() {
|
protected void uninstallDefaults() {
|
||||||
super.uninstallDefaults();
|
super.uninstallDefaults();
|
||||||
|
|
||||||
|
minimumButtonSize = null;
|
||||||
trackInsets = null;
|
trackInsets = null;
|
||||||
thumbInsets = null;
|
thumbInsets = null;
|
||||||
hoverTrackColor = null;
|
hoverTrackColor = null;
|
||||||
@@ -217,6 +234,37 @@ public class FlatScrollBarUI
|
|||||||
}
|
}
|
||||||
SwingUtilities.replaceUIInputMap( scrollbar, JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, inputMap );
|
SwingUtilities.replaceUIInputMap( scrollbar, JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, inputMap );
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case "ancestor":
|
||||||
|
// check whether scroll bar is used as AWT peer on macOS
|
||||||
|
if( SystemInfo.isMacOS ) {
|
||||||
|
Container p = scrollbar.getParent();
|
||||||
|
for( int i = 0; i < 2 && p != null; i++, p = p.getParent() ) {
|
||||||
|
if( FlatUIUtils.isAWTPeer( p ) ) {
|
||||||
|
// Used to disable hover, which does not work correctly
|
||||||
|
// because scroll bars do not receive mouse exited event.
|
||||||
|
// The scroll pane, including its scroll bars, is not part
|
||||||
|
// of the component hierarchy and does not receive mouse events
|
||||||
|
// directly. Instead LWComponentPeer receives mouse events
|
||||||
|
// and delegates them to peers, but entered/exited events
|
||||||
|
// are sent only for the whole scroll pane.
|
||||||
|
// Exited event is only sent when mouse leaves scroll pane.
|
||||||
|
// If mouse enters/exits scroll bar, no entered/exited events are sent.
|
||||||
|
isAWTPeer = true;
|
||||||
|
|
||||||
|
// if dark theme is active, reinstall using light theme
|
||||||
|
if( FlatLaf.isLafDark() ) {
|
||||||
|
FlatUIUtils.runWithLightAWTPeerUIDefaults( () -> {
|
||||||
|
JScrollBar scrollbar = this.scrollbar;
|
||||||
|
uninstallUI( scrollbar );
|
||||||
|
installUI( scrollbar );
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -242,30 +290,27 @@ public class FlatScrollBarUI
|
|||||||
|
|
||||||
/** @since 2 */
|
/** @since 2 */
|
||||||
protected Object applyStyleProperty( String key, Object value ) {
|
protected Object applyStyleProperty( String key, Object value ) {
|
||||||
Object oldValue;
|
|
||||||
switch( key ) {
|
|
||||||
// BasicScrollBarUI
|
|
||||||
case "track": oldValue = trackColor; trackColor = (Color) value; return oldValue;
|
|
||||||
case "thumb": oldValue = thumbColor; thumbColor = (Color) value; return oldValue;
|
|
||||||
case "width": oldValue = scrollBarWidth; scrollBarWidth = (int) value; return oldValue;
|
|
||||||
case "minimumThumbSize": oldValue = minimumThumbSize; minimumThumbSize = (Dimension) value; return oldValue;
|
|
||||||
case "maximumThumbSize": oldValue = maximumThumbSize; maximumThumbSize = (Dimension) value; return oldValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
return FlatStylingSupport.applyToAnnotatedObjectOrComponent( this, scrollbar, key, value );
|
return FlatStylingSupport.applyToAnnotatedObjectOrComponent( this, scrollbar, key, value );
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @since 2 */
|
/** @since 2 */
|
||||||
@Override
|
@Override
|
||||||
public Map<String, Class<?>> getStyleableInfos( JComponent c ) {
|
public Map<String, Class<?>> getStyleableInfos( JComponent c ) {
|
||||||
Map<String, Class<?>> infos = new FlatStylingSupport.StyleableInfosMap<>();
|
return FlatStylingSupport.getAnnotatedStyleableInfos( this );
|
||||||
infos.put( "track", Color.class );
|
}
|
||||||
infos.put( "thumb", Color.class );
|
|
||||||
infos.put( "width", int.class );
|
/** @since 2.5 */
|
||||||
infos.put( "minimumThumbSize", Dimension.class );
|
@Override
|
||||||
infos.put( "maximumThumbSize", Dimension.class );
|
public Object getStyleableValue( JComponent c, String key ) {
|
||||||
FlatStylingSupport.collectAnnotatedStyleableInfos( this, infos );
|
return FlatStylingSupport.getAnnotatedStyleableValue( this, key );
|
||||||
return infos;
|
}
|
||||||
|
|
||||||
|
/** @since 2.5 */
|
||||||
|
@Override
|
||||||
|
public MethodHandles.Lookup getLookupForStyling() {
|
||||||
|
// MethodHandles.lookup() is caller sensitive and must be invoked in this class,
|
||||||
|
// otherwise it is not possible to access protected fields in JRE superclass
|
||||||
|
return MethodHandles.lookup();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -353,7 +398,7 @@ public class FlatScrollBarUI
|
|||||||
Color trackColor = FlatUIUtils.deriveColor( this.trackColor, c.getBackground() );
|
Color trackColor = FlatUIUtils.deriveColor( this.trackColor, c.getBackground() );
|
||||||
return (pressed && pressedTrackColor != null)
|
return (pressed && pressedTrackColor != null)
|
||||||
? FlatUIUtils.deriveColor( pressedTrackColor, trackColor )
|
? FlatUIUtils.deriveColor( pressedTrackColor, trackColor )
|
||||||
: ((hover && hoverTrackColor != null)
|
: ((hover && hoverTrackColor != null && !isAWTPeer)
|
||||||
? FlatUIUtils.deriveColor( hoverTrackColor, trackColor )
|
? FlatUIUtils.deriveColor( hoverTrackColor, trackColor )
|
||||||
: trackColor);
|
: trackColor);
|
||||||
}
|
}
|
||||||
@@ -363,7 +408,7 @@ public class FlatScrollBarUI
|
|||||||
Color thumbColor = FlatUIUtils.deriveColor( this.thumbColor, trackColor );
|
Color thumbColor = FlatUIUtils.deriveColor( this.thumbColor, trackColor );
|
||||||
return (pressed && pressedThumbColor != null)
|
return (pressed && pressedThumbColor != null)
|
||||||
? FlatUIUtils.deriveColor( pressedThumbColor, thumbColor )
|
? FlatUIUtils.deriveColor( pressedThumbColor, thumbColor )
|
||||||
: ((hover && hoverThumbColor != null)
|
: ((hover && hoverThumbColor != null && !isAWTPeer)
|
||||||
? FlatUIUtils.deriveColor( hoverThumbColor, thumbColor )
|
? FlatUIUtils.deriveColor( hoverThumbColor, thumbColor )
|
||||||
: thumbColor);
|
: thumbColor);
|
||||||
}
|
}
|
||||||
@@ -451,7 +496,6 @@ public class FlatScrollBarUI
|
|||||||
super( direction, type, foreground, disabledForeground,
|
super( direction, type, foreground, disabledForeground,
|
||||||
hoverForeground, hoverBackground, pressedForeground, pressedBackground );
|
hoverForeground, hoverBackground, pressedForeground, pressedBackground );
|
||||||
|
|
||||||
setArrowWidth( FlatArrowButton.DEFAULT_ARROW_WIDTH - 2 );
|
|
||||||
setFocusable( false );
|
setFocusable( false );
|
||||||
setRequestFocusEnabled( false );
|
setRequestFocusEnabled( false );
|
||||||
}
|
}
|
||||||
@@ -461,6 +505,18 @@ public class FlatScrollBarUI
|
|||||||
null, hoverButtonBackground, null, pressedButtonBackground );
|
null, hoverButtonBackground, null, pressedButtonBackground );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getArrowWidth() {
|
||||||
|
// scale arrow size depending on scroll bar width
|
||||||
|
// (6 is default arrow width; 10 is base scroll bar width)
|
||||||
|
int arrowWidth = Math.round( 6 * (scrollBarWidth / 10f) );
|
||||||
|
|
||||||
|
// compute arrow size that leaves equal space on both sides (arrow is centered)
|
||||||
|
arrowWidth = scrollBarWidth - (((scrollBarWidth - arrowWidth) / 2) * 2);
|
||||||
|
|
||||||
|
return arrowWidth;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Color deriveBackground( Color background ) {
|
protected Color deriveBackground( Color background ) {
|
||||||
return FlatUIUtils.deriveColor( background, scrollbar.getBackground() );
|
return FlatUIUtils.deriveColor( background, scrollbar.getBackground() );
|
||||||
@@ -469,8 +525,9 @@ public class FlatScrollBarUI
|
|||||||
@Override
|
@Override
|
||||||
public Dimension getPreferredSize() {
|
public Dimension getPreferredSize() {
|
||||||
if( isShowButtons() ) {
|
if( isShowButtons() ) {
|
||||||
int w = UIScale.scale( scrollBarWidth );
|
int w = UIScale.scale( Math.max( scrollBarWidth, (minimumButtonSize != null) ? minimumButtonSize.width : 0 ) );
|
||||||
return new Dimension( w, w );
|
int h = UIScale.scale( Math.max( scrollBarWidth, (minimumButtonSize != null) ? minimumButtonSize.height : 0 ) );
|
||||||
|
return new Dimension( w, h );
|
||||||
} else
|
} else
|
||||||
return new Dimension();
|
return new Dimension();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -87,6 +87,13 @@ public class FlatScrollPaneUI
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void installUI( JComponent c ) {
|
public void installUI( JComponent c ) {
|
||||||
|
if( FlatUIUtils.needsLightAWTPeer( c ) )
|
||||||
|
FlatUIUtils.runWithLightAWTPeerUIDefaults( () -> installUIImpl( c ) );
|
||||||
|
else
|
||||||
|
installUIImpl( c );
|
||||||
|
}
|
||||||
|
|
||||||
|
private void installUIImpl( JComponent c ) {
|
||||||
super.installUI( c );
|
super.installUI( c );
|
||||||
|
|
||||||
int focusWidth = UIManager.getInt( "Component.focusWidth" );
|
int focusWidth = UIManager.getInt( "Component.focusWidth" );
|
||||||
@@ -291,6 +298,10 @@ public class FlatScrollPaneUI
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case FlatClientProperties.OUTLINE:
|
||||||
|
scrollpane.repaint();
|
||||||
|
break;
|
||||||
|
|
||||||
case FlatClientProperties.STYLE:
|
case FlatClientProperties.STYLE:
|
||||||
case FlatClientProperties.STYLE_CLASS:
|
case FlatClientProperties.STYLE_CLASS:
|
||||||
installStyle();
|
installStyle();
|
||||||
@@ -339,6 +350,12 @@ public class FlatScrollPaneUI
|
|||||||
return FlatStylingSupport.getAnnotatedStyleableInfos( this, scrollpane.getBorder() );
|
return FlatStylingSupport.getAnnotatedStyleableInfos( this, scrollpane.getBorder() );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @since 2.5 */
|
||||||
|
@Override
|
||||||
|
public Object getStyleableValue( JComponent c, String key ) {
|
||||||
|
return FlatStylingSupport.getAnnotatedStyleableValue( this, scrollpane.getBorder(), key );
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void updateViewport( PropertyChangeEvent e ) {
|
protected void updateViewport( PropertyChangeEvent e ) {
|
||||||
super.updateViewport( e );
|
super.updateViewport( e );
|
||||||
|
|||||||
@@ -170,6 +170,12 @@ public class FlatSeparatorUI
|
|||||||
return FlatStylingSupport.getAnnotatedStyleableInfos( this );
|
return FlatStylingSupport.getAnnotatedStyleableInfos( this );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @since 2.5 */
|
||||||
|
@Override
|
||||||
|
public Object getStyleableValue( JComponent c, String key ) {
|
||||||
|
return FlatStylingSupport.getAnnotatedStyleableValue( this, key );
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void paint( Graphics g, JComponent c ) {
|
public void paint( Graphics g, JComponent c ) {
|
||||||
Graphics2D g2 = (Graphics2D) g.create();
|
Graphics2D g2 = (Graphics2D) g.create();
|
||||||
|
|||||||
@@ -222,6 +222,12 @@ public class FlatSliderUI
|
|||||||
return FlatStylingSupport.getAnnotatedStyleableInfos( this );
|
return FlatStylingSupport.getAnnotatedStyleableInfos( this );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @since 2.5 */
|
||||||
|
@Override
|
||||||
|
public Object getStyleableValue( JComponent c, String key ) {
|
||||||
|
return FlatStylingSupport.getAnnotatedStyleableValue( this, key );
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getBaseline( JComponent c, int width, int height ) {
|
public int getBaseline( JComponent c, int width, int height ) {
|
||||||
if( c == null )
|
if( c == null )
|
||||||
@@ -234,7 +240,7 @@ public class FlatSliderUI
|
|||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
// use default font (instead of slider font) because the slider font size
|
// use default font (instead of slider font) because the slider font size
|
||||||
// may be different to label font size, but we want align the track/thumb with labels
|
// may be different to label font size, but we want to align the track/thumb with labels
|
||||||
Font font = UIManager.getFont( "defaultFont" );
|
Font font = UIManager.getFont( "defaultFont" );
|
||||||
if( font == null )
|
if( font == null )
|
||||||
font = slider.getFont();
|
font = slider.getFont();
|
||||||
|
|||||||
@@ -223,6 +223,12 @@ public class FlatSpinnerUI
|
|||||||
return FlatStylingSupport.getAnnotatedStyleableInfos( this, spinner.getBorder() );
|
return FlatStylingSupport.getAnnotatedStyleableInfos( this, spinner.getBorder() );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @since 2.5 */
|
||||||
|
@Override
|
||||||
|
public Object getStyleableValue( JComponent c, String key ) {
|
||||||
|
return FlatStylingSupport.getAnnotatedStyleableValue( this, spinner.getBorder(), key );
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected JComponent createEditor() {
|
protected JComponent createEditor() {
|
||||||
JComponent editor = super.createEditor();
|
JComponent editor = super.createEditor();
|
||||||
@@ -293,9 +299,7 @@ public class FlatSpinnerUI
|
|||||||
return true;
|
return true;
|
||||||
|
|
||||||
JTextField textField = getEditorTextField( spinner.getEditor() );
|
JTextField textField = getEditorTextField( spinner.getEditor() );
|
||||||
return (textField != null)
|
return textField != null && FlatUIUtils.isPermanentFocusOwner( textField );
|
||||||
? FlatUIUtils.isPermanentFocusOwner( textField )
|
|
||||||
: false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Color getBackground( boolean enabled ) {
|
protected Color getBackground( boolean enabled ) {
|
||||||
@@ -447,7 +451,7 @@ public class FlatSpinnerUI
|
|||||||
Insets padding = scale( FlatSpinnerUI.this.padding );
|
Insets padding = scale( FlatSpinnerUI.this.padding );
|
||||||
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 arrow buttons width is the same as the inner height so that the arrow buttons area is square
|
||||||
int minimumWidth = FlatUIUtils.minimumWidth( spinner, FlatSpinnerUI.this.minimumWidth );
|
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 );
|
float focusWidth = FlatUIUtils.getBorderFocusWidth( spinner );
|
||||||
@@ -538,6 +542,7 @@ public class FlatSpinnerUI
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case FlatClientProperties.COMPONENT_ROUND_RECT:
|
case FlatClientProperties.COMPONENT_ROUND_RECT:
|
||||||
|
case FlatClientProperties.OUTLINE:
|
||||||
spinner.repaint();
|
spinner.repaint();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ import javax.swing.UIManager;
|
|||||||
import javax.swing.plaf.ComponentUI;
|
import javax.swing.plaf.ComponentUI;
|
||||||
import javax.swing.plaf.basic.BasicSplitPaneDivider;
|
import javax.swing.plaf.basic.BasicSplitPaneDivider;
|
||||||
import javax.swing.plaf.basic.BasicSplitPaneUI;
|
import javax.swing.plaf.basic.BasicSplitPaneUI;
|
||||||
|
import com.formdev.flatlaf.FlatClientProperties;
|
||||||
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
|
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
|
||||||
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
|
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
|
||||||
import com.formdev.flatlaf.ui.FlatStylingSupport.UnknownStyleException;
|
import com.formdev.flatlaf.ui.FlatStylingSupport.UnknownStyleException;
|
||||||
@@ -52,6 +53,13 @@ import com.formdev.flatlaf.util.UIScale;
|
|||||||
* @uiDefault SplitPaneDivider.border Border
|
* @uiDefault SplitPaneDivider.border Border
|
||||||
* @uiDefault SplitPaneDivider.draggingColor Color only used if continuousLayout is false
|
* @uiDefault SplitPaneDivider.draggingColor Color only used if continuousLayout is false
|
||||||
*
|
*
|
||||||
|
* <!-- BasicSplitPaneDivider -->
|
||||||
|
*
|
||||||
|
* @uiDefault SplitPane.oneTouchButtonSize int
|
||||||
|
* @uiDefault SplitPane.oneTouchButtonOffset int
|
||||||
|
* @uiDefault SplitPane.centerOneTouchButtons boolean
|
||||||
|
* @uiDefault SplitPane.supportsOneTouchButtons boolean optional; default is true
|
||||||
|
*
|
||||||
* <!-- JSplitPane -->
|
* <!-- JSplitPane -->
|
||||||
*
|
*
|
||||||
* @uiDefault SplitPane.continuousLayout boolean
|
* @uiDefault SplitPane.continuousLayout boolean
|
||||||
@@ -175,6 +183,17 @@ public class FlatSplitPaneUI
|
|||||||
return infos;
|
return infos;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @since 2.5 */
|
||||||
|
@Override
|
||||||
|
public Object getStyleableValue( JComponent c, String key ) {
|
||||||
|
if( divider instanceof FlatSplitPaneDivider ) {
|
||||||
|
Object value = ((FlatSplitPaneDivider)divider).getStyleableValue( key );
|
||||||
|
if( value != null )
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
return FlatStylingSupport.getAnnotatedStyleableValue( this, key );
|
||||||
|
}
|
||||||
|
|
||||||
//---- class FlatSplitPaneDivider -----------------------------------------
|
//---- class FlatSplitPaneDivider -----------------------------------------
|
||||||
|
|
||||||
protected class FlatSplitPaneDivider
|
protected class FlatSplitPaneDivider
|
||||||
@@ -192,20 +211,21 @@ public class FlatSplitPaneUI
|
|||||||
setLayout( new FlatDividerLayout() );
|
setLayout( new FlatDividerLayout() );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** @since 2 */
|
||||||
* @since 2
|
|
||||||
*/
|
|
||||||
protected Object applyStyleProperty( String key, Object value ) {
|
protected Object applyStyleProperty( String key, Object value ) {
|
||||||
return FlatStylingSupport.applyToAnnotatedObject( this, key, value );
|
return FlatStylingSupport.applyToAnnotatedObject( this, key, value );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** @since 2 */
|
||||||
* @since 2
|
|
||||||
*/
|
|
||||||
public Map<String, Class<?>> getStyleableInfos() {
|
public Map<String, Class<?>> getStyleableInfos() {
|
||||||
return FlatStylingSupport.getAnnotatedStyleableInfos( this );
|
return FlatStylingSupport.getAnnotatedStyleableInfos( this );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @since 2.5 */
|
||||||
|
public Object getStyleableValue( String key ) {
|
||||||
|
return FlatStylingSupport.getAnnotatedStyleableValue( this, key );
|
||||||
|
}
|
||||||
|
|
||||||
void updateStyle() {
|
void updateStyle() {
|
||||||
if( leftButton instanceof FlatOneTouchButton )
|
if( leftButton instanceof FlatOneTouchButton )
|
||||||
((FlatOneTouchButton)leftButton).updateStyle();
|
((FlatOneTouchButton)leftButton).updateStyle();
|
||||||
@@ -235,7 +255,7 @@ public class FlatSplitPaneUI
|
|||||||
switch( e.getPropertyName() ) {
|
switch( e.getPropertyName() ) {
|
||||||
case JSplitPane.DIVIDER_LOCATION_PROPERTY:
|
case JSplitPane.DIVIDER_LOCATION_PROPERTY:
|
||||||
// necessary to show/hide one-touch buttons on expand/collapse
|
// necessary to show/hide one-touch buttons on expand/collapse
|
||||||
revalidate();
|
doLayout();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -345,7 +365,7 @@ public class FlatSplitPaneUI
|
|||||||
if( leftButton == null || rightButton == null || !splitPane.isOneTouchExpandable() )
|
if( leftButton == null || rightButton == null || !splitPane.isOneTouchExpandable() )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// increase side of buttons, which makes them easier to hit by the user
|
// increase size of buttons, which makes them easier to hit by the user
|
||||||
// and avoids cut arrows at small divider sizes
|
// and avoids cut arrows at small divider sizes
|
||||||
int extraSize = UIScale.scale( 4 );
|
int extraSize = UIScale.scale( 4 );
|
||||||
if( orientation == JSplitPane.VERTICAL_SPLIT ) {
|
if( orientation == JSplitPane.VERTICAL_SPLIT ) {
|
||||||
@@ -360,10 +380,19 @@ public class FlatSplitPaneUI
|
|||||||
|
|
||||||
// hide buttons if not applicable
|
// hide buttons if not applicable
|
||||||
boolean leftCollapsed = isLeftCollapsed();
|
boolean leftCollapsed = isLeftCollapsed();
|
||||||
if( leftCollapsed )
|
boolean rightCollapsed = isRightCollapsed();
|
||||||
|
if( leftCollapsed || rightCollapsed ) {
|
||||||
|
leftButton.setVisible( !leftCollapsed );
|
||||||
|
rightButton.setVisible( !rightCollapsed );
|
||||||
|
} else {
|
||||||
|
Object expandableSide = splitPane.getClientProperty( FlatClientProperties.SPLIT_PANE_EXPANDABLE_SIDE );
|
||||||
|
leftButton.setVisible( expandableSide == null || !FlatClientProperties.SPLIT_PANE_EXPANDABLE_SIDE_LEFT.equals( expandableSide ) );
|
||||||
|
rightButton.setVisible( expandableSide == null || !FlatClientProperties.SPLIT_PANE_EXPANDABLE_SIDE_RIGHT.equals( expandableSide ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
// move right button if left button is hidden
|
||||||
|
if( !leftButton.isVisible() )
|
||||||
rightButton.setLocation( leftButton.getLocation() );
|
rightButton.setLocation( leftButton.getLocation() );
|
||||||
leftButton.setVisible( !leftCollapsed );
|
|
||||||
rightButton.setVisible( !isRightCollapsed() );
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,9 +18,11 @@ package com.formdev.flatlaf.ui;
|
|||||||
|
|
||||||
import java.beans.PropertyChangeListener;
|
import java.beans.PropertyChangeListener;
|
||||||
import java.lang.annotation.ElementType;
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Repeatable;
|
||||||
import java.lang.annotation.Retention;
|
import java.lang.annotation.Retention;
|
||||||
import java.lang.annotation.RetentionPolicy;
|
import java.lang.annotation.RetentionPolicy;
|
||||||
import java.lang.annotation.Target;
|
import java.lang.annotation.Target;
|
||||||
|
import java.lang.invoke.MethodHandles;
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.lang.reflect.Modifier;
|
import java.lang.reflect.Modifier;
|
||||||
@@ -63,15 +65,55 @@ public class FlatStylingSupport
|
|||||||
Class<?> type() default Void.class;
|
Class<?> type() default Void.class;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates that a field in the specified (super) class
|
||||||
|
* is intended to be used by FlatLaf styling support.
|
||||||
|
* <p>
|
||||||
|
* Use this annotation, instead of {@link Styleable}, to style fields
|
||||||
|
* in superclasses, where it is not possible to use {@link Styleable}.
|
||||||
|
* <p>
|
||||||
|
* Classes using this annotation may implement {@link StyleableLookupProvider}
|
||||||
|
* to give access to protected fields (in JRE) in modular applications.
|
||||||
|
*
|
||||||
|
* @since 2.5
|
||||||
|
*/
|
||||||
|
@Target(ElementType.TYPE)
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@Repeatable(StyleableFields.class)
|
||||||
|
public @interface StyleableField {
|
||||||
|
Class<?> cls();
|
||||||
|
String key();
|
||||||
|
String fieldName() default "";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Container annotation for {@link StyleableField}.
|
||||||
|
*
|
||||||
|
* @since 2.5
|
||||||
|
*/
|
||||||
|
@Target(ElementType.TYPE)
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
public @interface StyleableFields {
|
||||||
|
StyleableField[] value();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/** @since 2 */
|
/** @since 2 */
|
||||||
public interface StyleableUI {
|
public interface StyleableUI {
|
||||||
Map<String, Class<?>> getStyleableInfos( JComponent c );
|
Map<String, Class<?>> getStyleableInfos( JComponent c );
|
||||||
|
/** @since 2.5 */ Object getStyleableValue( JComponent c, String key );
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @since 2 */
|
/** @since 2 */
|
||||||
public interface StyleableBorder {
|
public interface StyleableBorder {
|
||||||
Object applyStyleProperty( String key, Object value );
|
Object applyStyleProperty( String key, Object value );
|
||||||
Map<String, Class<?>> getStyleableInfos();
|
Map<String, Class<?>> getStyleableInfos();
|
||||||
|
/** @since 2.5 */ Object getStyleableValue( String key );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 2.5 */
|
||||||
|
public interface StyleableLookupProvider {
|
||||||
|
MethodHandles.Lookup getLookupForStyling();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -359,27 +401,31 @@ public class FlatStylingSupport
|
|||||||
* @param key the name of the field
|
* @param key the name of the field
|
||||||
* @param value the new value
|
* @param value the new value
|
||||||
* @return the old value of the field
|
* @return the old value of the field
|
||||||
* @throws UnknownStyleException if object does not have a annotated field with given name
|
* @throws UnknownStyleException if object does not have an annotated field with given name
|
||||||
* @throws IllegalArgumentException if value type does not fit to expected type
|
* @throws IllegalArgumentException if value type does not fit to expected type
|
||||||
*/
|
*/
|
||||||
public static Object applyToAnnotatedObject( Object obj, String key, Object value )
|
public static Object applyToAnnotatedObject( Object obj, String key, Object value )
|
||||||
throws UnknownStyleException, IllegalArgumentException
|
throws UnknownStyleException, IllegalArgumentException
|
||||||
{
|
{
|
||||||
String fieldName = key;
|
String fieldName = keyToFieldName( key );
|
||||||
int dotIndex = key.indexOf( '.' );
|
|
||||||
if( dotIndex >= 0 ) {
|
|
||||||
// remove first dot in key and change subsequent character to uppercase
|
|
||||||
fieldName = key.substring( 0, dotIndex )
|
|
||||||
+ Character.toUpperCase( key.charAt( dotIndex + 1 ) )
|
|
||||||
+ key.substring( dotIndex + 2 );
|
|
||||||
}
|
|
||||||
|
|
||||||
return applyToField( obj, fieldName, key, value, field -> {
|
return applyToField( obj, fieldName, key, value, field -> {
|
||||||
Styleable styleable = field.getAnnotation( Styleable.class );
|
Styleable styleable = field.getAnnotation( Styleable.class );
|
||||||
return styleable != null && styleable.dot() == (dotIndex >= 0);
|
return styleable != null && styleable.dot() == (fieldName != key);
|
||||||
} );
|
} );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static String keyToFieldName( String key ) {
|
||||||
|
int dotIndex = key.indexOf( '.' );
|
||||||
|
if( dotIndex < 0 )
|
||||||
|
return key;
|
||||||
|
|
||||||
|
// remove first dot in key and change subsequent character to uppercase
|
||||||
|
return key.substring( 0, dotIndex )
|
||||||
|
+ Character.toUpperCase( key.charAt( dotIndex + 1 ) )
|
||||||
|
+ key.substring( dotIndex + 2 );
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Applies the given value to a field of the given object.
|
* Applies the given value to a field of the given object.
|
||||||
*
|
*
|
||||||
@@ -405,26 +451,17 @@ public class FlatStylingSupport
|
|||||||
for(;;) {
|
for(;;) {
|
||||||
try {
|
try {
|
||||||
Field f = cls.getDeclaredField( fieldName );
|
Field f = cls.getDeclaredField( fieldName );
|
||||||
if( predicate == null || predicate.test( f ) ) {
|
if( predicate == null || predicate.test( f ) )
|
||||||
if( !isValidField( f ) )
|
return applyToField( f, obj, value, false );
|
||||||
throw new IllegalArgumentException( "field '" + cls.getName() + "." + fieldName + "' is final or static" );
|
|
||||||
|
|
||||||
try {
|
|
||||||
// necessary to access protected fields in other packages
|
|
||||||
f.setAccessible( true );
|
|
||||||
|
|
||||||
// get old value and set new value
|
|
||||||
Object oldValue = f.get( obj );
|
|
||||||
f.set( obj, convertToEnum( value, f.getType() ) );
|
|
||||||
return oldValue;
|
|
||||||
} catch( IllegalAccessException ex ) {
|
|
||||||
throw new IllegalArgumentException( "failed to access field '" + cls.getName() + "." + fieldName + "'", ex );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch( NoSuchFieldException ex ) {
|
} catch( NoSuchFieldException ex ) {
|
||||||
// field not found in class --> try superclass
|
// field not found in class --> try superclass
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for( StyleableField styleableField : cls.getAnnotationsByType( StyleableField.class ) ) {
|
||||||
|
if( key.equals( styleableField.key() ) )
|
||||||
|
return applyToField( getStyleableField( styleableField ), obj, value, true );
|
||||||
|
}
|
||||||
|
|
||||||
cls = cls.getSuperclass();
|
cls = cls.getSuperclass();
|
||||||
if( cls == null )
|
if( cls == null )
|
||||||
throw new UnknownStyleException( key );
|
throw new UnknownStyleException( key );
|
||||||
@@ -437,11 +474,83 @@ public class FlatStylingSupport
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static Object applyToField( Field f, Object obj, Object value, boolean useMethodHandles ) {
|
||||||
|
checkValidField( f );
|
||||||
|
|
||||||
|
if( useMethodHandles && obj instanceof StyleableLookupProvider ) {
|
||||||
|
try {
|
||||||
|
// use method handles to access protected fields in JRE in modular applications
|
||||||
|
MethodHandles.Lookup lookup = ((StyleableLookupProvider)obj).getLookupForStyling();
|
||||||
|
|
||||||
|
// get old value and set new value
|
||||||
|
Object oldValue = lookup.unreflectGetter( f ).invoke( obj );
|
||||||
|
lookup.unreflectSetter( f ).invoke( obj, convertToEnum( value, f.getType() ) );
|
||||||
|
return oldValue;
|
||||||
|
} catch( Throwable ex ) {
|
||||||
|
throw newFieldAccessFailed( f, ex );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// necessary to access protected fields in other packages
|
||||||
|
f.setAccessible( true );
|
||||||
|
|
||||||
|
// get old value and set new value
|
||||||
|
Object oldValue = f.get( obj );
|
||||||
|
f.set( obj, convertToEnum( value, f.getType() ) );
|
||||||
|
return oldValue;
|
||||||
|
} catch( IllegalAccessException ex ) {
|
||||||
|
throw newFieldAccessFailed( f, ex );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Object getFieldValue( Field f, Object obj, boolean useMethodHandles ) {
|
||||||
|
checkValidField( f );
|
||||||
|
|
||||||
|
if( useMethodHandles && obj instanceof StyleableLookupProvider ) {
|
||||||
|
// use method handles to access protected fields in JRE in modular applications
|
||||||
|
try {
|
||||||
|
MethodHandles.Lookup lookup = ((StyleableLookupProvider)obj).getLookupForStyling();
|
||||||
|
return lookup.unreflectGetter( f ).invoke( obj );
|
||||||
|
} catch( Throwable ex ) {
|
||||||
|
throw newFieldAccessFailed( f, ex );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
f.setAccessible( true );
|
||||||
|
return f.get( obj );
|
||||||
|
} catch( IllegalAccessException ex ) {
|
||||||
|
throw newFieldAccessFailed( f, ex );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static IllegalArgumentException newFieldAccessFailed( Field f, Throwable ex ) {
|
||||||
|
return new IllegalArgumentException( "failed to access field '" + f.getDeclaringClass().getName() + "." + f.getName() + "'", ex );
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void checkValidField( Field f ) {
|
||||||
|
if( !isValidField( f ) )
|
||||||
|
throw new IllegalArgumentException( "field '" + f.getDeclaringClass().getName() + "." + f.getName() + "' is final or static" );
|
||||||
|
}
|
||||||
|
|
||||||
private static boolean isValidField( Field f ) {
|
private static boolean isValidField( Field f ) {
|
||||||
int modifiers = f.getModifiers();
|
int modifiers = f.getModifiers();
|
||||||
return (modifiers & (Modifier.FINAL|Modifier.STATIC)) == 0 && !f.isSynthetic();
|
return (modifiers & (Modifier.FINAL|Modifier.STATIC)) == 0 && !f.isSynthetic();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static Field getStyleableField( StyleableField styleableField ) {
|
||||||
|
String fieldName = styleableField.fieldName();
|
||||||
|
if( fieldName.isEmpty() )
|
||||||
|
fieldName = styleableField.key();
|
||||||
|
|
||||||
|
try {
|
||||||
|
return styleableField.cls().getDeclaredField( fieldName );
|
||||||
|
} catch( NoSuchFieldException ex ) {
|
||||||
|
throw new IllegalArgumentException( "field '" + styleableField.cls().getName() + "." + fieldName + "' not found", ex );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Applies the given value to a property of the given object.
|
* Applies the given value to a property of the given object.
|
||||||
* Works only for properties that have public getter and setter methods.
|
* Works only for properties that have public getter and setter methods.
|
||||||
@@ -517,7 +626,7 @@ public class FlatStylingSupport
|
|||||||
* @param key the name of the field
|
* @param key the name of the field
|
||||||
* @param value the new value
|
* @param value the new value
|
||||||
* @return the old value of the field
|
* @return the old value of the field
|
||||||
* @throws UnknownStyleException if object does not have a annotated field with given name
|
* @throws UnknownStyleException if object does not have an annotated field with given name
|
||||||
* @throws IllegalArgumentException if value type does not fit to expected type
|
* @throws IllegalArgumentException if value type does not fit to expected type
|
||||||
*/
|
*/
|
||||||
public static Object applyToAnnotatedObjectOrComponent( Object obj, Object comp, String key, Object value )
|
public static Object applyToAnnotatedObjectOrComponent( Object obj, Object comp, String key, Object value )
|
||||||
@@ -628,6 +737,7 @@ public class FlatStylingSupport
|
|||||||
Class<?> cls = obj.getClass();
|
Class<?> cls = obj.getClass();
|
||||||
|
|
||||||
for(;;) {
|
for(;;) {
|
||||||
|
// find fields annotated with 'Styleable'
|
||||||
for( Field f : cls.getDeclaredFields() ) {
|
for( Field f : cls.getDeclaredFields() ) {
|
||||||
if( !isValidField( f ) )
|
if( !isValidField( f ) )
|
||||||
continue;
|
continue;
|
||||||
@@ -666,6 +776,20 @@ public class FlatStylingSupport
|
|||||||
infos.put( name, type );
|
infos.put( name, type );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// get fields specified in 'StyleableField' annotation
|
||||||
|
for( StyleableField styleableField : cls.getAnnotationsByType( StyleableField.class ) ) {
|
||||||
|
String name = styleableField.key();
|
||||||
|
|
||||||
|
// for the case that the same field name is used in a class and in
|
||||||
|
// one of its superclasses, do not process field in superclass
|
||||||
|
if( processedFields.contains( name ) )
|
||||||
|
continue;
|
||||||
|
processedFields.add( name );
|
||||||
|
|
||||||
|
Field f = getStyleableField( styleableField );
|
||||||
|
infos.put( name, f.getType() );
|
||||||
|
}
|
||||||
|
|
||||||
cls = cls.getSuperclass();
|
cls = cls.getSuperclass();
|
||||||
if( cls == null )
|
if( cls == null )
|
||||||
return;
|
return;
|
||||||
@@ -686,6 +810,52 @@ public class FlatStylingSupport
|
|||||||
infos.put( keyPrefix.concat( e.getKey() ), e.getValue() );
|
infos.put( keyPrefix.concat( e.getKey() ), e.getValue() );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Object getAnnotatedStyleableValue( Object obj, String key ) {
|
||||||
|
String fieldName = keyToFieldName( key );
|
||||||
|
Class<?> cls = obj.getClass();
|
||||||
|
|
||||||
|
for(;;) {
|
||||||
|
try {
|
||||||
|
// find field annotated with 'Styleable'
|
||||||
|
Field f = cls.getDeclaredField( fieldName );
|
||||||
|
Styleable styleable = f.getAnnotation( Styleable.class );
|
||||||
|
if( styleable != null ) {
|
||||||
|
if( styleable.dot() != (fieldName != key) )
|
||||||
|
throw new IllegalArgumentException( "'Styleable.dot' on field '" + fieldName + "' does not match key '" + key + "'" );
|
||||||
|
if( styleable.type() != Void.class )
|
||||||
|
throw new IllegalArgumentException( "'Styleable.type' on field '" + fieldName + "' not supported" );
|
||||||
|
|
||||||
|
return getFieldValue( f, obj, false );
|
||||||
|
}
|
||||||
|
} catch( NoSuchFieldException ex ) {
|
||||||
|
// field not found in class --> try superclass
|
||||||
|
}
|
||||||
|
|
||||||
|
// find field specified in 'StyleableField' annotation
|
||||||
|
for( StyleableField styleableField : cls.getAnnotationsByType( StyleableField.class ) ) {
|
||||||
|
if( key.equals( styleableField.key() ) )
|
||||||
|
return getFieldValue( getStyleableField( styleableField ), obj, true );
|
||||||
|
}
|
||||||
|
|
||||||
|
cls = cls.getSuperclass();
|
||||||
|
if( cls == null )
|
||||||
|
return null;
|
||||||
|
|
||||||
|
String superclassName = cls.getName();
|
||||||
|
if( superclassName.startsWith( "java." ) || superclassName.startsWith( "javax." ) )
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Object getAnnotatedStyleableValue( Object obj, Border border, String key ) {
|
||||||
|
if( border instanceof StyleableBorder ) {
|
||||||
|
Object value = ((StyleableBorder)border).getStyleableValue( key );
|
||||||
|
if( value != null )
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
return getAnnotatedStyleableValue( obj, key );
|
||||||
|
}
|
||||||
|
|
||||||
//---- class UnknownStyleException ----------------------------------------
|
//---- class UnknownStyleException ----------------------------------------
|
||||||
|
|
||||||
public static class UnknownStyleException
|
public static class UnknownStyleException
|
||||||
|
|||||||
@@ -59,6 +59,7 @@ import java.util.Map;
|
|||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.function.BiConsumer;
|
import java.util.function.BiConsumer;
|
||||||
import java.util.function.IntConsumer;
|
import java.util.function.IntConsumer;
|
||||||
|
import java.util.function.Predicate;
|
||||||
import javax.accessibility.Accessible;
|
import javax.accessibility.Accessible;
|
||||||
import javax.accessibility.AccessibleContext;
|
import javax.accessibility.AccessibleContext;
|
||||||
import javax.swing.Action;
|
import javax.swing.Action;
|
||||||
@@ -82,10 +83,12 @@ import javax.swing.event.ChangeListener;
|
|||||||
import javax.swing.event.PopupMenuEvent;
|
import javax.swing.event.PopupMenuEvent;
|
||||||
import javax.swing.event.PopupMenuListener;
|
import javax.swing.event.PopupMenuListener;
|
||||||
import javax.swing.plaf.ComponentUI;
|
import javax.swing.plaf.ComponentUI;
|
||||||
|
import javax.swing.plaf.TabbedPaneUI;
|
||||||
import javax.swing.plaf.UIResource;
|
import javax.swing.plaf.UIResource;
|
||||||
import javax.swing.plaf.basic.BasicTabbedPaneUI;
|
import javax.swing.plaf.basic.BasicTabbedPaneUI;
|
||||||
import javax.swing.text.JTextComponent;
|
import javax.swing.text.JTextComponent;
|
||||||
import javax.swing.text.View;
|
import javax.swing.text.View;
|
||||||
|
import com.formdev.flatlaf.FlatClientProperties;
|
||||||
import com.formdev.flatlaf.FlatLaf;
|
import com.formdev.flatlaf.FlatLaf;
|
||||||
import com.formdev.flatlaf.icons.FlatTabbedPaneCloseIcon;
|
import com.formdev.flatlaf.icons.FlatTabbedPaneCloseIcon;
|
||||||
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
|
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
|
||||||
@@ -127,6 +130,7 @@ import com.formdev.flatlaf.util.UIScale;
|
|||||||
* @uiDefault TabbedPane.selectedBackground Color optional
|
* @uiDefault TabbedPane.selectedBackground Color optional
|
||||||
* @uiDefault TabbedPane.selectedForeground Color
|
* @uiDefault TabbedPane.selectedForeground Color
|
||||||
* @uiDefault TabbedPane.underlineColor Color
|
* @uiDefault TabbedPane.underlineColor Color
|
||||||
|
* @uiDefault TabbedPane.inactiveUnderlineColor Color
|
||||||
* @uiDefault TabbedPane.disabledUnderlineColor Color
|
* @uiDefault TabbedPane.disabledUnderlineColor Color
|
||||||
* @uiDefault TabbedPane.hoverColor Color
|
* @uiDefault TabbedPane.hoverColor Color
|
||||||
* @uiDefault TabbedPane.focusColor Color
|
* @uiDefault TabbedPane.focusColor Color
|
||||||
@@ -141,7 +145,7 @@ import com.formdev.flatlaf.util.UIScale;
|
|||||||
* @uiDefault TabbedPane.showTabSeparators boolean
|
* @uiDefault TabbedPane.showTabSeparators boolean
|
||||||
* @uiDefault TabbedPane.tabSeparatorsFullHeight boolean
|
* @uiDefault TabbedPane.tabSeparatorsFullHeight boolean
|
||||||
* @uiDefault TabbedPane.hasFullBorder boolean
|
* @uiDefault TabbedPane.hasFullBorder boolean
|
||||||
* @uiDefault TabbedPane.activeTabBorder boolean
|
* @uiDefault TabbedPane.rotateTabRuns boolean
|
||||||
*
|
*
|
||||||
* @uiDefault TabbedPane.tabLayoutPolicy String wrap (default) or scroll
|
* @uiDefault TabbedPane.tabLayoutPolicy String wrap (default) or scroll
|
||||||
* @uiDefault TabbedPane.tabType String underlined (default) or card
|
* @uiDefault TabbedPane.tabType String underlined (default) or card
|
||||||
@@ -198,6 +202,7 @@ public class FlatTabbedPaneUI
|
|||||||
@Styleable protected Color selectedBackground;
|
@Styleable protected Color selectedBackground;
|
||||||
@Styleable protected Color selectedForeground;
|
@Styleable protected Color selectedForeground;
|
||||||
@Styleable protected Color underlineColor;
|
@Styleable protected Color underlineColor;
|
||||||
|
/** @since 2.2 */ @Styleable protected Color inactiveUnderlineColor;
|
||||||
@Styleable protected Color disabledUnderlineColor;
|
@Styleable protected Color disabledUnderlineColor;
|
||||||
@Styleable protected Color hoverColor;
|
@Styleable protected Color hoverColor;
|
||||||
@Styleable protected Color focusColor;
|
@Styleable protected Color focusColor;
|
||||||
@@ -215,6 +220,7 @@ public class FlatTabbedPaneUI
|
|||||||
@Styleable protected boolean tabSeparatorsFullHeight;
|
@Styleable protected boolean tabSeparatorsFullHeight;
|
||||||
@Styleable protected boolean hasFullBorder;
|
@Styleable protected boolean hasFullBorder;
|
||||||
@Styleable protected boolean tabsOpaque = true;
|
@Styleable protected boolean tabsOpaque = true;
|
||||||
|
/** @since 2.5 */ @Styleable protected boolean rotateTabRuns = true;
|
||||||
|
|
||||||
@Styleable(type=String.class) private int tabType;
|
@Styleable(type=String.class) private int tabType;
|
||||||
@Styleable(type=String.class) private int tabsPopupPolicy;
|
@Styleable(type=String.class) private int tabsPopupPolicy;
|
||||||
@@ -288,6 +294,7 @@ public class FlatTabbedPaneUI
|
|||||||
|
|
||||||
super.installUI( c );
|
super.installUI( c );
|
||||||
|
|
||||||
|
FlatSelectedTabRepainter.install();
|
||||||
installStyle();
|
installStyle();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -318,6 +325,7 @@ public class FlatTabbedPaneUI
|
|||||||
selectedBackground = UIManager.getColor( "TabbedPane.selectedBackground" );
|
selectedBackground = UIManager.getColor( "TabbedPane.selectedBackground" );
|
||||||
selectedForeground = UIManager.getColor( "TabbedPane.selectedForeground" );
|
selectedForeground = UIManager.getColor( "TabbedPane.selectedForeground" );
|
||||||
underlineColor = UIManager.getColor( "TabbedPane.underlineColor" );
|
underlineColor = UIManager.getColor( "TabbedPane.underlineColor" );
|
||||||
|
inactiveUnderlineColor = FlatUIUtils.getUIColor( "TabbedPane.inactiveUnderlineColor", underlineColor );
|
||||||
disabledUnderlineColor = UIManager.getColor( "TabbedPane.disabledUnderlineColor" );
|
disabledUnderlineColor = UIManager.getColor( "TabbedPane.disabledUnderlineColor" );
|
||||||
hoverColor = UIManager.getColor( "TabbedPane.hoverColor" );
|
hoverColor = UIManager.getColor( "TabbedPane.hoverColor" );
|
||||||
focusColor = UIManager.getColor( "TabbedPane.focusColor" );
|
focusColor = UIManager.getColor( "TabbedPane.focusColor" );
|
||||||
@@ -335,6 +343,7 @@ public class FlatTabbedPaneUI
|
|||||||
tabSeparatorsFullHeight = UIManager.getBoolean( "TabbedPane.tabSeparatorsFullHeight" );
|
tabSeparatorsFullHeight = UIManager.getBoolean( "TabbedPane.tabSeparatorsFullHeight" );
|
||||||
hasFullBorder = UIManager.getBoolean( "TabbedPane.hasFullBorder" );
|
hasFullBorder = UIManager.getBoolean( "TabbedPane.hasFullBorder" );
|
||||||
tabsOpaque = UIManager.getBoolean( "TabbedPane.tabsOpaque" );
|
tabsOpaque = UIManager.getBoolean( "TabbedPane.tabsOpaque" );
|
||||||
|
rotateTabRuns = FlatUIUtils.getUIBoolean( "TabbedPane.rotateTabRuns", true );
|
||||||
|
|
||||||
tabType = parseTabType( UIManager.getString( "TabbedPane.tabType" ) );
|
tabType = parseTabType( UIManager.getString( "TabbedPane.tabType" ) );
|
||||||
tabsPopupPolicy = parseTabsPopupPolicy( UIManager.getString( "TabbedPane.tabsPopupPolicy" ) );
|
tabsPopupPolicy = parseTabsPopupPolicy( UIManager.getString( "TabbedPane.tabsPopupPolicy" ) );
|
||||||
@@ -385,6 +394,7 @@ public class FlatTabbedPaneUI
|
|||||||
selectedBackground = null;
|
selectedBackground = null;
|
||||||
selectedForeground = null;
|
selectedForeground = null;
|
||||||
underlineColor = null;
|
underlineColor = null;
|
||||||
|
inactiveUnderlineColor = null;
|
||||||
disabledUnderlineColor = null;
|
disabledUnderlineColor = null;
|
||||||
hoverColor = null;
|
hoverColor = null;
|
||||||
focusColor = null;
|
focusColor = null;
|
||||||
@@ -650,7 +660,7 @@ public class FlatTabbedPaneUI
|
|||||||
case "tabIconPlacement": value = parseTabIconPlacement( (String) value ); break;
|
case "tabIconPlacement": value = parseTabIconPlacement( (String) value ); break;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Object oldValue = null;
|
Object oldValue;
|
||||||
switch( key ) {
|
switch( key ) {
|
||||||
// BasicTabbedPaneUI
|
// BasicTabbedPaneUI
|
||||||
case "tabInsets": oldValue = tabInsets; tabInsets = (Insets) value; return oldValue;
|
case "tabInsets": oldValue = tabInsets; tabInsets = (Insets) value; return oldValue;
|
||||||
@@ -679,6 +689,76 @@ public class FlatTabbedPaneUI
|
|||||||
return infos;
|
return infos;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @since 2.5 */
|
||||||
|
@Override
|
||||||
|
public Object getStyleableValue( JComponent c, String key ) {
|
||||||
|
// close icon
|
||||||
|
if( key.startsWith( "close" ) ) {
|
||||||
|
return (closeIcon instanceof FlatTabbedPaneCloseIcon)
|
||||||
|
? ((FlatTabbedPaneCloseIcon)closeIcon).getStyleableValue( key )
|
||||||
|
: null;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch( key ) {
|
||||||
|
// BasicTabbedPaneUI
|
||||||
|
case "tabInsets": return tabInsets;
|
||||||
|
case "tabAreaInsets": return tabAreaInsets;
|
||||||
|
case "textIconGap": return textIconGapUnscaled;
|
||||||
|
|
||||||
|
// FlatTabbedPaneUI
|
||||||
|
case "tabType":
|
||||||
|
switch( tabType ) {
|
||||||
|
default:
|
||||||
|
case TAB_TYPE_UNDERLINED: return TABBED_PANE_TAB_TYPE_UNDERLINED;
|
||||||
|
case TAB_TYPE_CARD: return TABBED_PANE_TAB_TYPE_CARD;
|
||||||
|
}
|
||||||
|
|
||||||
|
case "tabsPopupPolicy":
|
||||||
|
switch( tabsPopupPolicy ) {
|
||||||
|
default:
|
||||||
|
case AS_NEEDED: return TABBED_PANE_POLICY_AS_NEEDED;
|
||||||
|
case NEVER: return TABBED_PANE_POLICY_NEVER;
|
||||||
|
}
|
||||||
|
|
||||||
|
case "scrollButtonsPolicy":
|
||||||
|
switch( scrollButtonsPolicy ) {
|
||||||
|
default:
|
||||||
|
case AS_NEEDED_SINGLE: return TABBED_PANE_POLICY_AS_NEEDED_SINGLE;
|
||||||
|
case AS_NEEDED: return TABBED_PANE_POLICY_AS_NEEDED;
|
||||||
|
case NEVER: return TABBED_PANE_POLICY_NEVER;
|
||||||
|
}
|
||||||
|
|
||||||
|
case "scrollButtonsPlacement":
|
||||||
|
switch( scrollButtonsPlacement ) {
|
||||||
|
default:
|
||||||
|
case BOTH: return TABBED_PANE_PLACEMENT_BOTH;
|
||||||
|
case TRAILING: return TABBED_PANE_PLACEMENT_TRAILING;
|
||||||
|
}
|
||||||
|
|
||||||
|
case "tabAreaAlignment": return alignmentToString( tabAreaAlignment, TABBED_PANE_ALIGN_LEADING );
|
||||||
|
case "tabAlignment": return alignmentToString( tabAlignment, TABBED_PANE_ALIGN_CENTER );
|
||||||
|
|
||||||
|
case "tabWidthMode":
|
||||||
|
switch( tabWidthMode ) {
|
||||||
|
default:
|
||||||
|
case WIDTH_MODE_PREFERRED: return TABBED_PANE_TAB_WIDTH_MODE_PREFERRED;
|
||||||
|
case WIDTH_MODE_EQUAL: return TABBED_PANE_TAB_WIDTH_MODE_EQUAL;
|
||||||
|
case WIDTH_MODE_COMPACT: return TABBED_PANE_TAB_WIDTH_MODE_COMPACT;
|
||||||
|
}
|
||||||
|
|
||||||
|
case "tabIconPlacement":
|
||||||
|
switch( tabIconPlacement ) {
|
||||||
|
default:
|
||||||
|
case LEADING: return "leading";
|
||||||
|
case TRAILING: return "trailing";
|
||||||
|
case TOP: return "top";
|
||||||
|
case BOTTOM: return "bottom";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return FlatStylingSupport.getAnnotatedStyleableValue( this, key );
|
||||||
|
}
|
||||||
|
|
||||||
protected void setRolloverTab( int x, int y ) {
|
protected void setRolloverTab( int x, int y ) {
|
||||||
setRolloverTab( tabForCoordinate( tabPane, x, y ) );
|
setRolloverTab( tabForCoordinate( tabPane, x, y ) );
|
||||||
}
|
}
|
||||||
@@ -733,7 +813,6 @@ public class FlatTabbedPaneUI
|
|||||||
|
|
||||||
// increase size of repaint region to include part of content border
|
// increase size of repaint region to include part of content border
|
||||||
if( contentSeparatorHeight > 0 &&
|
if( contentSeparatorHeight > 0 &&
|
||||||
getTabType() == TAB_TYPE_CARD &&
|
|
||||||
clientPropertyBoolean( tabPane, TABBED_PANE_SHOW_CONTENT_SEPARATOR, true ) )
|
clientPropertyBoolean( tabPane, TABBED_PANE_SHOW_CONTENT_SEPARATOR, true ) )
|
||||||
{
|
{
|
||||||
int sh = scale( contentSeparatorHeight );
|
int sh = scale( contentSeparatorHeight );
|
||||||
@@ -1015,7 +1094,7 @@ public class FlatTabbedPaneUI
|
|||||||
|
|
||||||
// paint selection indicator
|
// paint selection indicator
|
||||||
if( isSelected )
|
if( isSelected )
|
||||||
paintTabSelection( g, tabPlacement, x, y, w, h );
|
paintTabSelection( g, tabPlacement, tabIndex, x, y, w, h );
|
||||||
|
|
||||||
if( tabPane.getTabComponentAt( tabIndex ) != null )
|
if( tabPane.getTabComponentAt( tabIndex ) != null )
|
||||||
return;
|
return;
|
||||||
@@ -1204,12 +1283,19 @@ public class FlatTabbedPaneUI
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void paintTabSelection( Graphics g, int tabPlacement, int x, int y, int w, int h ) {
|
protected void paintTabSelection( Graphics g, int tabPlacement, int tabIndex, int x, int y, int w, int h ) {
|
||||||
g.setColor( tabPane.isEnabled() ? underlineColor : disabledUnderlineColor );
|
g.setColor( tabPane.isEnabled()
|
||||||
|
? (isTabbedPaneOrChildFocused() ? underlineColor : inactiveUnderlineColor)
|
||||||
|
: disabledUnderlineColor );
|
||||||
|
|
||||||
// paint underline selection
|
// paint underline selection
|
||||||
boolean atBottom = (getTabType() != TAB_TYPE_CARD);
|
boolean atBottom = (getTabType() != TAB_TYPE_CARD);
|
||||||
Insets contentInsets = getContentBorderInsets( tabPlacement );
|
Insets contentInsets = atBottom
|
||||||
|
? ((!rotateTabRuns && runCount > 1 && !isScrollTabLayout() && getRunForTab( tabPane.getTabCount(), tabIndex ) > 0)
|
||||||
|
? new Insets( 0, 0, 0, 0 )
|
||||||
|
: getContentBorderInsets( tabPlacement ))
|
||||||
|
: null;
|
||||||
|
|
||||||
int tabSelectionHeight = scale( atBottom ? this.tabSelectionHeight : cardTabSelectionHeight );
|
int tabSelectionHeight = scale( atBottom ? this.tabSelectionHeight : cardTabSelectionHeight );
|
||||||
int sx, sy;
|
int sx, sy;
|
||||||
switch( tabPlacement ) {
|
switch( tabPlacement ) {
|
||||||
@@ -1236,6 +1322,23 @@ public class FlatTabbedPaneUI
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @since 2.2 */
|
||||||
|
@SuppressWarnings( "unchecked" )
|
||||||
|
protected boolean isTabbedPaneOrChildFocused() {
|
||||||
|
KeyboardFocusManager keyboardFocusManager = KeyboardFocusManager.getCurrentKeyboardFocusManager();
|
||||||
|
|
||||||
|
Object value = tabPane.getClientProperty( FlatClientProperties.COMPONENT_FOCUS_OWNER );
|
||||||
|
if( value instanceof Predicate ) {
|
||||||
|
return ((Predicate<JComponent>)value).test( tabPane ) &&
|
||||||
|
FlatUIUtils.isInActiveWindow( tabPane, keyboardFocusManager.getActiveWindow() );
|
||||||
|
}
|
||||||
|
|
||||||
|
Component focusOwner = keyboardFocusManager.getPermanentFocusOwner();
|
||||||
|
return focusOwner != null &&
|
||||||
|
SwingUtilities.isDescendingFrom( focusOwner, tabPane ) &&
|
||||||
|
FlatUIUtils.isInActiveWindow( focusOwner, keyboardFocusManager.getActiveWindow() );
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Actually does nearly the same as super.paintContentBorder() but
|
* Actually does nearly the same as super.paintContentBorder() but
|
||||||
* - not using UIManager.getColor("TabbedPane.contentAreaColor") to be GUI builder friendly
|
* - not using UIManager.getColor("TabbedPane.contentAreaColor") to be GUI builder friendly
|
||||||
@@ -1351,7 +1454,7 @@ public class FlatTabbedPaneUI
|
|||||||
else
|
else
|
||||||
g.clipRect( 0, vr.y, tabPane.getWidth(), vr.height );
|
g.clipRect( 0, vr.y, tabPane.getWidth(), vr.height );
|
||||||
|
|
||||||
paintTabSelection( g, tabPlacement, tabRect.x, tabRect.y, tabRect.width, tabRect.height );
|
paintTabSelection( g, tabPlacement, selectedIndex, tabRect.x, tabRect.y, tabRect.width, tabRect.height );
|
||||||
g.setClip( oldClip );
|
g.setClip( oldClip );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1504,6 +1607,11 @@ public class FlatTabbedPaneUI
|
|||||||
super.getTabRunCount( tabPane );
|
super.getTabRunCount( tabPane );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean shouldRotateTabRuns( int tabPlacement ) {
|
||||||
|
return rotateTabRuns;
|
||||||
|
}
|
||||||
|
|
||||||
private boolean isLastInRun( int tabIndex ) {
|
private boolean isLastInRun( int tabIndex ) {
|
||||||
int run = getRunForTab( tabPane.getTabCount(), tabIndex );
|
int run = getRunForTab( tabPane.getTabCount(), tabIndex );
|
||||||
return lastTabInRun( tabPane.getTabCount(), run ) == tabIndex;
|
return lastTabInRun( tabPane.getTabCount(), run ) == tabIndex;
|
||||||
@@ -1659,6 +1767,16 @@ public class FlatTabbedPaneUI
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static String alignmentToString( int value, String defaultValue ) {
|
||||||
|
switch( value ) {
|
||||||
|
case LEADING: return TABBED_PANE_ALIGN_LEADING;
|
||||||
|
case TRAILING: return TABBED_PANE_ALIGN_TRAILING;
|
||||||
|
case CENTER: return TABBED_PANE_ALIGN_CENTER;
|
||||||
|
case FILL: return TABBED_PANE_ALIGN_FILL;
|
||||||
|
default: return defaultValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected static int parseTabWidthMode( String str ) {
|
protected static int parseTabWidthMode( String str ) {
|
||||||
if( str == null )
|
if( str == null )
|
||||||
return WIDTH_MODE_PREFERRED;
|
return WIDTH_MODE_PREFERRED;
|
||||||
@@ -1841,7 +1959,7 @@ public class FlatTabbedPaneUI
|
|||||||
super( direction, arrowType,
|
super( direction, arrowType,
|
||||||
FlatTabbedPaneUI.this.foreground, FlatTabbedPaneUI.this.disabledForeground,
|
FlatTabbedPaneUI.this.foreground, FlatTabbedPaneUI.this.disabledForeground,
|
||||||
null, buttonHoverBackground, null, buttonPressedBackground );
|
null, buttonHoverBackground, null, buttonPressedBackground );
|
||||||
setArrowWidth( 10 );
|
setArrowWidth( 11 );
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void updateStyle() {
|
protected void updateStyle() {
|
||||||
@@ -1983,7 +2101,7 @@ public class FlatTabbedPaneUI
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected JMenuItem createTabMenuItem( int tabIndex ) {
|
protected JMenuItem createTabMenuItem( int tabIndex ) {
|
||||||
// search for tab name in this places
|
// search for tab name in these places
|
||||||
// 1. tab title
|
// 1. tab title
|
||||||
// 2. text of label or text component in custom tab component (including children)
|
// 2. text of label or text component in custom tab component (including children)
|
||||||
// 3. accessible name of tab
|
// 3. accessible name of tab
|
||||||
@@ -2016,7 +2134,7 @@ public class FlatTabbedPaneUI
|
|||||||
menuItem.setOpaque( true );
|
menuItem.setOpaque( true );
|
||||||
}
|
}
|
||||||
|
|
||||||
if( !tabPane.isEnabledAt( tabIndex ) )
|
if( !tabPane.isEnabled() || !tabPane.isEnabledAt( tabIndex ) )
|
||||||
menuItem.setEnabled( false );
|
menuItem.setEnabled( false );
|
||||||
|
|
||||||
menuItem.addActionListener( e -> selectTab( tabIndex ) );
|
menuItem.addActionListener( e -> selectTab( tabIndex ) );
|
||||||
@@ -2412,7 +2530,7 @@ public class FlatTabbedPaneUI
|
|||||||
if( tabPane == null || tabViewport == null )
|
if( tabPane == null || tabViewport == null )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if( !scrolled || tabViewport == null )
|
if( !scrolled )
|
||||||
return;
|
return;
|
||||||
scrolled = false;
|
scrolled = false;
|
||||||
|
|
||||||
@@ -2525,9 +2643,7 @@ public class FlatTabbedPaneUI
|
|||||||
setRolloverTab( tabIndex );
|
setRolloverTab( tabIndex );
|
||||||
|
|
||||||
// check whether mouse hit tab close area
|
// check whether mouse hit tab close area
|
||||||
boolean hitClose = isTabClosable( tabIndex )
|
boolean hitClose = isTabClosable( tabIndex ) && getTabCloseHitArea( tabIndex ).contains( x, y );
|
||||||
? getTabCloseHitArea( tabIndex ).contains( x, y )
|
|
||||||
: false;
|
|
||||||
if( e.getID() == MouseEvent.MOUSE_PRESSED )
|
if( e.getID() == MouseEvent.MOUSE_PRESSED )
|
||||||
pressedTabIndex = hitClose ? tabIndex : -1;
|
pressedTabIndex = hitClose ? tabIndex : -1;
|
||||||
setRolloverTabClose( hitClose );
|
setRolloverTabClose( hitClose );
|
||||||
@@ -2550,8 +2666,7 @@ public class FlatTabbedPaneUI
|
|||||||
if( tabIndex == lastTipTabIndex )
|
if( tabIndex == lastTipTabIndex )
|
||||||
return; // closeTip already set
|
return; // closeTip already set
|
||||||
|
|
||||||
if( tabIndex != lastTipTabIndex )
|
restoreTabToolTip();
|
||||||
restoreTabToolTip();
|
|
||||||
|
|
||||||
lastTipTabIndex = tabIndex;
|
lastTipTabIndex = tabIndex;
|
||||||
lastTip = tabPane.getToolTipTextAt( lastTipTabIndex );
|
lastTip = tabPane.getToolTipTextAt( lastTipTabIndex );
|
||||||
@@ -3344,4 +3459,77 @@ public class FlatTabbedPaneUI
|
|||||||
delegate.actionPerformed( e );
|
delegate.actionPerformed( e );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//---- class FlatSelectedTabRepainter -------------------------------------
|
||||||
|
|
||||||
|
private static class FlatSelectedTabRepainter
|
||||||
|
implements PropertyChangeListener//, Runnable
|
||||||
|
{
|
||||||
|
private static FlatSelectedTabRepainter instance;
|
||||||
|
|
||||||
|
private KeyboardFocusManager keyboardFocusManager;
|
||||||
|
|
||||||
|
static void install() {
|
||||||
|
synchronized( FlatSelectedTabRepainter.class ) {
|
||||||
|
if( instance != null )
|
||||||
|
return;
|
||||||
|
|
||||||
|
instance = new FlatSelectedTabRepainter();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FlatSelectedTabRepainter() {
|
||||||
|
keyboardFocusManager = KeyboardFocusManager.getCurrentKeyboardFocusManager();
|
||||||
|
keyboardFocusManager.addPropertyChangeListener( this );
|
||||||
|
}
|
||||||
|
|
||||||
|
private void uninstall() {
|
||||||
|
synchronized( FlatSelectedTabRepainter.class ) {
|
||||||
|
if( instance == null )
|
||||||
|
return;
|
||||||
|
|
||||||
|
keyboardFocusManager.removePropertyChangeListener( this );
|
||||||
|
keyboardFocusManager = null;
|
||||||
|
instance = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void propertyChange( PropertyChangeEvent e ) {
|
||||||
|
// uninstall if no longer using FlatLaf
|
||||||
|
if( !(UIManager.getLookAndFeel() instanceof FlatLaf) ) {
|
||||||
|
uninstall();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch( e.getPropertyName() ) {
|
||||||
|
case "permanentFocusOwner":
|
||||||
|
Object oldValue = e.getOldValue();
|
||||||
|
Object newValue = e.getNewValue();
|
||||||
|
if( oldValue instanceof Component )
|
||||||
|
repaintSelectedTabs( (Component) oldValue );
|
||||||
|
if( newValue instanceof Component )
|
||||||
|
repaintSelectedTabs( (Component) newValue );
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "activeWindow":
|
||||||
|
repaintSelectedTabs( keyboardFocusManager.getPermanentFocusOwner() );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void repaintSelectedTabs( Component c ) {
|
||||||
|
if( c instanceof JTabbedPane )
|
||||||
|
repaintSelectedTab( (JTabbedPane) c );
|
||||||
|
|
||||||
|
while( (c = SwingUtilities.getAncestorOfClass( JTabbedPane.class, c )) != null )
|
||||||
|
repaintSelectedTab( (JTabbedPane) c );
|
||||||
|
}
|
||||||
|
|
||||||
|
private void repaintSelectedTab( JTabbedPane tabbedPane ) {
|
||||||
|
TabbedPaneUI ui = tabbedPane.getUI();
|
||||||
|
if( ui instanceof FlatTabbedPaneUI )
|
||||||
|
((FlatTabbedPaneUI) ui).repaintTab( tabbedPane.getSelectedIndex() );
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ public class FlatTableCellBorder
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Because this borders are always shared for all tables,
|
* Because this border is always shared for all tables,
|
||||||
* get border specific style from FlatTableUI.
|
* get border specific style from FlatTableUI.
|
||||||
*/
|
*/
|
||||||
static <T> T getStyleFromTableUI( Component c, Function<FlatTableUI, T> f ) {
|
static <T> T getStyleFromTableUI( Component c, Function<FlatTableUI, T> f ) {
|
||||||
|
|||||||
@@ -39,7 +39,9 @@ import javax.swing.event.MouseInputListener;
|
|||||||
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.BasicTableHeaderUI;
|
import javax.swing.plaf.basic.BasicTableHeaderUI;
|
||||||
|
import javax.swing.table.JTableHeader;
|
||||||
import javax.swing.table.TableCellRenderer;
|
import javax.swing.table.TableCellRenderer;
|
||||||
|
import javax.swing.table.TableColumn;
|
||||||
import javax.swing.table.TableColumnModel;
|
import javax.swing.table.TableColumnModel;
|
||||||
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
|
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
|
||||||
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
|
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
|
||||||
@@ -169,6 +171,22 @@ public class FlatTableHeaderUI
|
|||||||
return FlatStylingSupport.getAnnotatedStyleableInfos( this );
|
return FlatStylingSupport.getAnnotatedStyleableInfos( this );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @since 2.5 */
|
||||||
|
@Override
|
||||||
|
public Object getStyleableValue( JComponent c, String key ) {
|
||||||
|
if( key.equals( "sortIconPosition" ) ) {
|
||||||
|
switch( sortIconPosition ) {
|
||||||
|
default:
|
||||||
|
case SwingConstants.RIGHT: return "right";
|
||||||
|
case SwingConstants.LEFT: return "left";
|
||||||
|
case SwingConstants.TOP: return "top";
|
||||||
|
case SwingConstants.BOTTOM: return "bottom";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return FlatStylingSupport.getAnnotatedStyleableValue( this, key );
|
||||||
|
}
|
||||||
|
|
||||||
private static int parseSortIconPosition( String str ) {
|
private static int parseSortIconPosition( String str ) {
|
||||||
if( str == null )
|
if( str == null )
|
||||||
str = "";
|
str = "";
|
||||||
@@ -195,6 +213,8 @@ public class FlatTableHeaderUI
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void paint( Graphics g, JComponent c ) {
|
public void paint( Graphics g, JComponent c ) {
|
||||||
|
fixDraggedAndResizingColumns( header );
|
||||||
|
|
||||||
TableColumnModel columnModel = header.getColumnModel();
|
TableColumnModel columnModel = header.getColumnModel();
|
||||||
if( columnModel.getColumnCount() <= 0 )
|
if( columnModel.getColumnCount() <= 0 )
|
||||||
return;
|
return;
|
||||||
@@ -269,6 +289,32 @@ public class FlatTableHeaderUI
|
|||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void fixDraggedAndResizingColumns( JTableHeader header ) {
|
||||||
|
if( header == null )
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Dragged column may be outdated in the case that the table structure
|
||||||
|
// was changed from a table header popup menu action. In this case
|
||||||
|
// the paint methods in BasicTableHeaderUI and BasicTableUI would throw exceptions.
|
||||||
|
TableColumn draggedColumn = header.getDraggedColumn();
|
||||||
|
if( draggedColumn != null && !isValidColumn( header.getColumnModel(), draggedColumn ) )
|
||||||
|
header.setDraggedColumn( null );
|
||||||
|
|
||||||
|
// also clear outdated resizing column (although this seems not cause exceptions)
|
||||||
|
TableColumn resizingColumn = header.getResizingColumn();
|
||||||
|
if( resizingColumn != null && !isValidColumn( header.getColumnModel(), resizingColumn ) )
|
||||||
|
header.setResizingColumn( null );
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isValidColumn( TableColumnModel cm, TableColumn column ) {
|
||||||
|
int count = cm.getColumnCount();
|
||||||
|
for( int i = 0; i < count; i++ ) {
|
||||||
|
if( cm.getColumn( i ) == column )
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
//---- class FlatTableCellHeaderRenderer ----------------------------------
|
//---- class FlatTableCellHeaderRenderer ----------------------------------
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -80,6 +80,7 @@ import com.formdev.flatlaf.util.UIScale;
|
|||||||
* @uiDefault Table.intercellSpacing Dimension
|
* @uiDefault Table.intercellSpacing Dimension
|
||||||
* @uiDefault Table.selectionInactiveBackground Color
|
* @uiDefault Table.selectionInactiveBackground Color
|
||||||
* @uiDefault Table.selectionInactiveForeground Color
|
* @uiDefault Table.selectionInactiveForeground Color
|
||||||
|
* @uiDefault Table.paintOutsideAlternateRows boolean
|
||||||
*
|
*
|
||||||
* <!-- FlatTableCellBorder -->
|
* <!-- FlatTableCellBorder -->
|
||||||
*
|
*
|
||||||
@@ -95,7 +96,7 @@ import com.formdev.flatlaf.util.UIScale;
|
|||||||
*/
|
*/
|
||||||
public class FlatTableUI
|
public class FlatTableUI
|
||||||
extends BasicTableUI
|
extends BasicTableUI
|
||||||
implements StyleableUI
|
implements StyleableUI, FlatViewportUI.ViewportPainter
|
||||||
{
|
{
|
||||||
protected boolean showHorizontalLines;
|
protected boolean showHorizontalLines;
|
||||||
protected boolean showVerticalLines;
|
protected boolean showVerticalLines;
|
||||||
@@ -285,10 +286,16 @@ public class FlatTableUI
|
|||||||
return FlatStylingSupport.getAnnotatedStyleableInfos( this );
|
return FlatStylingSupport.getAnnotatedStyleableInfos( this );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @since 2.5 */
|
||||||
|
@Override
|
||||||
|
public Object getStyleableValue( JComponent c, String key ) {
|
||||||
|
return FlatStylingSupport.getAnnotatedStyleableValue( this, key );
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Toggle selection colors from focused to inactive and vice versa.
|
* Toggle selection colors from focused to inactive and vice versa.
|
||||||
*
|
*
|
||||||
* This is not a optimal solution but much easier than rewriting the whole paint methods.
|
* This is not an optimal solution but much easier than rewriting the whole paint methods.
|
||||||
*
|
*
|
||||||
* Using a LaF specific renderer was avoided because often a custom renderer is
|
* Using a LaF specific renderer was avoided because often a custom renderer is
|
||||||
* already used in applications. Then either the inactive colors are not used,
|
* already used in applications. Then either the inactive colors are not used,
|
||||||
@@ -313,6 +320,8 @@ public class FlatTableUI
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void paint( Graphics g, JComponent c ) {
|
public void paint( Graphics g, JComponent c ) {
|
||||||
|
FlatTableHeaderUI.fixDraggedAndResizingColumns( table.getTableHeader() );
|
||||||
|
|
||||||
boolean horizontalLines = table.getShowHorizontalLines();
|
boolean horizontalLines = table.getShowHorizontalLines();
|
||||||
boolean verticalLines = table.getShowVerticalLines();
|
boolean verticalLines = table.getShowVerticalLines();
|
||||||
if( horizontalLines || verticalLines ) {
|
if( horizontalLines || verticalLines ) {
|
||||||
@@ -421,4 +430,38 @@ public class FlatTableUI
|
|||||||
? (viewport != rowHeader)
|
? (viewport != rowHeader)
|
||||||
: (viewport == rowHeader || rowHeader == null);
|
: (viewport == rowHeader || rowHeader == null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @since 2.3 */
|
||||||
|
@Override
|
||||||
|
public void paintViewport( Graphics g, JComponent c, JViewport viewport ) {
|
||||||
|
int viewportWidth = viewport.getWidth();
|
||||||
|
int viewportHeight = viewport.getHeight();
|
||||||
|
|
||||||
|
// fill viewport background in same color as table background
|
||||||
|
if( viewport.isOpaque() ) {
|
||||||
|
g.setColor( table.getBackground() );
|
||||||
|
g.fillRect( 0, 0, viewportWidth, viewportHeight );
|
||||||
|
}
|
||||||
|
|
||||||
|
// paint alternating empty rows
|
||||||
|
boolean paintOutside = UIManager.getBoolean( "Table.paintOutsideAlternateRows" );
|
||||||
|
Color alternateColor;
|
||||||
|
if( paintOutside && (alternateColor = UIManager.getColor( "Table.alternateRowColor" )) != null ) {
|
||||||
|
g.setColor( alternateColor );
|
||||||
|
|
||||||
|
int rowCount = table.getRowCount();
|
||||||
|
|
||||||
|
// paint alternating empty rows below the table
|
||||||
|
int tableHeight = table.getHeight();
|
||||||
|
if( tableHeight < viewportHeight ) {
|
||||||
|
int tableWidth = table.getWidth();
|
||||||
|
int rowHeight = table.getRowHeight();
|
||||||
|
|
||||||
|
for( int y = tableHeight, row = rowCount; y < viewportHeight; y += rowHeight, row++ ) {
|
||||||
|
if( row % 2 != 0 )
|
||||||
|
g.fillRect( 0, y, tableWidth, rowHeight );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -86,6 +86,13 @@ public class FlatTextAreaUI
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void installUI( JComponent c ) {
|
public void installUI( JComponent c ) {
|
||||||
|
if( FlatUIUtils.needsLightAWTPeer( c ) )
|
||||||
|
FlatUIUtils.runWithLightAWTPeerUIDefaults( () -> installUIImpl( c ) );
|
||||||
|
else
|
||||||
|
installUIImpl( c );
|
||||||
|
}
|
||||||
|
|
||||||
|
private void installUIImpl( JComponent c ) {
|
||||||
super.installUI( c );
|
super.installUI( c );
|
||||||
|
|
||||||
installStyle();
|
installStyle();
|
||||||
@@ -183,6 +190,12 @@ public class FlatTextAreaUI
|
|||||||
return FlatStylingSupport.getAnnotatedStyleableInfos( this );
|
return FlatStylingSupport.getAnnotatedStyleableInfos( this );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @since 2.5 */
|
||||||
|
@Override
|
||||||
|
public Object getStyleableValue( JComponent c, String key ) {
|
||||||
|
return FlatStylingSupport.getAnnotatedStyleableValue( this, key );
|
||||||
|
}
|
||||||
|
|
||||||
private void updateBackground() {
|
private void updateBackground() {
|
||||||
FlatTextFieldUI.updateBackground( getComponent(), background,
|
FlatTextFieldUI.updateBackground( getComponent(), background,
|
||||||
disabledBackground, inactiveBackground,
|
disabledBackground, inactiveBackground,
|
||||||
|
|||||||
@@ -128,6 +128,13 @@ public class FlatTextFieldUI
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void installUI( JComponent c ) {
|
public void installUI( JComponent c ) {
|
||||||
|
if( FlatUIUtils.needsLightAWTPeer( c ) )
|
||||||
|
FlatUIUtils.runWithLightAWTPeerUIDefaults( () -> installUIImpl( c ) );
|
||||||
|
else
|
||||||
|
installUIImpl( c );
|
||||||
|
}
|
||||||
|
|
||||||
|
private void installUIImpl( JComponent c ) {
|
||||||
super.installUI( c );
|
super.installUI( c );
|
||||||
|
|
||||||
leadingIcon = clientProperty( c, TEXT_FIELD_LEADING_ICON, null, Icon.class );
|
leadingIcon = clientProperty( c, TEXT_FIELD_LEADING_ICON, null, Icon.class );
|
||||||
@@ -232,6 +239,7 @@ public class FlatTextFieldUI
|
|||||||
switch( e.getPropertyName() ) {
|
switch( e.getPropertyName() ) {
|
||||||
case PLACEHOLDER_TEXT:
|
case PLACEHOLDER_TEXT:
|
||||||
case COMPONENT_ROUND_RECT:
|
case COMPONENT_ROUND_RECT:
|
||||||
|
case OUTLINE:
|
||||||
case TEXT_FIELD_PADDING:
|
case TEXT_FIELD_PADDING:
|
||||||
c.repaint();
|
c.repaint();
|
||||||
break;
|
break;
|
||||||
@@ -353,6 +361,12 @@ public class FlatTextFieldUI
|
|||||||
return FlatStylingSupport.getAnnotatedStyleableInfos( this, getComponent().getBorder() );
|
return FlatStylingSupport.getAnnotatedStyleableInfos( this, getComponent().getBorder() );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @since 2.5 */
|
||||||
|
@Override
|
||||||
|
public Object getStyleableValue( JComponent c, String key ) {
|
||||||
|
return FlatStylingSupport.getAnnotatedStyleableValue( this, getComponent().getBorder(), key );
|
||||||
|
}
|
||||||
|
|
||||||
private void updateBackground() {
|
private void updateBackground() {
|
||||||
updateBackground( getComponent(), background,
|
updateBackground( getComponent(), background,
|
||||||
disabledBackground, inactiveBackground,
|
disabledBackground, inactiveBackground,
|
||||||
@@ -368,7 +382,7 @@ public class FlatTextFieldUI
|
|||||||
if( !(oldBackground instanceof UIResource) )
|
if( !(oldBackground instanceof UIResource) )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// do not update background if it currently has a unknown color (assigned from outside)
|
// do not update background if it currently has an unknown color (assigned from outside)
|
||||||
if( oldBackground != background &&
|
if( oldBackground != background &&
|
||||||
oldBackground != disabledBackground &&
|
oldBackground != disabledBackground &&
|
||||||
oldBackground != inactiveBackground &&
|
oldBackground != inactiveBackground &&
|
||||||
@@ -609,7 +623,7 @@ debug*/
|
|||||||
* Returns the rectangle used to paint leading and trailing icons.
|
* Returns the rectangle used to paint leading and trailing icons.
|
||||||
* It invokes {@code super.getVisibleEditorRect()} and reduces left and/or
|
* It invokes {@code super.getVisibleEditorRect()} and reduces left and/or
|
||||||
* right margin if the text field has leading or trailing icons or components.
|
* right margin if the text field has leading or trailing icons or components.
|
||||||
* Also the preferred widths of leading and trailing components are removed.
|
* Also, the preferred widths of leading and trailing components are removed.
|
||||||
*
|
*
|
||||||
* @since 2
|
* @since 2
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -191,6 +191,12 @@ public class FlatTextPaneUI
|
|||||||
return FlatStylingSupport.getAnnotatedStyleableInfos( this );
|
return FlatStylingSupport.getAnnotatedStyleableInfos( this );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @since 2.5 */
|
||||||
|
@Override
|
||||||
|
public Object getStyleableValue( JComponent c, String key ) {
|
||||||
|
return FlatStylingSupport.getAnnotatedStyleableValue( this, key );
|
||||||
|
}
|
||||||
|
|
||||||
private void updateBackground() {
|
private void updateBackground() {
|
||||||
FlatTextFieldUI.updateBackground( getComponent(), background,
|
FlatTextFieldUI.updateBackground( getComponent(), background,
|
||||||
disabledBackground, inactiveBackground,
|
disabledBackground, inactiveBackground,
|
||||||
|
|||||||
@@ -24,6 +24,8 @@ import java.awt.Container;
|
|||||||
import java.awt.Dialog;
|
import java.awt.Dialog;
|
||||||
import java.awt.Dimension;
|
import java.awt.Dimension;
|
||||||
import java.awt.EventQueue;
|
import java.awt.EventQueue;
|
||||||
|
import java.awt.Font;
|
||||||
|
import java.awt.FontMetrics;
|
||||||
import java.awt.Frame;
|
import java.awt.Frame;
|
||||||
import java.awt.Graphics;
|
import java.awt.Graphics;
|
||||||
import java.awt.GraphicsConfiguration;
|
import java.awt.GraphicsConfiguration;
|
||||||
@@ -31,6 +33,7 @@ import java.awt.Image;
|
|||||||
import java.awt.Insets;
|
import java.awt.Insets;
|
||||||
import java.awt.Point;
|
import java.awt.Point;
|
||||||
import java.awt.Rectangle;
|
import java.awt.Rectangle;
|
||||||
|
import java.awt.Toolkit;
|
||||||
import java.awt.Window;
|
import java.awt.Window;
|
||||||
import java.awt.event.ActionListener;
|
import java.awt.event.ActionListener;
|
||||||
import java.awt.event.ComponentEvent;
|
import java.awt.event.ComponentEvent;
|
||||||
@@ -50,13 +53,14 @@ import javax.accessibility.AccessibleContext;
|
|||||||
import javax.swing.BorderFactory;
|
import javax.swing.BorderFactory;
|
||||||
import javax.swing.Box;
|
import javax.swing.Box;
|
||||||
import javax.swing.BoxLayout;
|
import javax.swing.BoxLayout;
|
||||||
|
import javax.swing.Icon;
|
||||||
import javax.swing.JButton;
|
import javax.swing.JButton;
|
||||||
import javax.swing.JComponent;
|
import javax.swing.JComponent;
|
||||||
|
import javax.swing.JDialog;
|
||||||
import javax.swing.JLabel;
|
import javax.swing.JLabel;
|
||||||
import javax.swing.JMenuBar;
|
import javax.swing.JMenuBar;
|
||||||
import javax.swing.JPanel;
|
import javax.swing.JPanel;
|
||||||
import javax.swing.JRootPane;
|
import javax.swing.JRootPane;
|
||||||
import javax.swing.SwingConstants;
|
|
||||||
import javax.swing.SwingUtilities;
|
import javax.swing.SwingUtilities;
|
||||||
import javax.swing.UIManager;
|
import javax.swing.UIManager;
|
||||||
import javax.swing.border.AbstractBorder;
|
import javax.swing.border.AbstractBorder;
|
||||||
@@ -70,6 +74,7 @@ import com.formdev.flatlaf.util.UIScale;
|
|||||||
/**
|
/**
|
||||||
* Provides the Flat LaF title bar.
|
* Provides the Flat LaF title bar.
|
||||||
*
|
*
|
||||||
|
* @uiDefault TitlePane.font Font
|
||||||
* @uiDefault TitlePane.background Color
|
* @uiDefault TitlePane.background Color
|
||||||
* @uiDefault TitlePane.inactiveBackground Color
|
* @uiDefault TitlePane.inactiveBackground Color
|
||||||
* @uiDefault TitlePane.foreground Color
|
* @uiDefault TitlePane.foreground Color
|
||||||
@@ -78,15 +83,20 @@ import com.formdev.flatlaf.util.UIScale;
|
|||||||
* @uiDefault TitlePane.borderColor Color optional
|
* @uiDefault TitlePane.borderColor Color optional
|
||||||
* @uiDefault TitlePane.unifiedBackground boolean
|
* @uiDefault TitlePane.unifiedBackground boolean
|
||||||
* @uiDefault TitlePane.showIcon boolean
|
* @uiDefault TitlePane.showIcon boolean
|
||||||
|
* @uiDefault TitlePane.showIconInDialogs boolean
|
||||||
* @uiDefault TitlePane.noIconLeftGap int
|
* @uiDefault TitlePane.noIconLeftGap int
|
||||||
* @uiDefault TitlePane.iconSize Dimension
|
* @uiDefault TitlePane.iconSize Dimension
|
||||||
* @uiDefault TitlePane.iconMargins Insets
|
* @uiDefault TitlePane.iconMargins Insets
|
||||||
* @uiDefault TitlePane.titleMargins Insets
|
* @uiDefault TitlePane.titleMargins Insets
|
||||||
* @uiDefault TitlePane.menuBarEmbedded boolean
|
* @uiDefault TitlePane.menuBarEmbedded boolean
|
||||||
|
* @uiDefault TitlePane.titleMinimumWidth int
|
||||||
|
* @uiDefault TitlePane.buttonMinimumWidth int
|
||||||
* @uiDefault TitlePane.buttonMaximizedHeight int
|
* @uiDefault TitlePane.buttonMaximizedHeight int
|
||||||
* @uiDefault TitlePane.centerTitle boolean
|
* @uiDefault TitlePane.centerTitle boolean
|
||||||
* @uiDefault TitlePane.centerTitleIfMenuBarEmbedded boolean
|
* @uiDefault TitlePane.centerTitleIfMenuBarEmbedded boolean
|
||||||
|
* @uiDefault TitlePane.showIconBesideTitle boolean
|
||||||
* @uiDefault TitlePane.menuBarTitleGap int
|
* @uiDefault TitlePane.menuBarTitleGap int
|
||||||
|
* @uiDefault TitlePane.menuBarResizeHeight int
|
||||||
* @uiDefault TitlePane.closeIcon Icon
|
* @uiDefault TitlePane.closeIcon Icon
|
||||||
* @uiDefault TitlePane.iconifyIcon Icon
|
* @uiDefault TitlePane.iconifyIcon Icon
|
||||||
* @uiDefault TitlePane.maximizeIcon Icon
|
* @uiDefault TitlePane.maximizeIcon Icon
|
||||||
@@ -97,6 +107,7 @@ import com.formdev.flatlaf.util.UIScale;
|
|||||||
public class FlatTitlePane
|
public class FlatTitlePane
|
||||||
extends JComponent
|
extends JComponent
|
||||||
{
|
{
|
||||||
|
/** @since 2.5 */ protected final Font titleFont = UIManager.getFont( "TitlePane.font" );
|
||||||
protected final Color activeBackground = UIManager.getColor( "TitlePane.background" );
|
protected final Color activeBackground = UIManager.getColor( "TitlePane.background" );
|
||||||
protected final Color inactiveBackground = UIManager.getColor( "TitlePane.inactiveBackground" );
|
protected final Color inactiveBackground = UIManager.getColor( "TitlePane.inactiveBackground" );
|
||||||
protected final Color activeForeground = UIManager.getColor( "TitlePane.foreground" );
|
protected final Color activeForeground = UIManager.getColor( "TitlePane.foreground" );
|
||||||
@@ -105,12 +116,18 @@ public class FlatTitlePane
|
|||||||
protected final Color borderColor = UIManager.getColor( "TitlePane.borderColor" );
|
protected final Color borderColor = UIManager.getColor( "TitlePane.borderColor" );
|
||||||
|
|
||||||
/** @since 2 */ protected final boolean showIcon = FlatUIUtils.getUIBoolean( "TitlePane.showIcon", true );
|
/** @since 2 */ protected final boolean showIcon = FlatUIUtils.getUIBoolean( "TitlePane.showIcon", true );
|
||||||
|
/** @since 2.5 */ protected final boolean showIconInDialogs = FlatUIUtils.getUIBoolean( "TitlePane.showIconInDialogs", true );
|
||||||
/** @since 2 */ protected final int noIconLeftGap = FlatUIUtils.getUIInt( "TitlePane.noIconLeftGap", 8 );
|
/** @since 2 */ protected final int noIconLeftGap = FlatUIUtils.getUIInt( "TitlePane.noIconLeftGap", 8 );
|
||||||
protected final Dimension iconSize = UIManager.getDimension( "TitlePane.iconSize" );
|
protected final Dimension iconSize = UIManager.getDimension( "TitlePane.iconSize" );
|
||||||
|
/** @since 2.4 */ protected final int titleMinimumWidth = FlatUIUtils.getUIInt( "TitlePane.titleMinimumWidth", 60 );
|
||||||
|
/** @since 2.4 */ protected final int buttonMinimumWidth = FlatUIUtils.getUIInt( "TitlePane.buttonMinimumWidth", 30 );
|
||||||
protected final int buttonMaximizedHeight = UIManager.getInt( "TitlePane.buttonMaximizedHeight" );
|
protected final int buttonMaximizedHeight = UIManager.getInt( "TitlePane.buttonMaximizedHeight" );
|
||||||
protected final boolean centerTitle = UIManager.getBoolean( "TitlePane.centerTitle" );
|
protected final boolean centerTitle = UIManager.getBoolean( "TitlePane.centerTitle" );
|
||||||
protected final boolean centerTitleIfMenuBarEmbedded = FlatUIUtils.getUIBoolean( "TitlePane.centerTitleIfMenuBarEmbedded", true );
|
protected final boolean centerTitleIfMenuBarEmbedded = FlatUIUtils.getUIBoolean( "TitlePane.centerTitleIfMenuBarEmbedded", true );
|
||||||
protected final int menuBarTitleGap = FlatUIUtils.getUIInt( "TitlePane.menuBarTitleGap", 20 );
|
/** @since 2.4 */ protected final boolean showIconBesideTitle = UIManager.getBoolean( "TitlePane.showIconBesideTitle" );
|
||||||
|
protected final int menuBarTitleGap = FlatUIUtils.getUIInt( "TitlePane.menuBarTitleGap", 40 );
|
||||||
|
/** @since 2.4 */ protected final int menuBarTitleMinimumGap = FlatUIUtils.getUIInt( "TitlePane.menuBarTitleMinimumGap", 12 );
|
||||||
|
/** @since 2.4 */ protected final int menuBarResizeHeight = FlatUIUtils.getUIInt( "TitlePane.menuBarResizeHeight", 4 );
|
||||||
|
|
||||||
protected final JRootPane rootPane;
|
protected final JRootPane rootPane;
|
||||||
|
|
||||||
@@ -142,6 +159,8 @@ public class FlatTitlePane
|
|||||||
|
|
||||||
// necessary for closing window with double-click on icon
|
// necessary for closing window with double-click on icon
|
||||||
iconLabel.addMouseListener( handler );
|
iconLabel.addMouseListener( handler );
|
||||||
|
|
||||||
|
applyComponentOrientation( rootPane.getComponentOrientation() );
|
||||||
}
|
}
|
||||||
|
|
||||||
protected FlatTitlePaneBorder createTitlePaneBorder() {
|
protected FlatTitlePaneBorder createTitlePaneBorder() {
|
||||||
@@ -163,7 +182,6 @@ public class FlatTitlePane
|
|||||||
};
|
};
|
||||||
iconLabel.setBorder( new FlatEmptyBorder( UIManager.getInsets( "TitlePane.iconMargins" ) ) );
|
iconLabel.setBorder( new FlatEmptyBorder( UIManager.getInsets( "TitlePane.iconMargins" ) ) );
|
||||||
titleLabel.setBorder( new FlatEmptyBorder( UIManager.getInsets( "TitlePane.titleMargins" ) ) );
|
titleLabel.setBorder( new FlatEmptyBorder( UIManager.getInsets( "TitlePane.titleMargins" ) ) );
|
||||||
titleLabel.setHorizontalAlignment( SwingConstants.CENTER );
|
|
||||||
|
|
||||||
leftPanel.setLayout( new BoxLayout( leftPanel, BoxLayout.LINE_AXIS ) );
|
leftPanel.setLayout( new BoxLayout( leftPanel, BoxLayout.LINE_AXIS ) );
|
||||||
leftPanel.setOpaque( false );
|
leftPanel.setOpaque( false );
|
||||||
@@ -183,18 +201,52 @@ public class FlatTitlePane
|
|||||||
setLayout( new BorderLayout() {
|
setLayout( new BorderLayout() {
|
||||||
@Override
|
@Override
|
||||||
public void layoutContainer( Container target ) {
|
public void layoutContainer( Container target ) {
|
||||||
super.layoutContainer( target );
|
// compute available bounds
|
||||||
|
|
||||||
// make left panel (with embedded menu bar) smaller if horizontal space is rare
|
|
||||||
// to avoid that embedded menu bar overlaps button bar
|
|
||||||
Insets insets = target.getInsets();
|
Insets insets = target.getInsets();
|
||||||
int width = target.getWidth() - insets.left - insets.right;
|
int x = insets.left;
|
||||||
if( leftPanel.getWidth() + buttonPanel.getWidth() > width ) {
|
int y = insets.top;
|
||||||
int oldWidth = leftPanel.getWidth();
|
int w = target.getWidth() - insets.left - insets.right;
|
||||||
int newWidth = Math.max( width - buttonPanel.getWidth(), 0 );
|
int h = target.getHeight() - insets.top - insets.bottom;
|
||||||
leftPanel.setSize( newWidth, leftPanel.getHeight() );
|
|
||||||
if( !getComponentOrientation().isLeftToRight() )
|
// compute widths
|
||||||
leftPanel.setLocation( leftPanel.getX() + (oldWidth - newWidth), leftPanel.getY() );
|
int leftWidth = leftPanel.getPreferredSize().width;
|
||||||
|
int buttonsWidth = buttonPanel.getPreferredSize().width;
|
||||||
|
int titleWidth = w - leftWidth - buttonsWidth;
|
||||||
|
int minTitleWidth = UIScale.scale( titleMinimumWidth );
|
||||||
|
|
||||||
|
// increase minimum width if icon is show besides the title
|
||||||
|
Icon icon = titleLabel.getIcon();
|
||||||
|
if( icon != null ) {
|
||||||
|
Insets iconInsets = iconLabel.getInsets();
|
||||||
|
int iconTextGap = titleLabel.getComponentOrientation().isLeftToRight() ? iconInsets.right : iconInsets.left;
|
||||||
|
minTitleWidth += icon.getIconWidth() + iconTextGap;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if title is too small, reduce width of buttons
|
||||||
|
if( titleWidth < minTitleWidth ) {
|
||||||
|
buttonsWidth = Math.max( buttonsWidth - (minTitleWidth - titleWidth), buttonPanel.getMinimumSize().width );
|
||||||
|
titleWidth = w - leftWidth - buttonsWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if title is still too small, reduce width of left panel (icon and embedded menu bar)
|
||||||
|
if( titleWidth < minTitleWidth ) {
|
||||||
|
int minLeftWidth = iconLabel.isVisible()
|
||||||
|
? iconLabel.getWidth() - iconLabel.getInsets().right
|
||||||
|
: UIScale.scale( noIconLeftGap );
|
||||||
|
leftWidth = Math.max( leftWidth - (minTitleWidth - titleWidth), minLeftWidth );
|
||||||
|
titleWidth = w - leftWidth - buttonsWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( target.getComponentOrientation().isLeftToRight() ) {
|
||||||
|
// left-to-right
|
||||||
|
leftPanel.setBounds( x, y, leftWidth, h );
|
||||||
|
titleLabel.setBounds( x + leftWidth, y, titleWidth, h );
|
||||||
|
buttonPanel.setBounds( x + leftWidth + titleWidth, y, buttonsWidth, h );
|
||||||
|
} else {
|
||||||
|
// right-to-left
|
||||||
|
buttonPanel.setBounds( x, y, buttonsWidth, h );
|
||||||
|
titleLabel.setBounds( x + buttonsWidth, y, titleWidth, h );
|
||||||
|
leftPanel.setBounds( x + buttonsWidth + titleWidth, y, leftWidth, h );
|
||||||
}
|
}
|
||||||
|
|
||||||
// If menu bar is embedded and contains a horizontal glue component,
|
// If menu bar is embedded and contains a horizontal glue component,
|
||||||
@@ -233,10 +285,7 @@ public class FlatTitlePane
|
|||||||
@Override
|
@Override
|
||||||
public Dimension getPreferredSize() {
|
public Dimension getPreferredSize() {
|
||||||
Dimension size = super.getPreferredSize();
|
Dimension size = super.getPreferredSize();
|
||||||
if( buttonMaximizedHeight > 0 &&
|
if( buttonMaximizedHeight > 0 && isWindowMaximized() && !hasVisibleEmbeddedMenuBar( rootPane.getJMenuBar() ) ) {
|
||||||
window instanceof Frame &&
|
|
||||||
(((Frame)window).getExtendedState() & Frame.MAXIMIZED_BOTH) != 0 )
|
|
||||||
{
|
|
||||||
// make title pane height smaller when frame is maximized
|
// make title pane height smaller when frame is maximized
|
||||||
size = new Dimension( size.width, Math.min( size.height, UIScale.scale( buttonMaximizedHeight ) ) );
|
size = new Dimension( size.width, Math.min( size.height, UIScale.scale( buttonMaximizedHeight ) ) );
|
||||||
}
|
}
|
||||||
@@ -248,7 +297,7 @@ public class FlatTitlePane
|
|||||||
if( rootPane.getWindowDecorationStyle() == JRootPane.FRAME ) {
|
if( rootPane.getWindowDecorationStyle() == JRootPane.FRAME ) {
|
||||||
// JRootPane.FRAME works only for frames (and not for dialogs)
|
// JRootPane.FRAME works only for frames (and not for dialogs)
|
||||||
// but at this time the owner window type is unknown (not yet added)
|
// but at this time the owner window type is unknown (not yet added)
|
||||||
// so we add the iconify/maximize/restore buttons and they are shown
|
// so we add the iconify/maximize/restore buttons, and they are shown
|
||||||
// later in frameStateChanged(), which is invoked from addNotify()
|
// later in frameStateChanged(), which is invoked from addNotify()
|
||||||
|
|
||||||
buttonPanel.add( iconifyButton );
|
buttonPanel.add( iconifyButton );
|
||||||
@@ -259,7 +308,13 @@ public class FlatTitlePane
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected JButton createButton( String iconKey, String accessibleName, ActionListener action ) {
|
protected JButton createButton( String iconKey, String accessibleName, ActionListener action ) {
|
||||||
JButton button = new JButton( UIManager.getIcon( iconKey ) );
|
JButton button = new JButton( UIManager.getIcon( iconKey ) ) {
|
||||||
|
@Override
|
||||||
|
public Dimension getMinimumSize() {
|
||||||
|
// allow the button to shrink if space is rare
|
||||||
|
return new Dimension( UIScale.scale( buttonMinimumWidth ), super.getMinimumSize().height );
|
||||||
|
}
|
||||||
|
};
|
||||||
button.setFocusable( false );
|
button.setFocusable( false );
|
||||||
button.setContentAreaFilled( false );
|
button.setContentAreaFilled( false );
|
||||||
button.setBorder( BorderFactory.createEmptyBorder() );
|
button.setBorder( BorderFactory.createEmptyBorder() );
|
||||||
@@ -309,6 +364,7 @@ public class FlatTitlePane
|
|||||||
restoreButton.setVisible( resizable && maximized );
|
restoreButton.setVisible( resizable && maximized );
|
||||||
|
|
||||||
if( maximized &&
|
if( maximized &&
|
||||||
|
!(SystemInfo.isLinux && FlatNativeLinuxLibrary.isWMUtilsSupported( window )) &&
|
||||||
rootPane.getClientProperty( "_flatlaf.maximizedBoundsUpToDate" ) == null )
|
rootPane.getClientProperty( "_flatlaf.maximizedBoundsUpToDate" ) == null )
|
||||||
{
|
{
|
||||||
rootPane.putClientProperty( "_flatlaf.maximizedBoundsUpToDate", null );
|
rootPane.putClientProperty( "_flatlaf.maximizedBoundsUpToDate", null );
|
||||||
@@ -339,9 +395,13 @@ public class FlatTitlePane
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected void updateIcon() {
|
protected void updateIcon() {
|
||||||
|
boolean defaultShowIcon = showIcon;
|
||||||
|
if( !showIconInDialogs && rootPane.getParent() instanceof JDialog )
|
||||||
|
defaultShowIcon = false;
|
||||||
|
|
||||||
// get window images
|
// get window images
|
||||||
List<Image> images = null;
|
List<Image> images = null;
|
||||||
if( clientPropertyBoolean( rootPane, TITLE_BAR_SHOW_ICON, showIcon ) ) {
|
if( clientPropertyBoolean( rootPane, TITLE_BAR_SHOW_ICON, defaultShowIcon ) ) {
|
||||||
images = window.getIconImages();
|
images = window.getIconImages();
|
||||||
if( images.isEmpty() ) {
|
if( images.isEmpty() ) {
|
||||||
// search in owners
|
// search in owners
|
||||||
@@ -356,11 +416,12 @@ public class FlatTitlePane
|
|||||||
boolean hasIcon = (images != null && !images.isEmpty());
|
boolean hasIcon = (images != null && !images.isEmpty());
|
||||||
|
|
||||||
// set icon
|
// set icon
|
||||||
iconLabel.setIcon( hasIcon ? new FlatTitlePaneIcon( images, iconSize ) : null );
|
iconLabel.setIcon( hasIcon && !showIconBesideTitle ? new FlatTitlePaneIcon( images, iconSize ) : null );
|
||||||
|
titleLabel.setIcon( hasIcon && showIconBesideTitle ? new FlatTitlePaneIcon( images, iconSize ) : null );
|
||||||
|
|
||||||
// show/hide icon
|
// show/hide icon
|
||||||
iconLabel.setVisible( hasIcon );
|
iconLabel.setVisible( hasIcon && !showIconBesideTitle );
|
||||||
leftPanel.setBorder( hasIcon ? null : FlatUIUtils.nonUIResource( new FlatEmptyBorder( 0, noIconLeftGap, 0, 0 ) ) );
|
leftPanel.setBorder( hasIcon && !showIconBesideTitle ? null : FlatUIUtils.nonUIResource( new FlatEmptyBorder( 0, noIconLeftGap, 0, 0 ) ) );
|
||||||
|
|
||||||
updateNativeTitleBarHeightAndHitTestSpotsLater();
|
updateNativeTitleBarHeightAndHitTestSpotsLater();
|
||||||
}
|
}
|
||||||
@@ -420,7 +481,7 @@ public class FlatTitlePane
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns whether this title pane currently has an visible and embedded menubar.
|
* Returns whether this title pane currently has a visible and embedded menubar.
|
||||||
*/
|
*/
|
||||||
protected boolean hasVisibleEmbeddedMenuBar( JMenuBar menuBar ) {
|
protected boolean hasVisibleEmbeddedMenuBar( JMenuBar menuBar ) {
|
||||||
return menuBar != null && menuBar.isVisible() && isMenuBarEmbedded();
|
return menuBar != null && menuBar.isVisible() && isMenuBarEmbedded();
|
||||||
@@ -567,6 +628,11 @@ debug*/
|
|||||||
frame.setExtendedState( frame.getExtendedState() | Frame.ICONIFIED );
|
frame.setExtendedState( frame.getExtendedState() | Frame.ICONIFIED );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @since 2.4 */
|
||||||
|
protected boolean isWindowMaximized() {
|
||||||
|
return window instanceof Frame && (((Frame)window).getExtendedState() & Frame.MAXIMIZED_BOTH) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Maximizes the window.
|
* Maximizes the window.
|
||||||
*/
|
*/
|
||||||
@@ -615,7 +681,7 @@ debug*/
|
|||||||
int maximizedWidth = screenBounds.width;
|
int maximizedWidth = screenBounds.width;
|
||||||
int maximizedHeight = screenBounds.height;
|
int maximizedHeight = screenBounds.height;
|
||||||
|
|
||||||
if( !isMaximizedBoundsFixed() ) {
|
if( SystemInfo.isWindows && !isMaximizedBoundsFixed() ) {
|
||||||
// on Java 8 to 14, maximized x,y are 0,0 based on all screens in a multi-screen environment
|
// on Java 8 to 14, maximized x,y are 0,0 based on all screens in a multi-screen environment
|
||||||
maximizedX = 0;
|
maximizedX = 0;
|
||||||
maximizedY = 0;
|
maximizedY = 0;
|
||||||
@@ -683,6 +749,17 @@ debug*/
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void maximizeOrRestore() {
|
||||||
|
if( !(window instanceof Frame) || !((Frame)window).isResizable() )
|
||||||
|
return;
|
||||||
|
|
||||||
|
Frame frame = (Frame) window;
|
||||||
|
if( (frame.getExtendedState() & Frame.MAXIMIZED_BOTH) != 0 )
|
||||||
|
restore();
|
||||||
|
else
|
||||||
|
maximize();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Closes the window.
|
* Closes the window.
|
||||||
*/
|
*/
|
||||||
@@ -729,7 +806,7 @@ debug*/
|
|||||||
List<Rectangle> hitTestSpots = new ArrayList<>();
|
List<Rectangle> hitTestSpots = new ArrayList<>();
|
||||||
Rectangle appIconBounds = null;
|
Rectangle appIconBounds = null;
|
||||||
|
|
||||||
if( iconLabel.isVisible() ) {
|
if( !showIconBesideTitle && iconLabel.isVisible() ) {
|
||||||
// compute real icon size (without insets; 1px larger for easier hitting)
|
// compute real icon size (without insets; 1px larger for easier hitting)
|
||||||
Point location = SwingUtilities.convertPoint( iconLabel, 0, 0, window );
|
Point location = SwingUtilities.convertPoint( iconLabel, 0, 0, window );
|
||||||
Insets iconInsets = iconLabel.getInsets();
|
Insets iconInsets = iconLabel.getInsets();
|
||||||
@@ -741,9 +818,7 @@ debug*/
|
|||||||
|
|
||||||
// if frame is maximized, increase icon bounds to upper-left corner
|
// if frame is maximized, increase icon bounds to upper-left corner
|
||||||
// of window to allow closing window via double-click in upper-left corner
|
// of window to allow closing window via double-click in upper-left corner
|
||||||
if( window instanceof Frame &&
|
if( isWindowMaximized() ) {
|
||||||
(((Frame)window).getExtendedState() & Frame.MAXIMIZED_BOTH) != 0 )
|
|
||||||
{
|
|
||||||
iconBounds.height += iconBounds.y;
|
iconBounds.height += iconBounds.y;
|
||||||
iconBounds.y = 0;
|
iconBounds.y = 0;
|
||||||
|
|
||||||
@@ -758,6 +833,38 @@ debug*/
|
|||||||
hitTestSpots.add( iconBounds );
|
hitTestSpots.add( iconBounds );
|
||||||
else
|
else
|
||||||
appIconBounds = iconBounds;
|
appIconBounds = iconBounds;
|
||||||
|
} else if( showIconBesideTitle && titleLabel.getIcon() != null && titleLabel.getUI() instanceof FlatTitleLabelUI ) {
|
||||||
|
FlatTitleLabelUI ui = (FlatTitleLabelUI) titleLabel.getUI();
|
||||||
|
|
||||||
|
// compute real icon bounds
|
||||||
|
Insets insets = titleLabel.getInsets();
|
||||||
|
Rectangle viewR = new Rectangle( insets.left, insets.top,
|
||||||
|
titleLabel.getWidth() - insets.left - insets.right,
|
||||||
|
titleLabel.getHeight() - insets.top - insets.bottom );
|
||||||
|
Rectangle iconR = new Rectangle();
|
||||||
|
Rectangle textR = new Rectangle();
|
||||||
|
ui.layoutCL( titleLabel, titleLabel.getFontMetrics( titleLabel.getFont() ),
|
||||||
|
titleLabel.getText(), titleLabel.getIcon(),
|
||||||
|
viewR, iconR, textR );
|
||||||
|
|
||||||
|
// Windows shows the window system menu only in the upper-left corner
|
||||||
|
if( iconR.x == 0 ) {
|
||||||
|
// convert icon location to window coordinates
|
||||||
|
Point location = SwingUtilities.convertPoint( titleLabel, 0, 0, window );
|
||||||
|
iconR.x += location.x;
|
||||||
|
iconR.y += location.y;
|
||||||
|
|
||||||
|
// make icon bounds 1px larger for easier hitting
|
||||||
|
iconR.x -= 1;
|
||||||
|
iconR.y -= 1;
|
||||||
|
iconR.width += 2;
|
||||||
|
iconR.height += 2;
|
||||||
|
|
||||||
|
if( hasJBRCustomDecoration() )
|
||||||
|
hitTestSpots.add( iconR );
|
||||||
|
else
|
||||||
|
appIconBounds = iconR;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle r = getNativeHitTestSpot( buttonPanel );
|
Rectangle r = getNativeHitTestSpot( buttonPanel );
|
||||||
@@ -768,26 +875,41 @@ debug*/
|
|||||||
if( hasVisibleEmbeddedMenuBar( menuBar ) ) {
|
if( hasVisibleEmbeddedMenuBar( menuBar ) ) {
|
||||||
r = getNativeHitTestSpot( menuBar );
|
r = getNativeHitTestSpot( menuBar );
|
||||||
if( r != null ) {
|
if( r != null ) {
|
||||||
Component horizontalGlue = findHorizontalGlue( menuBar );
|
// if frame is resizable and not maximized, make menu bar hit test spot smaller at top
|
||||||
if( horizontalGlue != null ) {
|
// to have a small area above the menu bar to resize the window
|
||||||
// If menu bar is embedded and contains a horizontal glue component,
|
if( window instanceof Frame && ((Frame)window).isResizable() && !isWindowMaximized() ) {
|
||||||
// then split the hit test spot into two spots so that
|
// limit to 8, because Windows does not use a larger height
|
||||||
// the glue component area can used to move the window.
|
int resizeHeight = UIScale.scale( Math.min( menuBarResizeHeight, 8 ) );
|
||||||
|
r.y += resizeHeight;
|
||||||
|
r.height -= resizeHeight;
|
||||||
|
}
|
||||||
|
|
||||||
Point glueLocation = SwingUtilities.convertPoint( horizontalGlue, 0, 0, window );
|
int count = menuBar.getComponentCount();
|
||||||
int x2 = glueLocation.x + horizontalGlue.getWidth();
|
for( int i = count - 1; i >= 0; i-- ) {
|
||||||
Rectangle r2;
|
Component c = menuBar.getComponent( i );
|
||||||
if( getComponentOrientation().isLeftToRight() ) {
|
if( c instanceof Box.Filler ||
|
||||||
r2 = new Rectangle( x2, r.y, (r.x + r.width) - x2, r.height );
|
(c instanceof JComponent && clientPropertyBoolean( (JComponent) c, COMPONENT_TITLE_BAR_CAPTION, false ) ) )
|
||||||
|
{
|
||||||
|
// If menu bar is embedded and contains a horizontal glue or caption component,
|
||||||
|
// then split the hit test spot so that
|
||||||
|
// the glue/caption component area can be used to move the window.
|
||||||
|
|
||||||
r.width = glueLocation.x - r.x;
|
Point glueLocation = SwingUtilities.convertPoint( c, 0, 0, window );
|
||||||
} else {
|
int x2 = glueLocation.x + c.getWidth();
|
||||||
r2 = new Rectangle( r.x, r.y, glueLocation.x - r.x, r.height );
|
Rectangle r2;
|
||||||
|
if( getComponentOrientation().isLeftToRight() ) {
|
||||||
|
r2 = new Rectangle( x2, r.y, (r.x + r.width) - x2, r.height );
|
||||||
|
|
||||||
r.width = (r.x + r.width) - x2;
|
r.width = glueLocation.x - r.x;
|
||||||
r.x = x2;
|
} else {
|
||||||
|
r2 = new Rectangle( r.x, r.y, glueLocation.x - r.x, r.height );
|
||||||
|
|
||||||
|
r.width = (r.x + r.width) - x2;
|
||||||
|
r.x = x2;
|
||||||
|
}
|
||||||
|
if( r2.width > 0 )
|
||||||
|
hitTestSpots.add( r2 );
|
||||||
}
|
}
|
||||||
hitTestSpots.add( r2 );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
hitTestSpots.add( r );
|
hitTestSpots.add( r );
|
||||||
@@ -854,7 +976,7 @@ debug*/
|
|||||||
} else if( borderColor != null && (rootPane.getJMenuBar() == null || !rootPane.getJMenuBar().isVisible()) )
|
} else if( borderColor != null && (rootPane.getJMenuBar() == null || !rootPane.getJMenuBar().isVisible()) )
|
||||||
insets.bottom += UIScale.scale( 1 );
|
insets.bottom += UIScale.scale( 1 );
|
||||||
|
|
||||||
if( !SystemInfo.isWindows_11_orLater && hasNativeCustomDecoration() && !isWindowMaximized( c ) )
|
if( !SystemInfo.isWindows_11_orLater && hasNativeCustomDecoration() && !isWindowMaximized() )
|
||||||
insets = FlatUIUtils.addInsets( insets, WindowTopBorder.getInstance().getBorderInsets() );
|
insets = FlatUIUtils.addInsets( insets, WindowTopBorder.getInstance().getBorderInsets() );
|
||||||
|
|
||||||
return insets;
|
return insets;
|
||||||
@@ -873,7 +995,7 @@ debug*/
|
|||||||
FlatUIUtils.paintFilledRectangle( g, borderColor, x, y + height - lineHeight, width, lineHeight );
|
FlatUIUtils.paintFilledRectangle( g, borderColor, x, y + height - lineHeight, width, lineHeight );
|
||||||
}
|
}
|
||||||
|
|
||||||
if( !SystemInfo.isWindows_11_orLater && hasNativeCustomDecoration() && !isWindowMaximized( c ) )
|
if( !SystemInfo.isWindows_11_orLater && hasNativeCustomDecoration() && !isWindowMaximized() )
|
||||||
WindowTopBorder.getInstance().paintBorder( c, g, x, y, width, height );
|
WindowTopBorder.getInstance().paintBorder( c, g, x, y, width, height );
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -881,12 +1003,6 @@ debug*/
|
|||||||
JMenuBar menuBar = rootPane.getJMenuBar();
|
JMenuBar menuBar = rootPane.getJMenuBar();
|
||||||
return hasVisibleEmbeddedMenuBar( menuBar ) ? menuBar.getBorder() : null;
|
return hasVisibleEmbeddedMenuBar( menuBar ) ? menuBar.getBorder() : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected boolean isWindowMaximized( Component c ) {
|
|
||||||
return window instanceof Frame
|
|
||||||
? (((Frame)window).getExtendedState() & Frame.MAXIMIZED_BOTH) != 0
|
|
||||||
: false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//---- class FlatTitleLabelUI ---------------------------------------------
|
//---- class FlatTitleLabelUI ---------------------------------------------
|
||||||
@@ -900,32 +1016,109 @@ debug*/
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void paintEnabledText( JLabel l, Graphics g, String s, int textX, int textY ) {
|
protected void installDefaults( JLabel c ) {
|
||||||
boolean hasEmbeddedMenuBar = hasVisibleEmbeddedMenuBar( rootPane.getJMenuBar() );
|
super.installDefaults( c );
|
||||||
int labelWidth = l.getWidth();
|
|
||||||
int textWidth = labelWidth - (textX * 2);
|
|
||||||
int gap = UIScale.scale( menuBarTitleGap );
|
|
||||||
|
|
||||||
// The passed in textX coordinate is always to horizontally center the text within the label bounds.
|
if( titleFont != null )
|
||||||
// Modify textX so that the text is painted either centered within the window bounds or leading aligned.
|
c.setFont( titleFont );
|
||||||
boolean center = hasEmbeddedMenuBar ? centerTitleIfMenuBarEmbedded : centerTitle;
|
}
|
||||||
if( center ) {
|
|
||||||
// If window is wide enough, center title within window bounds.
|
@Override
|
||||||
// Otherwise leave it centered within free space (label bounds).
|
protected String layoutCL( JLabel label, FontMetrics fontMetrics, String text, Icon icon,
|
||||||
int centeredTextX = ((l.getParent().getWidth() - textWidth) / 2) - l.getX();
|
Rectangle viewR, Rectangle iconR, Rectangle textR )
|
||||||
if( centeredTextX >= gap && centeredTextX + textWidth <= labelWidth - gap )
|
{
|
||||||
textX = centeredTextX;
|
JMenuBar menuBar = rootPane.getJMenuBar();
|
||||||
} else {
|
boolean hasEmbeddedMenuBar = hasVisibleEmbeddedMenuBar( menuBar );
|
||||||
// leading aligned
|
boolean hasEmbeddedLeadingMenus = hasEmbeddedMenuBar && hasLeadingMenus( menuBar );
|
||||||
boolean leftToRight = getComponentOrientation().isLeftToRight();
|
boolean leftToRight = getComponentOrientation().isLeftToRight();
|
||||||
Insets insets = l.getInsets();
|
|
||||||
int leadingInset = hasEmbeddedMenuBar ? gap : (leftToRight ? insets.left : insets.right);
|
if( hasEmbeddedMenuBar ) {
|
||||||
int leadingTextX = leftToRight ? leadingInset : labelWidth - leadingInset - textWidth;
|
int minGap = UIScale.scale( menuBarTitleMinimumGap );
|
||||||
if( leftToRight ? leadingTextX < textX : leadingTextX > textX )
|
|
||||||
textX = leadingTextX;
|
// apply minimum leading gap (between embedded menu bar and title)
|
||||||
|
if( hasEmbeddedLeadingMenus ) {
|
||||||
|
if( leftToRight )
|
||||||
|
viewR.x += minGap;
|
||||||
|
viewR.width -= minGap;
|
||||||
|
}
|
||||||
|
|
||||||
|
// apply minimum trailing gap (between title and right aligned components of embedded menu bar)
|
||||||
|
Component horizontalGlue = findHorizontalGlue( menuBar );
|
||||||
|
if( horizontalGlue != null && menuBar.getComponent( menuBar.getComponentCount() - 1 ) != horizontalGlue ) {
|
||||||
|
if( !leftToRight )
|
||||||
|
viewR.x += minGap;
|
||||||
|
viewR.width -= minGap;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
super.paintEnabledText( l, g, s, textX, textY );
|
// compute icon width and gap (if icon is show besides the title)
|
||||||
|
int iconTextGap = 0;
|
||||||
|
int iconWidthAndGap = 0;
|
||||||
|
if( icon != null ) {
|
||||||
|
Insets iconInsets = iconLabel.getInsets();
|
||||||
|
iconTextGap = leftToRight ? iconInsets.right : iconInsets.left;
|
||||||
|
iconWidthAndGap = icon.getIconWidth() + iconTextGap;
|
||||||
|
}
|
||||||
|
|
||||||
|
// layout title and icon (if show besides the title)
|
||||||
|
String clippedText = SwingUtilities.layoutCompoundLabel( label, fontMetrics, text, icon,
|
||||||
|
label.getVerticalAlignment(), label.getHorizontalAlignment(),
|
||||||
|
label.getVerticalTextPosition(), label.getHorizontalTextPosition(),
|
||||||
|
viewR, iconR, textR,
|
||||||
|
iconTextGap );
|
||||||
|
|
||||||
|
// compute text X location
|
||||||
|
if( !clippedText.equals( text ) ) {
|
||||||
|
// if text is clipped, align to left (or right)
|
||||||
|
textR.x = leftToRight
|
||||||
|
? viewR.x + iconWidthAndGap
|
||||||
|
: viewR.x + viewR.width - iconWidthAndGap - textR.width;
|
||||||
|
} else {
|
||||||
|
int leadingGap = hasEmbeddedLeadingMenus ? UIScale.scale( menuBarTitleGap - menuBarTitleMinimumGap ) : 0;
|
||||||
|
|
||||||
|
boolean center = hasEmbeddedLeadingMenus ? centerTitleIfMenuBarEmbedded : centerTitle;
|
||||||
|
if( center ) {
|
||||||
|
// If window is wide enough, center title within window bounds.
|
||||||
|
// Otherwise, center within free space (label bounds).
|
||||||
|
Container parent = label.getParent();
|
||||||
|
int centeredTextX = (parent != null) ? ((parent.getWidth() - textR.width - iconWidthAndGap) / 2) + iconWidthAndGap - label.getX() : -1;
|
||||||
|
textR.x = (centeredTextX >= viewR.x + leadingGap && centeredTextX + textR.width <= viewR.x + viewR.width - leadingGap)
|
||||||
|
? centeredTextX
|
||||||
|
: viewR.x + ((viewR.width - textR.width - iconWidthAndGap) / 2) + iconWidthAndGap;
|
||||||
|
} else {
|
||||||
|
// leading aligned with leading gap, which is reduced if space is rare
|
||||||
|
textR.x = leftToRight
|
||||||
|
? Math.min( viewR.x + leadingGap + iconWidthAndGap, viewR.x + viewR.width - textR.width )
|
||||||
|
: Math.max( viewR.x + viewR.width - leadingGap - iconWidthAndGap - textR.width, viewR.x );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// compute icon X location (relative to text X location)
|
||||||
|
if( icon != null ) {
|
||||||
|
iconR.x = leftToRight
|
||||||
|
? textR.x - iconWidthAndGap
|
||||||
|
: textR.x + textR.width + iconTextGap;
|
||||||
|
}
|
||||||
|
|
||||||
|
return clippedText;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean hasLeadingMenus( JMenuBar menuBar ) {
|
||||||
|
// check whether menu bar is empty
|
||||||
|
if( menuBar.getComponentCount() == 0 || menuBar.getWidth() == 0 )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// check whether menu bar has a leading glue component
|
||||||
|
// (no menus/components at left side)
|
||||||
|
Component horizontalGlue = findHorizontalGlue( menuBar );
|
||||||
|
if( horizontalGlue != null ) {
|
||||||
|
boolean leftToRight = getComponentOrientation().isLeftToRight();
|
||||||
|
if( (leftToRight && horizontalGlue.getX() == 0) ||
|
||||||
|
(!leftToRight && horizontalGlue.getX() + horizontalGlue.getWidth() == menuBar.getWidth()) )
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -992,23 +1185,30 @@ debug*/
|
|||||||
//---- interface MouseListener ----
|
//---- interface MouseListener ----
|
||||||
|
|
||||||
private Point dragOffset;
|
private Point dragOffset;
|
||||||
|
private boolean nativeMove;
|
||||||
|
private long lastSingleClickWhen;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void mouseClicked( MouseEvent e ) {
|
public void mouseClicked( MouseEvent e ) {
|
||||||
|
// on Linux, when using native library, the mouse clicked event
|
||||||
|
// is usually not sent and maximize/restore is done in mouse pressed event
|
||||||
|
// this check is here for the case that a mouse clicked event comes thru for some reason
|
||||||
|
if( SystemInfo.isLinux && FlatNativeLinuxLibrary.isWMUtilsSupported( window ) ) {
|
||||||
|
// see comment in mousePressed()
|
||||||
|
if( lastSingleClickWhen != 0 && (e.getWhen() - lastSingleClickWhen) <= getMultiClickInterval() ) {
|
||||||
|
lastSingleClickWhen = 0;
|
||||||
|
maximizeOrRestore();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if( e.getClickCount() == 2 && SwingUtilities.isLeftMouseButton( e ) ) {
|
if( e.getClickCount() == 2 && SwingUtilities.isLeftMouseButton( e ) ) {
|
||||||
if( e.getSource() == iconLabel ) {
|
if( e.getSource() == iconLabel ) {
|
||||||
// double-click on icon closes window
|
// double-click on icon closes window
|
||||||
close();
|
close();
|
||||||
} else if( !hasNativeCustomDecoration() &&
|
} else if( !hasNativeCustomDecoration() ) {
|
||||||
window instanceof Frame &&
|
|
||||||
((Frame)window).isResizable() )
|
|
||||||
{
|
|
||||||
// maximize/restore on double-click
|
// maximize/restore on double-click
|
||||||
Frame frame = (Frame) window;
|
maximizeOrRestore();
|
||||||
if( (frame.getExtendedState() & Frame.MAXIMIZED_BOTH) != 0 )
|
|
||||||
restore();
|
|
||||||
else
|
|
||||||
maximize();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1018,7 +1218,56 @@ debug*/
|
|||||||
if( window == null )
|
if( window == null )
|
||||||
return; // should newer occur
|
return; // should newer occur
|
||||||
|
|
||||||
|
// on Linux, show window menu
|
||||||
|
if( SwingUtilities.isRightMouseButton( e ) &&
|
||||||
|
SystemInfo.isLinux && FlatNativeLinuxLibrary.isWMUtilsSupported( window ) )
|
||||||
|
{
|
||||||
|
e.consume();
|
||||||
|
FlatNativeLinuxLibrary.showWindowMenu( window, e );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( !SwingUtilities.isLeftMouseButton( e ) )
|
||||||
|
return;
|
||||||
|
|
||||||
dragOffset = SwingUtilities.convertPoint( FlatTitlePane.this, e.getPoint(), window );
|
dragOffset = SwingUtilities.convertPoint( FlatTitlePane.this, e.getPoint(), window );
|
||||||
|
nativeMove = false;
|
||||||
|
|
||||||
|
// on Linux, move or maximize/restore window
|
||||||
|
if( SystemInfo.isLinux && FlatNativeLinuxLibrary.isWMUtilsSupported( window ) ) {
|
||||||
|
// The fired Java mouse events, when doing a double-click and the first click
|
||||||
|
// sends a _NET_WM_MOVERESIZE message, are different for various Linux distributions:
|
||||||
|
// CentOS 7 (GNOME 3.28.2, X11): PRESSED(clickCount=1) PRESSED(clickCount=2) RELEASED(clickCount=2)
|
||||||
|
// Ubuntu 20.04 (GNOME 3.36.1, X11): PRESSED(clickCount=1) PRESSED(clickCount=2) RELEASED(clickCount=2)
|
||||||
|
// Ubuntu 22.04 (GNOME 42.2, Wayland): PRESSED(clickCount=1) RELEASED(clickCount=1) CLICKED(clickCount=1)
|
||||||
|
// Kubuntu 22.04 (KDE 5.24.4, X11): PRESSED(clickCount=1) PRESSED(clickCount=1) RELEASED(clickCount=1)
|
||||||
|
|
||||||
|
// double-click is not always recognized in Java when using _NET_WM_MOVERESIZE message
|
||||||
|
int clickCount = e.getClickCount();
|
||||||
|
if( clickCount == 1 && lastSingleClickWhen != 0 && (e.getWhen() - lastSingleClickWhen) <= getMultiClickInterval() )
|
||||||
|
clickCount = 2;
|
||||||
|
|
||||||
|
switch( clickCount ) {
|
||||||
|
case 1:
|
||||||
|
// move window via _NET_WM_MOVERESIZE message
|
||||||
|
e.consume();
|
||||||
|
nativeMove = FlatNativeLinuxLibrary.moveOrResizeWindow( window, e, FlatNativeLinuxLibrary.MOVE );
|
||||||
|
lastSingleClickWhen = e.getWhen();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 2:
|
||||||
|
// maximize/restore on double-click
|
||||||
|
// also done here because no mouse clicked event is sent when using _NET_WM_MOVERESIZE message
|
||||||
|
lastSingleClickWhen = 0;
|
||||||
|
maximizeOrRestore();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private int getMultiClickInterval() {
|
||||||
|
Object value = Toolkit.getDefaultToolkit().getDesktopProperty( "awt.multiClickInterval" );
|
||||||
|
return (value instanceof Integer) ? (Integer) value : 500;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override public void mouseReleased( MouseEvent e ) {}
|
@Override public void mouseReleased( MouseEvent e ) {}
|
||||||
@@ -1029,9 +1278,15 @@ debug*/
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void mouseDragged( MouseEvent e ) {
|
public void mouseDragged( MouseEvent e ) {
|
||||||
if( window == null )
|
if( window == null || dragOffset == null )
|
||||||
return; // should newer occur
|
return; // should newer occur
|
||||||
|
|
||||||
|
if( nativeMove )
|
||||||
|
return;
|
||||||
|
|
||||||
|
if( !SwingUtilities.isLeftMouseButton( e ) )
|
||||||
|
return;
|
||||||
|
|
||||||
if( hasNativeCustomDecoration() )
|
if( hasNativeCustomDecoration() )
|
||||||
return; // do nothing if having native window border
|
return; // do nothing if having native window border
|
||||||
|
|
||||||
|
|||||||
@@ -21,12 +21,8 @@ import java.awt.Color;
|
|||||||
import java.awt.Component;
|
import java.awt.Component;
|
||||||
import java.awt.Graphics;
|
import java.awt.Graphics;
|
||||||
import java.beans.PropertyChangeEvent;
|
import java.beans.PropertyChangeEvent;
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import javax.swing.AbstractButton;
|
import javax.swing.*;
|
||||||
import javax.swing.JComponent;
|
|
||||||
import javax.swing.JToggleButton;
|
|
||||||
import javax.swing.UIManager;
|
|
||||||
import javax.swing.plaf.ComponentUI;
|
import javax.swing.plaf.ComponentUI;
|
||||||
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
|
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
|
||||||
import com.formdev.flatlaf.ui.FlatStylingSupport.UnknownStyleException;
|
import com.formdev.flatlaf.ui.FlatStylingSupport.UnknownStyleException;
|
||||||
@@ -52,15 +48,29 @@ import com.formdev.flatlaf.util.UIScale;
|
|||||||
* @uiDefault ToggleButton.iconTextGap int
|
* @uiDefault ToggleButton.iconTextGap int
|
||||||
* @uiDefault ToggleButton.startBackground Color optional; if set, a gradient paint is used and ToggleButton.background is ignored
|
* @uiDefault ToggleButton.startBackground Color optional; if set, a gradient paint is used and ToggleButton.background is ignored
|
||||||
* @uiDefault ToggleButton.endBackground Color optional; if set, a gradient paint is used
|
* @uiDefault ToggleButton.endBackground Color optional; if set, a gradient paint is used
|
||||||
* @uiDefault ToggleButton.pressedBackground Color
|
* @uiDefault ToggleButton.focusedBackground Color optional
|
||||||
|
* @uiDefault ToggleButton.focusedForeground Color optional
|
||||||
|
* @uiDefault ToggleButton.hoverBackground Color optional
|
||||||
|
* @uiDefault ToggleButton.hoverForeground Color optional
|
||||||
|
* @uiDefault ToggleButton.pressedBackground Color optional
|
||||||
|
* @uiDefault ToggleButton.pressedForeground Color optional
|
||||||
* @uiDefault ToggleButton.selectedBackground Color
|
* @uiDefault ToggleButton.selectedBackground Color
|
||||||
* @uiDefault ToggleButton.selectedForeground Color
|
* @uiDefault ToggleButton.selectedForeground Color
|
||||||
* @uiDefault ToggleButton.disabledBackground Color optional
|
* @uiDefault ToggleButton.disabledBackground Color optional
|
||||||
* @uiDefault ToggleButton.disabledText Color
|
* @uiDefault ToggleButton.disabledText Color
|
||||||
* @uiDefault ToggleButton.disabledSelectedBackground Color
|
* @uiDefault ToggleButton.disabledSelectedBackground Color
|
||||||
|
* @uiDefault ToggleButton.disabledSelectedForeground Color optional
|
||||||
|
* @uiDefault Button.paintShadow boolean default is false
|
||||||
|
* @uiDefault Button.shadowWidth int default is 2
|
||||||
|
* @uiDefault Button.shadowColor Color optional
|
||||||
* @uiDefault ToggleButton.toolbar.hoverBackground Color
|
* @uiDefault ToggleButton.toolbar.hoverBackground Color
|
||||||
|
* @uiDefault ToggleButton.toolbar.hoverForeground Color optional
|
||||||
* @uiDefault ToggleButton.toolbar.pressedBackground Color
|
* @uiDefault ToggleButton.toolbar.pressedBackground Color
|
||||||
|
* @uiDefault ToggleButton.toolbar.pressedForeground Color optional
|
||||||
* @uiDefault ToggleButton.toolbar.selectedBackground Color
|
* @uiDefault ToggleButton.toolbar.selectedBackground Color
|
||||||
|
* @uiDefault ToggleButton.toolbar.selectedForeground Color optional
|
||||||
|
* @uiDefault ToggleButton.toolbar.disabledSelectedBackground Color optional
|
||||||
|
* @uiDefault ToggleButton.toolbar.disabledSelectedForeground Color optional
|
||||||
*
|
*
|
||||||
* <!-- FlatToggleButtonUI -->
|
* <!-- FlatToggleButtonUI -->
|
||||||
*
|
*
|
||||||
@@ -68,8 +78,11 @@ import com.formdev.flatlaf.util.UIScale;
|
|||||||
* @uiDefault ToggleButton.tab.underlineColor Color
|
* @uiDefault ToggleButton.tab.underlineColor Color
|
||||||
* @uiDefault ToggleButton.tab.disabledUnderlineColor Color
|
* @uiDefault ToggleButton.tab.disabledUnderlineColor Color
|
||||||
* @uiDefault ToggleButton.tab.selectedBackground Color optional
|
* @uiDefault ToggleButton.tab.selectedBackground Color optional
|
||||||
|
* @uiDefault ToggleButton.tab.selectedForeground Color optional
|
||||||
* @uiDefault ToggleButton.tab.hoverBackground Color
|
* @uiDefault ToggleButton.tab.hoverBackground Color
|
||||||
|
* @uiDefault ToggleButton.tab.hoverForeground Color optional
|
||||||
* @uiDefault ToggleButton.tab.focusBackground Color
|
* @uiDefault ToggleButton.tab.focusBackground Color
|
||||||
|
* @uiDefault ToggleButton.tab.focusForeground Color optional
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* @author Karl Tauber
|
* @author Karl Tauber
|
||||||
@@ -81,8 +94,11 @@ public class FlatToggleButtonUI
|
|||||||
@Styleable(dot=true) protected Color tabUnderlineColor;
|
@Styleable(dot=true) protected Color tabUnderlineColor;
|
||||||
@Styleable(dot=true) protected Color tabDisabledUnderlineColor;
|
@Styleable(dot=true) protected Color tabDisabledUnderlineColor;
|
||||||
@Styleable(dot=true) protected Color tabSelectedBackground;
|
@Styleable(dot=true) protected Color tabSelectedBackground;
|
||||||
|
/** @since 2.3 */ @Styleable(dot=true) protected Color tabSelectedForeground;
|
||||||
@Styleable(dot=true) protected Color tabHoverBackground;
|
@Styleable(dot=true) protected Color tabHoverBackground;
|
||||||
|
/** @since 2.3 */ @Styleable(dot=true) protected Color tabHoverForeground;
|
||||||
@Styleable(dot=true) protected Color tabFocusBackground;
|
@Styleable(dot=true) protected Color tabFocusBackground;
|
||||||
|
/** @since 2.3 */ @Styleable(dot=true) protected Color tabFocusForeground;
|
||||||
|
|
||||||
private boolean defaults_initialized = false;
|
private boolean defaults_initialized = false;
|
||||||
|
|
||||||
@@ -115,8 +131,11 @@ public class FlatToggleButtonUI
|
|||||||
tabUnderlineColor = UIManager.getColor( "ToggleButton.tab.underlineColor" );
|
tabUnderlineColor = UIManager.getColor( "ToggleButton.tab.underlineColor" );
|
||||||
tabDisabledUnderlineColor = UIManager.getColor( "ToggleButton.tab.disabledUnderlineColor" );
|
tabDisabledUnderlineColor = UIManager.getColor( "ToggleButton.tab.disabledUnderlineColor" );
|
||||||
tabSelectedBackground = UIManager.getColor( "ToggleButton.tab.selectedBackground" );
|
tabSelectedBackground = UIManager.getColor( "ToggleButton.tab.selectedBackground" );
|
||||||
|
tabSelectedForeground = UIManager.getColor( "ToggleButton.tab.selectedForeground" );
|
||||||
tabHoverBackground = UIManager.getColor( "ToggleButton.tab.hoverBackground" );
|
tabHoverBackground = UIManager.getColor( "ToggleButton.tab.hoverBackground" );
|
||||||
|
tabHoverForeground = UIManager.getColor( "ToggleButton.tab.hoverForeground" );
|
||||||
tabFocusBackground = UIManager.getColor( "ToggleButton.tab.focusBackground" );
|
tabFocusBackground = UIManager.getColor( "ToggleButton.tab.focusBackground" );
|
||||||
|
tabFocusForeground = UIManager.getColor( "ToggleButton.tab.focusForeground" );
|
||||||
|
|
||||||
defaults_initialized = true;
|
defaults_initialized = true;
|
||||||
}
|
}
|
||||||
@@ -143,6 +162,7 @@ public class FlatToggleButtonUI
|
|||||||
b.repaint();
|
b.repaint();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case TAB_BUTTON_UNDERLINE_PLACEMENT:
|
||||||
case TAB_BUTTON_UNDERLINE_HEIGHT:
|
case TAB_BUTTON_UNDERLINE_HEIGHT:
|
||||||
case TAB_BUTTON_UNDERLINE_COLOR:
|
case TAB_BUTTON_UNDERLINE_COLOR:
|
||||||
case TAB_BUTTON_SELECTED_BACKGROUND:
|
case TAB_BUTTON_SELECTED_BACKGROUND:
|
||||||
@@ -164,11 +184,7 @@ public class FlatToggleButtonUI
|
|||||||
@Override
|
@Override
|
||||||
public Map<String, Class<?>> getStyleableInfos( JComponent c ) {
|
public Map<String, Class<?>> getStyleableInfos( JComponent c ) {
|
||||||
Map<String, Class<?>> infos = super.getStyleableInfos( c );
|
Map<String, Class<?>> infos = super.getStyleableInfos( c );
|
||||||
Iterator<String> it = infos.keySet().iterator();
|
infos.keySet().removeIf( s -> s.startsWith( "help." ) );
|
||||||
while( it.hasNext() ) {
|
|
||||||
if( it.next().startsWith( "help." ) )
|
|
||||||
it.remove();
|
|
||||||
}
|
|
||||||
return infos;
|
return infos;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -201,13 +217,42 @@ public class FlatToggleButtonUI
|
|||||||
|
|
||||||
// paint underline if selected
|
// paint underline if selected
|
||||||
if( selected ) {
|
if( selected ) {
|
||||||
int underlineHeight = UIScale.scale( clientPropertyInt( c, TAB_BUTTON_UNDERLINE_HEIGHT, tabUnderlineHeight ) );
|
int underlineThickness = UIScale.scale( clientPropertyInt( c, TAB_BUTTON_UNDERLINE_HEIGHT, tabUnderlineHeight ) );
|
||||||
g.setColor( c.isEnabled()
|
g.setColor( c.isEnabled()
|
||||||
? clientPropertyColor( c, TAB_BUTTON_UNDERLINE_COLOR, tabUnderlineColor )
|
? clientPropertyColor( c, TAB_BUTTON_UNDERLINE_COLOR, tabUnderlineColor )
|
||||||
: tabDisabledUnderlineColor );
|
: tabDisabledUnderlineColor );
|
||||||
g.fillRect( 0, height - underlineHeight, width, underlineHeight );
|
int placement = clientPropertyInt( c, TAB_BUTTON_UNDERLINE_PLACEMENT, SwingConstants.BOTTOM );
|
||||||
|
switch (placement) {
|
||||||
|
case SwingConstants.TOP:
|
||||||
|
g.fillRect( 0, 0, width, underlineThickness );
|
||||||
|
break;
|
||||||
|
case SwingConstants.LEFT:
|
||||||
|
g.fillRect( 0, 0, underlineThickness, height );
|
||||||
|
break;
|
||||||
|
case SwingConstants.RIGHT:
|
||||||
|
g.fillRect( width - underlineThickness, 0, underlineThickness, height );
|
||||||
|
break;
|
||||||
|
case SwingConstants.BOTTOM:
|
||||||
|
default:
|
||||||
|
g.fillRect( 0, height - underlineThickness, width, underlineThickness );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else
|
} else
|
||||||
super.paintBackground( g, c );
|
super.paintBackground( g, c );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Color getForeground( JComponent c ) {
|
||||||
|
if( isTabButton( c ) ) {
|
||||||
|
if( !c.isEnabled() )
|
||||||
|
return disabledText;
|
||||||
|
|
||||||
|
if( tabSelectedForeground != null && ((AbstractButton)c).isSelected() )
|
||||||
|
return tabSelectedForeground;
|
||||||
|
|
||||||
|
return buttonStateColor( c, c.getForeground(), disabledText,
|
||||||
|
tabFocusForeground, tabHoverForeground, null );
|
||||||
|
} else
|
||||||
|
return super.getForeground( c );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -160,6 +160,12 @@ public class FlatToolBarSeparatorUI
|
|||||||
return FlatStylingSupport.getAnnotatedStyleableInfos( this );
|
return FlatStylingSupport.getAnnotatedStyleableInfos( this );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @since 2.5 */
|
||||||
|
@Override
|
||||||
|
public Object getStyleableValue( JComponent c, String key ) {
|
||||||
|
return FlatStylingSupport.getAnnotatedStyleableValue( this, key );
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Dimension getPreferredSize( JComponent c ) {
|
public Dimension getPreferredSize( JComponent c ) {
|
||||||
Dimension size = ((JToolBar.Separator)c).getSeparatorSize();
|
Dimension size = ((JToolBar.Separator)c).getSeparatorSize();
|
||||||
|
|||||||
@@ -201,6 +201,12 @@ public class FlatToolBarUI
|
|||||||
return FlatStylingSupport.getAnnotatedStyleableInfos( this );
|
return FlatStylingSupport.getAnnotatedStyleableInfos( this );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @since 2.5 */
|
||||||
|
@Override
|
||||||
|
public Object getStyleableValue( JComponent c, String key ) {
|
||||||
|
return FlatStylingSupport.getAnnotatedStyleableValue( this, key );
|
||||||
|
}
|
||||||
|
|
||||||
/** @since 1.4 */
|
/** @since 1.4 */
|
||||||
protected void setButtonsFocusable( boolean focusable ) {
|
protected void setButtonsFocusable( boolean focusable ) {
|
||||||
for( Component c : toolBar.getComponents() )
|
for( Component c : toolBar.getComponents() )
|
||||||
@@ -256,11 +262,15 @@ public class FlatToolBarUI
|
|||||||
default: return;
|
default: return;
|
||||||
}
|
}
|
||||||
|
|
||||||
for( int i = focusedCompIndex + add; i != focusedCompIndex; i += add ) {
|
int i = focusedCompIndex;
|
||||||
|
for(;;) {
|
||||||
|
i += add;
|
||||||
if( i < 0 )
|
if( i < 0 )
|
||||||
i = count - 1;
|
i = count - 1;
|
||||||
else if( i >= count )
|
else if( i >= count )
|
||||||
i = 0;
|
i = 0;
|
||||||
|
if( i == focusedCompIndex )
|
||||||
|
break;
|
||||||
|
|
||||||
Component c = toolBar.getComponentAtIndex( i );
|
Component c = toolBar.getComponentAtIndex( i );
|
||||||
if( canBeFocusOwner( c ) ) {
|
if( canBeFocusOwner( c ) ) {
|
||||||
@@ -282,7 +292,7 @@ public class FlatToolBarUI
|
|||||||
return comboBox.getUI().isFocusTraversable( comboBox );
|
return comboBox.getUI().isFocusTraversable( comboBox );
|
||||||
}
|
}
|
||||||
|
|
||||||
// check whether component has a empty input map to skip components that
|
// check whether component has an empty input map to skip components that
|
||||||
// are focusable, but do nothing when focused (e.g. JLabel)
|
// are focusable, but do nothing when focused (e.g. JLabel)
|
||||||
// see LayoutFocusTraversalPolicy.accept()
|
// see LayoutFocusTraversalPolicy.accept()
|
||||||
if( c instanceof JComponent ) {
|
if( c instanceof JComponent ) {
|
||||||
|
|||||||
@@ -340,6 +340,12 @@ public class FlatTreeUI
|
|||||||
return FlatStylingSupport.getAnnotatedStyleableInfos( this );
|
return FlatStylingSupport.getAnnotatedStyleableInfos( this );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @since 2.5 */
|
||||||
|
@Override
|
||||||
|
public Object getStyleableValue( JComponent c, String key ) {
|
||||||
|
return FlatStylingSupport.getAnnotatedStyleableValue( this, key );
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Same as super.paintRow(), but supports wide selection and uses
|
* Same as super.paintRow(), but supports wide selection and uses
|
||||||
* inactive selection background/foreground if tree is not focused.
|
* inactive selection background/foreground if tree is not focused.
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ import java.awt.Rectangle;
|
|||||||
import java.awt.RenderingHints;
|
import java.awt.RenderingHints;
|
||||||
import java.awt.Shape;
|
import java.awt.Shape;
|
||||||
import java.awt.Stroke;
|
import java.awt.Stroke;
|
||||||
|
import java.awt.SystemColor;
|
||||||
import java.awt.Window;
|
import java.awt.Window;
|
||||||
import java.awt.event.FocusEvent;
|
import java.awt.event.FocusEvent;
|
||||||
import java.awt.event.FocusListener;
|
import java.awt.event.FocusListener;
|
||||||
@@ -45,20 +46,27 @@ import java.util.WeakHashMap;
|
|||||||
import java.util.function.Predicate;
|
import java.util.function.Predicate;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
import javax.swing.JComponent;
|
import javax.swing.JComponent;
|
||||||
|
import javax.swing.JScrollPane;
|
||||||
import javax.swing.JTable;
|
import javax.swing.JTable;
|
||||||
|
import javax.swing.JTextField;
|
||||||
import javax.swing.LookAndFeel;
|
import javax.swing.LookAndFeel;
|
||||||
import javax.swing.SwingConstants;
|
import javax.swing.SwingConstants;
|
||||||
import javax.swing.SwingUtilities;
|
import javax.swing.SwingUtilities;
|
||||||
|
import javax.swing.UIDefaults;
|
||||||
import javax.swing.UIManager;
|
import javax.swing.UIManager;
|
||||||
import javax.swing.border.Border;
|
import javax.swing.border.Border;
|
||||||
import javax.swing.border.CompoundBorder;
|
import javax.swing.border.CompoundBorder;
|
||||||
import javax.swing.plaf.ComponentUI;
|
import javax.swing.plaf.ComponentUI;
|
||||||
import javax.swing.plaf.UIResource;
|
import javax.swing.plaf.UIResource;
|
||||||
import com.formdev.flatlaf.FlatClientProperties;
|
import com.formdev.flatlaf.FlatClientProperties;
|
||||||
|
import com.formdev.flatlaf.FlatIntelliJLaf;
|
||||||
|
import com.formdev.flatlaf.FlatLaf;
|
||||||
|
import com.formdev.flatlaf.FlatLightLaf;
|
||||||
import com.formdev.flatlaf.FlatSystemProperties;
|
import com.formdev.flatlaf.FlatSystemProperties;
|
||||||
import com.formdev.flatlaf.util.DerivedColor;
|
import com.formdev.flatlaf.util.DerivedColor;
|
||||||
import com.formdev.flatlaf.util.Graphics2DProxy;
|
import com.formdev.flatlaf.util.Graphics2DProxy;
|
||||||
import com.formdev.flatlaf.util.HiDPIUtils;
|
import com.formdev.flatlaf.util.HiDPIUtils;
|
||||||
|
import com.formdev.flatlaf.util.SystemInfo;
|
||||||
import com.formdev.flatlaf.util.UIScale;
|
import com.formdev.flatlaf.util.UIScale;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -71,7 +79,7 @@ public class FlatUIUtils
|
|||||||
public static final boolean MAC_USE_QUARTZ = Boolean.getBoolean( "apple.awt.graphics.UseQuartz" );
|
public static final boolean MAC_USE_QUARTZ = Boolean.getBoolean( "apple.awt.graphics.UseQuartz" );
|
||||||
|
|
||||||
private static boolean useSharedUIs = true;
|
private static boolean useSharedUIs = true;
|
||||||
private static WeakHashMap<LookAndFeel, IdentityHashMap<Object, ComponentUI>> sharedUIinstances = new WeakHashMap<>();
|
private static final WeakHashMap<LookAndFeel, IdentityHashMap<Object, ComponentUI>> sharedUIinstances = new WeakHashMap<>();
|
||||||
|
|
||||||
public static Rectangle addInsets( Rectangle r, Insets insets ) {
|
public static Rectangle addInsets( Rectangle r, Insets insets ) {
|
||||||
return new Rectangle(
|
return new Rectangle(
|
||||||
@@ -241,16 +249,53 @@ public class FlatUIUtils
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// invoke hasFocus() here because components may have overridden this method
|
||||||
|
// (e.g. Swing delegate components used for AWT components on macOS)
|
||||||
|
if( c.hasFocus() )
|
||||||
|
return true;
|
||||||
|
|
||||||
return keyboardFocusManager.getPermanentFocusOwner() == c &&
|
return keyboardFocusManager.getPermanentFocusOwner() == c &&
|
||||||
isInActiveWindow( c, keyboardFocusManager.getActiveWindow() );
|
isInActiveWindow( c, keyboardFocusManager.getActiveWindow() );
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean isInActiveWindow( Component c, Window activeWindow ) {
|
static boolean isInActiveWindow( Component c, Window activeWindow ) {
|
||||||
Window window = SwingUtilities.windowForComponent( c );
|
Window window = SwingUtilities.windowForComponent( c );
|
||||||
return window == activeWindow ||
|
return window == activeWindow ||
|
||||||
(window != null && window.getType() == Window.Type.POPUP && window.getOwner() == activeWindow);
|
(window != null && window.getType() == Window.Type.POPUP && window.getOwner() == activeWindow);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static boolean isAWTPeer( Component c ) {
|
||||||
|
// on macOS, Swing components are used for AWT components
|
||||||
|
if( SystemInfo.isMacOS )
|
||||||
|
return c.getClass().getName().startsWith( "sun.lwawt.LW" );
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether component is used as peer for AWT (on macOS) and
|
||||||
|
* whether a dark FlatLaf theme is active, which requires special handling
|
||||||
|
* because AWT always uses light colors.
|
||||||
|
*/
|
||||||
|
static boolean needsLightAWTPeer( JComponent c ) {
|
||||||
|
return FlatUIUtils.isAWTPeer( c ) && FlatLaf.isLafDark();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static UIDefaults lightAWTPeerDefaults;
|
||||||
|
|
||||||
|
static void runWithLightAWTPeerUIDefaults( Runnable runnable ) {
|
||||||
|
if( lightAWTPeerDefaults == null ) {
|
||||||
|
FlatLaf lightLaf = UIManager.getInt( "Component.focusWidth" ) >= 2
|
||||||
|
? new FlatIntelliJLaf()
|
||||||
|
: new FlatLightLaf();
|
||||||
|
lightAWTPeerDefaults = lightLaf.getDefaults();
|
||||||
|
}
|
||||||
|
|
||||||
|
FlatLaf.runWithUIDefaultsGetter( key -> {
|
||||||
|
Object value = lightAWTPeerDefaults.get( key );
|
||||||
|
return (value != null) ? value : FlatLaf.NULL_VALUE;
|
||||||
|
}, runnable );
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns whether the given component is in a window that is in full-screen mode.
|
* Returns whether the given component is in a window that is in full-screen mode.
|
||||||
*/
|
*/
|
||||||
@@ -335,7 +380,7 @@ public class FlatUIUtils
|
|||||||
*/
|
*/
|
||||||
public static Object[] setRenderingHints( Graphics g ) {
|
public static Object[] setRenderingHints( Graphics g ) {
|
||||||
Graphics2D g2 = (Graphics2D) g;
|
Graphics2D g2 = (Graphics2D) g;
|
||||||
Object[] oldRenderingHints = new Object[] {
|
Object[] oldRenderingHints = {
|
||||||
g2.getRenderingHint( RenderingHints.KEY_ANTIALIASING ),
|
g2.getRenderingHint( RenderingHints.KEY_ANTIALIASING ),
|
||||||
g2.getRenderingHint( RenderingHints.KEY_STROKE_CONTROL ),
|
g2.getRenderingHint( RenderingHints.KEY_STROKE_CONTROL ),
|
||||||
};
|
};
|
||||||
@@ -352,8 +397,10 @@ public class FlatUIUtils
|
|||||||
*/
|
*/
|
||||||
public static void resetRenderingHints( Graphics g, Object[] oldRenderingHints ) {
|
public static void resetRenderingHints( Graphics g, Object[] oldRenderingHints ) {
|
||||||
Graphics2D g2 = (Graphics2D) g;
|
Graphics2D g2 = (Graphics2D) g;
|
||||||
g2.setRenderingHint( RenderingHints.KEY_ANTIALIASING, oldRenderingHints[0] );
|
if( oldRenderingHints[0] != null )
|
||||||
g2.setRenderingHint( RenderingHints.KEY_STROKE_CONTROL, oldRenderingHints[1] );
|
g2.setRenderingHint( RenderingHints.KEY_ANTIALIASING, oldRenderingHints[0] );
|
||||||
|
if( oldRenderingHints[1] != null )
|
||||||
|
g2.setRenderingHint( RenderingHints.KEY_STROKE_CONTROL, oldRenderingHints[1] );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -376,7 +423,7 @@ public class FlatUIUtils
|
|||||||
}
|
}
|
||||||
|
|
||||||
Graphics2D g2 = (Graphics2D) g;
|
Graphics2D g2 = (Graphics2D) g;
|
||||||
Object[] oldRenderingHints2 = new Object[] {
|
Object[] oldRenderingHints2 = {
|
||||||
g2.getRenderingHint( RenderingHints.KEY_ANTIALIASING ),
|
g2.getRenderingHint( RenderingHints.KEY_ANTIALIASING ),
|
||||||
g2.getRenderingHint( RenderingHints.KEY_STROKE_CONTROL ),
|
g2.getRenderingHint( RenderingHints.KEY_STROKE_CONTROL ),
|
||||||
};
|
};
|
||||||
@@ -664,9 +711,9 @@ public class FlatUIUtils
|
|||||||
* is smaller than its bounds (for the focus decoration).
|
* is smaller than its bounds (for the focus decoration).
|
||||||
*/
|
*/
|
||||||
public static void paintParentBackground( Graphics g, JComponent c ) {
|
public static void paintParentBackground( Graphics g, JComponent c ) {
|
||||||
Container parent = findOpaqueParent( c );
|
Color background = getParentBackground( c );
|
||||||
if( parent != null ) {
|
if( background != null ) {
|
||||||
g.setColor( parent.getBackground() );
|
g.setColor( background );
|
||||||
g.fillRect( 0, 0, c.getWidth(), c.getHeight() );
|
g.fillRect( 0, 0, c.getWidth(), c.getHeight() );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -676,9 +723,20 @@ public class FlatUIUtils
|
|||||||
*/
|
*/
|
||||||
public static Color getParentBackground( JComponent c ) {
|
public static Color getParentBackground( JComponent c ) {
|
||||||
Container parent = findOpaqueParent( c );
|
Container parent = findOpaqueParent( c );
|
||||||
return (parent != null)
|
// parent.getBackground() may return null
|
||||||
? parent.getBackground()
|
// (e.g. for Swing delegate components used for AWT components on macOS)
|
||||||
: UIManager.getColor( "Panel.background" ); // fallback, probably never used
|
Color background = (parent != null) ? parent.getBackground() : null;
|
||||||
|
if( background != null )
|
||||||
|
return background;
|
||||||
|
|
||||||
|
if( isAWTPeer( c ) ) {
|
||||||
|
// AWT peers usually use component background, except for TextField and ScrollPane
|
||||||
|
return c instanceof JTextField || c instanceof JScrollPane || c.getBackground() == null
|
||||||
|
? SystemColor.window
|
||||||
|
: c.getBackground();
|
||||||
|
}
|
||||||
|
|
||||||
|
return UIManager.getColor( "Panel.background" );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -777,8 +835,8 @@ public class FlatUIUtils
|
|||||||
* {@link SwingConstants#WEST} or {@link SwingConstants#EAST})
|
* {@link SwingConstants#WEST} or {@link SwingConstants#EAST})
|
||||||
* @param chevron {@code true} for chevron arrow, {@code false} for triangle arrow
|
* @param chevron {@code true} for chevron arrow, {@code false} for triangle arrow
|
||||||
* @param arrowSize the width of the painted arrow (for vertical direction) (will be scaled)
|
* @param arrowSize the width of the painted arrow (for vertical direction) (will be scaled)
|
||||||
* @param xOffset a offset added to the x coordinate of the arrow to paint it out-of-center. Usually zero. (will be scaled)
|
* @param xOffset an offset added to the x coordinate of the arrow to paint it out-of-center. Usually zero. (will be scaled)
|
||||||
* @param yOffset a offset added to the y coordinate of the arrow to paint it out-of-center. Usually zero. (will be scaled)
|
* @param yOffset an offset added to the y coordinate of the arrow to paint it out-of-center. Usually zero. (will be scaled)
|
||||||
*
|
*
|
||||||
* @since 1.1
|
* @since 1.1
|
||||||
*/
|
*/
|
||||||
@@ -786,13 +844,15 @@ public class FlatUIUtils
|
|||||||
int direction, boolean chevron, int arrowSize, float xOffset, float yOffset )
|
int direction, boolean chevron, int arrowSize, float xOffset, float yOffset )
|
||||||
{
|
{
|
||||||
// compute arrow width/height
|
// compute arrow width/height
|
||||||
int aw = UIScale.scale( arrowSize + (chevron ? 0 : 1) );
|
// - make chevron arrows one pixel smaller because coordinates are based on center of pixels (0.5/0.5)
|
||||||
int ah = UIScale.scale( (arrowSize / 2) + (chevron ? 0 : 1) );
|
// - make triangle arrows one pixel taller (and round height up) to make them look stronger
|
||||||
|
float aw = UIScale.scale( arrowSize + (chevron ? -1 : 0) );
|
||||||
|
float ah = chevron ? (aw / 2) : UIScale.scale( (arrowSize / 2) + 1 );
|
||||||
|
|
||||||
// rotate arrow width/height for horizontal directions
|
// rotate arrow width/height for horizontal directions
|
||||||
boolean vert = (direction == SwingConstants.NORTH || direction == SwingConstants.SOUTH);
|
boolean vert = (direction == SwingConstants.NORTH || direction == SwingConstants.SOUTH);
|
||||||
if( !vert ) {
|
if( !vert ) {
|
||||||
int temp = aw;
|
float temp = aw;
|
||||||
aw = ah;
|
aw = ah;
|
||||||
ah = temp;
|
ah = temp;
|
||||||
}
|
}
|
||||||
@@ -804,19 +864,19 @@ public class FlatUIUtils
|
|||||||
// compute arrow location
|
// compute arrow location
|
||||||
float ox = ((width - (aw + extra)) / 2f) + UIScale.scale( xOffset );
|
float ox = ((width - (aw + extra)) / 2f) + UIScale.scale( xOffset );
|
||||||
float oy = ((height - (ah + extra)) / 2f) + UIScale.scale( yOffset );
|
float oy = ((height - (ah + extra)) / 2f) + UIScale.scale( yOffset );
|
||||||
int ax = x + ((direction == SwingConstants.WEST) ? -Math.round( -ox ) : Math.round( ox ));
|
float ax = x + ((direction == SwingConstants.WEST) ? -Math.round( -(ox + aw) ) - aw : Math.round( ox ));
|
||||||
int ay = y + ((direction == SwingConstants.NORTH) ? -Math.round( -oy ) : Math.round( oy ));
|
float ay = y + ((direction == SwingConstants.NORTH) ? -Math.round( -(oy + ah) ) - ah : Math.round( oy ));
|
||||||
|
|
||||||
// paint arrow
|
// paint arrow
|
||||||
g.translate( ax, ay );
|
g.translate( ax, ay );
|
||||||
/*debug
|
/*debug
|
||||||
debugPaintArrow( g, Color.red, vert, aw + extra, ah + extra );
|
debugPaintArrow( g, Color.red, vert, Math.round( aw + extra ), Math.round( ah + extra ) );
|
||||||
debug*/
|
debug*/
|
||||||
Shape arrowShape = createArrowShape( direction, chevron, aw, ah );
|
Shape arrowShape = createArrowShape( direction, chevron, aw, ah );
|
||||||
if( chevron ) {
|
if( chevron ) {
|
||||||
Stroke oldStroke = g.getStroke();
|
Stroke oldStroke = g.getStroke();
|
||||||
g.setStroke( new BasicStroke( UIScale.scale( 1f ) ) );
|
g.setStroke( new BasicStroke( UIScale.scale( 1f ) ) );
|
||||||
g.draw( arrowShape );
|
drawShapePure( g, arrowShape );
|
||||||
g.setStroke( oldStroke );
|
g.setStroke( oldStroke );
|
||||||
} else {
|
} else {
|
||||||
// triangle
|
// triangle
|
||||||
@@ -828,7 +888,7 @@ debug*/
|
|||||||
/**
|
/**
|
||||||
* Creates a chevron or triangle arrow shape for the given direction and size.
|
* Creates a chevron or triangle arrow shape for the given direction and size.
|
||||||
* <p>
|
* <p>
|
||||||
* The chevron shape is a open path that can be painted with {@link Graphics2D#draw(Shape)}.
|
* The chevron shape is an open path that can be painted with {@link Graphics2D#draw(Shape)}.
|
||||||
* The triangle shape is a close path that can be painted with {@link Graphics2D#fill(Shape)}.
|
* The triangle shape is a close path that can be painted with {@link Graphics2D#fill(Shape)}.
|
||||||
*
|
*
|
||||||
* @param direction the arrow direction ({@link SwingConstants#NORTH}, {@link SwingConstants#SOUTH}
|
* @param direction the arrow direction ({@link SwingConstants#NORTH}, {@link SwingConstants#SOUTH}
|
||||||
@@ -880,7 +940,7 @@ debug*/
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a open or closed path for the given points.
|
* Creates an open or closed path for the given points.
|
||||||
*/
|
*/
|
||||||
public static Path2D createPath( boolean close, double... points ) {
|
public static Path2D createPath( boolean close, double... points ) {
|
||||||
Path2D path = new Path2D.Float();
|
Path2D path = new Path2D.Float();
|
||||||
@@ -892,6 +952,23 @@ debug*/
|
|||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Draws the given shape with disabled stroke normalization.
|
||||||
|
* The x/y coordinates of the shape are translated by a half pixel.
|
||||||
|
*
|
||||||
|
* @since 2.1
|
||||||
|
*/
|
||||||
|
public static void drawShapePure( Graphics2D g, Shape shape ) {
|
||||||
|
Object oldStrokeControl = g.getRenderingHint( RenderingHints.KEY_STROKE_CONTROL );
|
||||||
|
g.setRenderingHint( RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE );
|
||||||
|
|
||||||
|
g.translate( 0.5, 0.5 );
|
||||||
|
g.draw( shape );
|
||||||
|
g.translate( -0.5, -0.5 );
|
||||||
|
|
||||||
|
g.setRenderingHint( RenderingHints.KEY_STROKE_CONTROL, oldStrokeControl );
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Draws the given string at the specified location.
|
* Draws the given string at the specified location.
|
||||||
* The provided component is used to query text properties and anti-aliasing hints.
|
* The provided component is used to query text properties and anti-aliasing hints.
|
||||||
|
|||||||
@@ -18,8 +18,8 @@ package com.formdev.flatlaf.ui;
|
|||||||
|
|
||||||
import java.awt.Component;
|
import java.awt.Component;
|
||||||
import java.awt.Graphics;
|
import java.awt.Graphics;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
import javax.swing.JComponent;
|
import javax.swing.JComponent;
|
||||||
import javax.swing.JTable;
|
|
||||||
import javax.swing.JViewport;
|
import javax.swing.JViewport;
|
||||||
import javax.swing.plaf.ComponentUI;
|
import javax.swing.plaf.ComponentUI;
|
||||||
import javax.swing.plaf.basic.BasicViewportUI;
|
import javax.swing.plaf.basic.BasicViewportUI;
|
||||||
@@ -43,15 +43,28 @@ public class FlatViewportUI
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void update( Graphics g, JComponent c ) {
|
public void paint( Graphics g, JComponent c ) {
|
||||||
Component view = ((JViewport)c).getView();
|
super.paint( g, c );
|
||||||
if( c.isOpaque() && view instanceof JTable ) {
|
|
||||||
// paint viewport background in same color as table background
|
|
||||||
g.setColor( view.getBackground() );
|
|
||||||
g.fillRect( 0, 0, c.getWidth(), c.getHeight() );
|
|
||||||
|
|
||||||
paint( g, c );
|
Component view = ((JViewport)c).getView();
|
||||||
} else
|
if( view instanceof JComponent ) {
|
||||||
super.update( g, c );
|
try {
|
||||||
|
Method m = view.getClass().getMethod( "getUI" );
|
||||||
|
Object ui = m.invoke( view );
|
||||||
|
if( ui instanceof ViewportPainter )
|
||||||
|
((ViewportPainter)ui).paintViewport( g, (JComponent) view, (JViewport) c );
|
||||||
|
} catch( Exception ex ) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//---- interface ViewportPainter ------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @since 2.3
|
||||||
|
*/
|
||||||
|
public interface ViewportPainter {
|
||||||
|
void paintViewport( Graphics g, JComponent c, JViewport viewport );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import java.awt.Dialog;
|
|||||||
import java.awt.Dimension;
|
import java.awt.Dimension;
|
||||||
import java.awt.Frame;
|
import java.awt.Frame;
|
||||||
import java.awt.Graphics;
|
import java.awt.Graphics;
|
||||||
|
import java.awt.GraphicsConfiguration;
|
||||||
import java.awt.Insets;
|
import java.awt.Insets;
|
||||||
import java.awt.Rectangle;
|
import java.awt.Rectangle;
|
||||||
import java.awt.Toolkit;
|
import java.awt.Toolkit;
|
||||||
@@ -42,7 +43,9 @@ import javax.swing.JComponent;
|
|||||||
import javax.swing.JInternalFrame;
|
import javax.swing.JInternalFrame;
|
||||||
import javax.swing.JLayeredPane;
|
import javax.swing.JLayeredPane;
|
||||||
import javax.swing.JRootPane;
|
import javax.swing.JRootPane;
|
||||||
|
import javax.swing.SwingUtilities;
|
||||||
import javax.swing.UIManager;
|
import javax.swing.UIManager;
|
||||||
|
import com.formdev.flatlaf.util.SystemInfo;
|
||||||
import com.formdev.flatlaf.util.UIScale;
|
import com.formdev.flatlaf.util.UIScale;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -231,8 +234,15 @@ public abstract class FlatWindowResizer
|
|||||||
{
|
{
|
||||||
protected Window window;
|
protected Window window;
|
||||||
|
|
||||||
|
private final boolean limitResizeToScreenBounds;
|
||||||
|
|
||||||
public WindowResizer( JRootPane rootPane ) {
|
public WindowResizer( JRootPane rootPane ) {
|
||||||
super( rootPane );
|
super( rootPane );
|
||||||
|
|
||||||
|
// On Linux, limit window resizing to screen bounds because otherwise
|
||||||
|
// there would be a strange effect when the mouse is moved over a sidebar
|
||||||
|
// while resizing and the opposite window side is also resized.
|
||||||
|
limitResizeToScreenBounds = SystemInfo.isLinux;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -289,11 +299,19 @@ public abstract class FlatWindowResizer
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean limitToParentBounds() {
|
protected boolean limitToParentBounds() {
|
||||||
return false;
|
return limitResizeToScreenBounds && window != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Rectangle getParentBounds() {
|
protected Rectangle getParentBounds() {
|
||||||
|
if( limitResizeToScreenBounds && window != null ) {
|
||||||
|
GraphicsConfiguration gc = window.getGraphicsConfiguration();
|
||||||
|
Rectangle bounds = gc.getBounds();
|
||||||
|
Insets insets = window.getToolkit().getScreenInsets( gc );
|
||||||
|
return new Rectangle( bounds.x + insets.left, bounds.y + insets.top,
|
||||||
|
bounds.width - insets.left - insets.right,
|
||||||
|
bounds.height - insets.top - insets.bottom );
|
||||||
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -385,7 +403,7 @@ public abstract class FlatWindowResizer
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Rectangle getParentBounds() {
|
protected Rectangle getParentBounds() {
|
||||||
return getFrame().getParent().getBounds();
|
return new Rectangle( getFrame().getParent().getSize() );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -504,7 +522,7 @@ debug*/
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void mousePressed( MouseEvent e ) {
|
public void mousePressed( MouseEvent e ) {
|
||||||
if( !isWindowResizable() )
|
if( !SwingUtilities.isLeftMouseButton( e ) || !isWindowResizable() )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
int xOnScreen = e.getXOnScreen();
|
int xOnScreen = e.getXOnScreen();
|
||||||
@@ -533,7 +551,7 @@ debug*/
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void mouseReleased( MouseEvent e ) {
|
public void mouseReleased( MouseEvent e ) {
|
||||||
if( !isWindowResizable() )
|
if( !SwingUtilities.isLeftMouseButton( e ) || !isWindowResizable() )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
dragLeftOffset = dragRightOffset = dragTopOffset = dragBottomOffset = 0;
|
dragLeftOffset = dragRightOffset = dragTopOffset = dragBottomOffset = 0;
|
||||||
@@ -559,7 +577,7 @@ debug*/
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void mouseDragged( MouseEvent e ) {
|
public void mouseDragged( MouseEvent e ) {
|
||||||
if( !isWindowResizable() )
|
if( !SwingUtilities.isLeftMouseButton( e ) || !isWindowResizable() )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
int xOnScreen = e.getXOnScreen();
|
int xOnScreen = e.getXOnScreen();
|
||||||
@@ -579,8 +597,8 @@ debug*/
|
|||||||
// top
|
// top
|
||||||
if( resizeDir == N_RESIZE_CURSOR || resizeDir == NW_RESIZE_CURSOR || resizeDir == NE_RESIZE_CURSOR ) {
|
if( resizeDir == N_RESIZE_CURSOR || resizeDir == NW_RESIZE_CURSOR || resizeDir == NE_RESIZE_CURSOR ) {
|
||||||
newBounds.y = yOnScreen - dragTopOffset;
|
newBounds.y = yOnScreen - dragTopOffset;
|
||||||
if( limitToParentBounds() && newBounds.y < 0 )
|
if( limitToParentBounds() )
|
||||||
newBounds.y = 0;
|
newBounds.y = Math.max( newBounds.y, getParentBounds().y );
|
||||||
newBounds.height += (oldBounds.y - newBounds.y);
|
newBounds.height += (oldBounds.y - newBounds.y);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -597,8 +615,8 @@ debug*/
|
|||||||
// left
|
// left
|
||||||
if( resizeDir == W_RESIZE_CURSOR || resizeDir == NW_RESIZE_CURSOR || resizeDir == SW_RESIZE_CURSOR ) {
|
if( resizeDir == W_RESIZE_CURSOR || resizeDir == NW_RESIZE_CURSOR || resizeDir == SW_RESIZE_CURSOR ) {
|
||||||
newBounds.x = xOnScreen - dragLeftOffset;
|
newBounds.x = xOnScreen - dragLeftOffset;
|
||||||
if( limitToParentBounds() && newBounds.x < 0 )
|
if( limitToParentBounds() )
|
||||||
newBounds.x = 0;
|
newBounds.x = Math.max( newBounds.x, getParentBounds().x );
|
||||||
newBounds.width += (oldBounds.x - newBounds.x);
|
newBounds.width += (oldBounds.x - newBounds.x);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -27,7 +27,6 @@ import java.awt.Window;
|
|||||||
import java.awt.geom.AffineTransform;
|
import java.awt.geom.AffineTransform;
|
||||||
import java.beans.PropertyChangeEvent;
|
import java.beans.PropertyChangeEvent;
|
||||||
import java.beans.PropertyChangeListener;
|
import java.beans.PropertyChangeListener;
|
||||||
import java.io.File;
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.IdentityHashMap;
|
import java.util.IdentityHashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -38,9 +37,7 @@ import javax.swing.Timer;
|
|||||||
import javax.swing.event.ChangeEvent;
|
import javax.swing.event.ChangeEvent;
|
||||||
import javax.swing.event.ChangeListener;
|
import javax.swing.event.ChangeListener;
|
||||||
import javax.swing.event.EventListenerList;
|
import javax.swing.event.EventListenerList;
|
||||||
import com.formdev.flatlaf.FlatSystemProperties;
|
|
||||||
import com.formdev.flatlaf.util.LoggingFacade;
|
import com.formdev.flatlaf.util.LoggingFacade;
|
||||||
import com.formdev.flatlaf.util.NativeLibrary;
|
|
||||||
import com.formdev.flatlaf.util.SystemInfo;
|
import com.formdev.flatlaf.util.SystemInfo;
|
||||||
|
|
||||||
//
|
//
|
||||||
@@ -84,7 +81,6 @@ class FlatWindowsNativeWindowBorder
|
|||||||
private Color colorizationColor;
|
private Color colorizationColor;
|
||||||
private int colorizationColorBalance;
|
private int colorizationColorBalance;
|
||||||
|
|
||||||
private static NativeLibrary nativeLibrary;
|
|
||||||
private static FlatWindowsNativeWindowBorder instance;
|
private static FlatWindowsNativeWindowBorder instance;
|
||||||
|
|
||||||
static FlatNativeWindowBorder.Provider getInstance() {
|
static FlatNativeWindowBorder.Provider getInstance() {
|
||||||
@@ -96,31 +92,8 @@ class FlatWindowsNativeWindowBorder
|
|||||||
if( !SystemInfo.isX86 && !SystemInfo.isX86_64 )
|
if( !SystemInfo.isX86 && !SystemInfo.isX86_64 )
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
// load native library
|
|
||||||
if( nativeLibrary == null ) {
|
|
||||||
if( !SystemInfo.isJava_9_orLater ) {
|
|
||||||
// In Java 8, load jawt.dll (part of JRE) explicitly because it
|
|
||||||
// is not found when running application with <jdk>/bin/java.exe.
|
|
||||||
// When using <jdk>/jre/bin/java.exe, it is found.
|
|
||||||
// jawt.dll is located in <jdk>/jre/bin/.
|
|
||||||
// Java 9 and later does not have this problem.
|
|
||||||
try {
|
|
||||||
System.loadLibrary( "jawt" );
|
|
||||||
} catch( UnsatisfiedLinkError ex ) {
|
|
||||||
// log error only if native library jawt.dll not already loaded
|
|
||||||
String message = ex.getMessage();
|
|
||||||
if( message == null || !message.contains( "already loaded in another classloader" ) )
|
|
||||||
LoggingFacade.INSTANCE.logSevere( null, ex );
|
|
||||||
} catch( Exception ex ) {
|
|
||||||
LoggingFacade.INSTANCE.logSevere( null, ex );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
nativeLibrary = createNativeLibrary();
|
|
||||||
}
|
|
||||||
|
|
||||||
// check whether native library was successfully loaded
|
// check whether native library was successfully loaded
|
||||||
if( !nativeLibrary.isLoaded() )
|
if( !FlatNativeLibrary.isLoaded() )
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
// create new instance
|
// create new instance
|
||||||
@@ -129,23 +102,6 @@ class FlatWindowsNativeWindowBorder
|
|||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static NativeLibrary createNativeLibrary() {
|
|
||||||
String libraryName = "flatlaf-windows-x86";
|
|
||||||
if( SystemInfo.isX86_64 )
|
|
||||||
libraryName += "_64";
|
|
||||||
|
|
||||||
String libraryPath = System.getProperty( FlatSystemProperties.NATIVE_LIBRARY_PATH );
|
|
||||||
if( libraryPath != null ) {
|
|
||||||
File libraryFile = new File( libraryPath, libraryName + ".dll" );
|
|
||||||
if( libraryFile.exists() )
|
|
||||||
return new NativeLibrary( libraryFile, true );
|
|
||||||
else
|
|
||||||
LoggingFacade.INSTANCE.logSevere( "Did not find external library " + libraryFile + ", using extracted library instead", null );
|
|
||||||
}
|
|
||||||
|
|
||||||
return new NativeLibrary( "com/formdev/flatlaf/natives/" + libraryName, null, true );
|
|
||||||
}
|
|
||||||
|
|
||||||
private FlatWindowsNativeWindowBorder() {
|
private FlatWindowsNativeWindowBorder() {
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -155,7 +111,7 @@ class FlatWindowsNativeWindowBorder
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tell the window whether the application wants use custom decorations.
|
* Tell the window whether the application wants to use custom decorations.
|
||||||
* If {@code true}, the Windows 10 title bar is hidden (including minimize,
|
* If {@code true}, the Windows 10 title bar is hidden (including minimize,
|
||||||
* maximize and close buttons), but not the resize borders (including drop shadow).
|
* maximize and close buttons), but not the resize borders (including drop shadow).
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -195,11 +195,13 @@ public class JBRCustomDecorations
|
|||||||
{
|
{
|
||||||
private static JBRWindowTopBorder instance;
|
private static JBRWindowTopBorder instance;
|
||||||
|
|
||||||
private final Color defaultActiveBorder = new Color( 0x707070 );
|
private final Color activeLightColor = new Color( 0x707070 );
|
||||||
|
private final Color activeDarkColor = new Color( 0x2D2E2F );
|
||||||
private final Color inactiveLightColor = new Color( 0xaaaaaa );
|
private final Color inactiveLightColor = new Color( 0xaaaaaa );
|
||||||
|
private final Color inactiveDarkColor = new Color( 0x494A4B );
|
||||||
|
|
||||||
private boolean colorizationAffectsBorders;
|
private boolean colorizationAffectsBorders;
|
||||||
private Color activeColor = defaultActiveBorder;
|
private Color activeColor;
|
||||||
|
|
||||||
static JBRWindowTopBorder getInstance() {
|
static JBRWindowTopBorder getInstance() {
|
||||||
if( instance == null )
|
if( instance == null )
|
||||||
@@ -250,7 +252,7 @@ public class JBRCustomDecorations
|
|||||||
|
|
||||||
private Color calculateActiveBorderColor() {
|
private Color calculateActiveBorderColor() {
|
||||||
if( !colorizationAffectsBorders )
|
if( !colorizationAffectsBorders )
|
||||||
return defaultActiveBorder;
|
return null;
|
||||||
|
|
||||||
Color colorizationColor = getColorizationColor();
|
Color colorizationColor = getColorizationColor();
|
||||||
if( colorizationColor != null ) {
|
if( colorizationColor != null ) {
|
||||||
@@ -284,16 +286,12 @@ public class JBRCustomDecorations
|
|||||||
@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 ) {
|
||||||
Window window = SwingUtilities.windowForComponent( c );
|
Window window = SwingUtilities.windowForComponent( c );
|
||||||
boolean active = (window != null) ? window.isActive() : false;
|
boolean active = window != null && window.isActive();
|
||||||
|
boolean dark = FlatLaf.isLafDark();
|
||||||
|
|
||||||
// paint top border
|
g.setColor( active
|
||||||
// - in light themes
|
? (activeColor != null ? activeColor : (dark ? activeDarkColor : activeLightColor))
|
||||||
// - in dark themes only for active windows if colorization affects borders
|
: (dark ? inactiveDarkColor : inactiveLightColor) );
|
||||||
boolean paintTopBorder = !FlatLaf.isLafDark() || (active && colorizationAffectsBorders);
|
|
||||||
if( !paintTopBorder )
|
|
||||||
return;
|
|
||||||
|
|
||||||
g.setColor( active ? activeColor : inactiveLightColor );
|
|
||||||
HiDPIUtils.paintAtScale1x( (Graphics2D) g, x, y, width, height, this::paintImpl );
|
HiDPIUtils.paintAtScale1x( (Graphics2D) g, x, y, width, height, this::paintImpl );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -143,8 +143,8 @@ public class MigLayoutVisualPadding
|
|||||||
//---- class FlatMigInsets ------------------------------------------------
|
//---- class FlatMigInsets ------------------------------------------------
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Marker class to identify our visual paddings and leaf paddings,
|
* Marker class to identify our visual paddings and leave paddings
|
||||||
* which were set from outside, untouched.
|
* set from outside untouched.
|
||||||
*/
|
*/
|
||||||
private static class FlatMigInsets
|
private static class FlatMigInsets
|
||||||
extends Insets
|
extends Insets
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ import com.formdev.flatlaf.util.Animator.Interpolator;
|
|||||||
* </pre>
|
* </pre>
|
||||||
*
|
*
|
||||||
* Animation works only if the component passed to {@link #paintIcon(Component, Graphics, int, int)}
|
* Animation works only if the component passed to {@link #paintIcon(Component, Graphics, int, int)}
|
||||||
* is a instance of {@link JComponent}.
|
* is an instance of {@link JComponent}.
|
||||||
* A client property is set on the component to store the animation state.
|
* A client property is set on the component to store the animation state.
|
||||||
*
|
*
|
||||||
* @author Karl Tauber
|
* @author Karl Tauber
|
||||||
@@ -68,7 +68,7 @@ public interface AnimatedIcon
|
|||||||
extends Icon
|
extends Icon
|
||||||
{
|
{
|
||||||
@Override
|
@Override
|
||||||
public default void paintIcon( Component c, Graphics g, int x, int y ) {
|
default void paintIcon( Component c, Graphics g, int x, int y ) {
|
||||||
AnimationSupport.paintIcon( this, c, g, x, y );
|
AnimationSupport.paintIcon( this, c, g, x, y );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ public class DerivedColor
|
|||||||
Color result = ColorFunctions.applyFunctions( baseColor, functions );
|
Color result = ColorFunctions.applyFunctions( baseColor, functions );
|
||||||
|
|
||||||
// if the result is equal to the default color, then the original base color
|
// 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
|
// was passed, and we can cache this to avoid color calculations
|
||||||
if( !hasBaseOfDefaultColor && result.getRGB() == this.getRGB() ) {
|
if( !hasBaseOfDefaultColor && result.getRGB() == this.getRGB() ) {
|
||||||
hasBaseOfDefaultColor = true;
|
hasBaseOfDefaultColor = true;
|
||||||
baseOfDefaultColorRGB = baseColor.getRGB();
|
baseOfDefaultColorRGB = baseColor.getRGB();
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ public class HSLColor
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a HSLColor object using an an array containing the
|
* Create a HSLColor object using an array containing the
|
||||||
* individual HSL values and with a default alpha value of 1.
|
* individual HSL values and with a default alpha value of 1.
|
||||||
*
|
*
|
||||||
* @param hsl array containing HSL values
|
* @param hsl array containing HSL values
|
||||||
@@ -87,7 +87,7 @@ public class HSLColor
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a HSLColor object using an an array containing the
|
* Create a HSLColor object using an array containing the
|
||||||
* individual HSL values.
|
* individual HSL values.
|
||||||
*
|
*
|
||||||
* @param hsl array containing HSL values
|
* @param hsl array containing HSL values
|
||||||
@@ -291,7 +291,7 @@ public class HSLColor
|
|||||||
|
|
||||||
// Calculate the Saturation
|
// Calculate the Saturation
|
||||||
|
|
||||||
float s = 0;
|
float s;
|
||||||
|
|
||||||
if (max == min)
|
if (max == min)
|
||||||
s = 0;
|
s = 0;
|
||||||
@@ -386,7 +386,7 @@ public class HSLColor
|
|||||||
s /= 100f;
|
s /= 100f;
|
||||||
l /= 100f;
|
l /= 100f;
|
||||||
|
|
||||||
float q = 0;
|
float q;
|
||||||
|
|
||||||
if (l < 0.5)
|
if (l < 0.5)
|
||||||
q = l * (1 + s);
|
q = l * (1 + s);
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ import com.formdev.flatlaf.FlatSystemProperties;
|
|||||||
public class HiDPIUtils
|
public class HiDPIUtils
|
||||||
{
|
{
|
||||||
public interface Painter {
|
public interface Painter {
|
||||||
public void paint( Graphics2D g, int x, int y, int width, int height, double scaleFactor );
|
void paint( Graphics2D g, int x, int y, int width, int height, double scaleFactor );
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void paintAtScale1x( Graphics2D g, JComponent c, Painter painter ) {
|
public static void paintAtScale1x( Graphics2D g, JComponent c, Painter painter ) {
|
||||||
@@ -48,30 +48,55 @@ public class HiDPIUtils
|
|||||||
*/
|
*/
|
||||||
public static void paintAtScale1x( Graphics2D g, int x, int y, int width, int height, Painter painter ) {
|
public static void paintAtScale1x( Graphics2D g, int x, int y, int width, int height, Painter painter ) {
|
||||||
// save original transform
|
// save original transform
|
||||||
AffineTransform transform = g.getTransform();
|
AffineTransform t = g.getTransform();
|
||||||
|
|
||||||
|
// get scale X/Y and shear X/Y
|
||||||
|
double scaleX = t.getScaleX();
|
||||||
|
double scaleY = t.getScaleY();
|
||||||
|
double shearX = t.getShearX();
|
||||||
|
double shearY = t.getShearY();
|
||||||
|
|
||||||
|
// check whether rotated
|
||||||
|
// (also check for negative scale X/Y because shear X/Y are zero for 180 degrees rotation)
|
||||||
|
boolean rotated = (shearX != 0 || shearY != 0 || scaleX <= 0 || scaleY <= 0);
|
||||||
|
if( rotated ) {
|
||||||
|
// resulting scale X/Y values are always positive
|
||||||
|
scaleX = Math.hypot( scaleX, shearX );
|
||||||
|
scaleY = Math.hypot( scaleY, shearY );
|
||||||
|
} else {
|
||||||
|
// make scale X/Y positive
|
||||||
|
scaleX = Math.abs( scaleX );
|
||||||
|
scaleY = Math.abs( scaleY );
|
||||||
|
}
|
||||||
|
|
||||||
// check whether scaled
|
// check whether scaled
|
||||||
if( transform.getScaleX() == 1 && transform.getScaleY() == 1 ) {
|
if( scaleX == 1 && scaleY == 1 ) {
|
||||||
painter.paint( g, x, y, width, height, 1 );
|
painter.paint( g, x, y, width, height, 1 );
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// scale rectangle
|
// scale rectangle
|
||||||
Rectangle2D.Double scaledRect = scale( transform, x, y, width, height );
|
Rectangle2D.Double scaledRect = scale( scaleX, scaleY, t, x, y, width, height );
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// unscale to factor 1.0 and move origin (to whole numbers)
|
// unscale to factor 1.0, keep rotation and move origin (to whole numbers)
|
||||||
g.setTransform( new AffineTransform( 1, 0, 0, 1,
|
AffineTransform t1x;
|
||||||
Math.floor( scaledRect.x ), Math.floor( scaledRect.y ) ) );
|
if( rotated ) {
|
||||||
|
t1x = new AffineTransform( t.getScaleX(), t.getShearY(), t.getShearX(), t.getScaleY(),
|
||||||
|
Math.floor( scaledRect.x ), Math.floor( scaledRect.y ) );
|
||||||
|
t1x.scale( 1. / scaleX, 1. / scaleY );
|
||||||
|
} else
|
||||||
|
t1x = new AffineTransform( 1, 0, 0, 1, Math.floor( scaledRect.x ), Math.floor( scaledRect.y ) );
|
||||||
|
g.setTransform( t1x );
|
||||||
|
|
||||||
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, transform.getScaleX() );
|
painter.paint( g, 0, 0, swidth, sheight, scaleX );
|
||||||
} finally {
|
} finally {
|
||||||
// restore original transform
|
// restore original transform
|
||||||
g.setTransform( transform );
|
g.setTransform( t );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -80,20 +105,16 @@ public class HiDPIUtils
|
|||||||
* sun.java2d.pipe.PixelToParallelogramConverter.fillRectangle(),
|
* sun.java2d.pipe.PixelToParallelogramConverter.fillRectangle(),
|
||||||
* which is used by Graphics.fillRect().
|
* which is used by Graphics.fillRect().
|
||||||
*/
|
*/
|
||||||
private static Rectangle2D.Double scale( AffineTransform transform, int x, int y, int width, int height ) {
|
private static Rectangle2D.Double scale( double scaleX, double scaleY, AffineTransform t, int x, int y, int width, int height ) {
|
||||||
double dx1 = transform.getScaleX();
|
double px = (x * scaleX) + t.getTranslateX();
|
||||||
double dy2 = transform.getScaleY();
|
double py = (y * scaleY) + t.getTranslateY();
|
||||||
double px = x * dx1 + transform.getTranslateX();
|
|
||||||
double py = y * dy2 + transform.getTranslateY();
|
|
||||||
dx1 *= width;
|
|
||||||
dy2 *= height;
|
|
||||||
|
|
||||||
double newx = normalize( px );
|
double newX = normalize( px );
|
||||||
double newy = normalize( py );
|
double newY = normalize( py );
|
||||||
dx1 = normalize( px + dx1 ) - newx;
|
double newWidth = normalize( px + (width * scaleX) ) - newX;
|
||||||
dy2 = normalize( py + dy2 ) - newy;
|
double newHeight = normalize( py + (height * scaleY) ) - newY;
|
||||||
|
|
||||||
return new Rectangle2D.Double( newx, newy, dx1, dy2 );
|
return new Rectangle2D.Double( newX, newY, newWidth, newHeight );
|
||||||
}
|
}
|
||||||
|
|
||||||
private static double normalize( double value ) {
|
private static double normalize( double value ) {
|
||||||
@@ -114,7 +135,7 @@ public class HiDPIUtils
|
|||||||
* painted too far down on some operating systems.
|
* painted too far down on some operating systems.
|
||||||
* The higher the system scale factor is, the more.
|
* The higher the system scale factor is, the more.
|
||||||
* <p>
|
* <p>
|
||||||
* This methods computes a correction value for the Y position.
|
* This method computes a correction value for the Y position.
|
||||||
*/
|
*/
|
||||||
public static float computeTextYCorrection( Graphics2D g ) {
|
public static float computeTextYCorrection( Graphics2D g ) {
|
||||||
if( !useTextYCorrection() || !SystemInfo.isWindows )
|
if( !useTextYCorrection() || !SystemInfo.isWindows )
|
||||||
|
|||||||
@@ -19,8 +19,9 @@ package com.formdev.flatlaf.util;
|
|||||||
import java.awt.FontMetrics;
|
import java.awt.FontMetrics;
|
||||||
import java.awt.Graphics;
|
import java.awt.Graphics;
|
||||||
import java.awt.Graphics2D;
|
import java.awt.Graphics2D;
|
||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.invoke.MethodHandle;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.invoke.MethodHandles;
|
||||||
|
import java.lang.invoke.MethodType;
|
||||||
import javax.swing.JComponent;
|
import javax.swing.JComponent;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -32,8 +33,8 @@ import javax.swing.JComponent;
|
|||||||
*/
|
*/
|
||||||
public class JavaCompatibility
|
public class JavaCompatibility
|
||||||
{
|
{
|
||||||
private static Method drawStringUnderlineCharAtMethod;
|
private static MethodHandle drawStringUnderlineCharAtMethod;
|
||||||
private static Method getClippedStringMethod;
|
private static MethodHandle getClippedStringMethod;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Java 8: sun.swing.SwingUtilities2.drawStringUnderlineCharAt( JComponent c,
|
* Java 8: sun.swing.SwingUtilities2.drawStringUnderlineCharAt( JComponent c,
|
||||||
@@ -51,9 +52,10 @@ public class JavaCompatibility
|
|||||||
Class<?> cls = Class.forName( SystemInfo.isJava_9_orLater
|
Class<?> cls = Class.forName( SystemInfo.isJava_9_orLater
|
||||||
? "javax.swing.plaf.basic.BasicGraphicsUtils"
|
? "javax.swing.plaf.basic.BasicGraphicsUtils"
|
||||||
: "sun.swing.SwingUtilities2" );
|
: "sun.swing.SwingUtilities2" );
|
||||||
drawStringUnderlineCharAtMethod = cls.getMethod( "drawStringUnderlineCharAt", SystemInfo.isJava_9_orLater
|
MethodType mt = MethodType.methodType( void.class, SystemInfo.isJava_9_orLater
|
||||||
? 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 } );
|
||||||
|
drawStringUnderlineCharAtMethod = MethodHandles.publicLookup().findStatic( cls, "drawStringUnderlineCharAt", mt );
|
||||||
} catch( Exception ex ) {
|
} catch( Exception ex ) {
|
||||||
LoggingFacade.INSTANCE.logSevere( null, ex );
|
LoggingFacade.INSTANCE.logSevere( null, ex );
|
||||||
throw new RuntimeException( ex );
|
throw new RuntimeException( ex );
|
||||||
@@ -63,10 +65,10 @@ public class JavaCompatibility
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
if( SystemInfo.isJava_9_orLater )
|
if( SystemInfo.isJava_9_orLater )
|
||||||
drawStringUnderlineCharAtMethod.invoke( null, c, g, text, underlinedIndex, (float) x, (float) y );
|
drawStringUnderlineCharAtMethod.invoke( c, (Graphics2D) g, text, underlinedIndex, (float) x, (float) y );
|
||||||
else
|
else
|
||||||
drawStringUnderlineCharAtMethod.invoke( null, c, g, text, underlinedIndex, x, y );
|
drawStringUnderlineCharAtMethod.invoke( c, g, text, underlinedIndex, x, y );
|
||||||
} catch( IllegalAccessException | IllegalArgumentException | InvocationTargetException ex ) {
|
} catch( Throwable ex ) {
|
||||||
LoggingFacade.INSTANCE.logSevere( null, ex );
|
LoggingFacade.INSTANCE.logSevere( null, ex );
|
||||||
throw new RuntimeException( ex );
|
throw new RuntimeException( ex );
|
||||||
}
|
}
|
||||||
@@ -86,10 +88,11 @@ public class JavaCompatibility
|
|||||||
Class<?> cls = Class.forName( SystemInfo.isJava_9_orLater
|
Class<?> cls = Class.forName( SystemInfo.isJava_9_orLater
|
||||||
? "javax.swing.plaf.basic.BasicGraphicsUtils"
|
? "javax.swing.plaf.basic.BasicGraphicsUtils"
|
||||||
: "sun.swing.SwingUtilities2" );
|
: "sun.swing.SwingUtilities2" );
|
||||||
getClippedStringMethod = cls.getMethod( SystemInfo.isJava_9_orLater
|
MethodType mt = MethodType.methodType( String.class, JComponent.class, FontMetrics.class, String.class, int.class );
|
||||||
|
getClippedStringMethod = MethodHandles.publicLookup().findStatic( cls, SystemInfo.isJava_9_orLater
|
||||||
? "getClippedString"
|
? "getClippedString"
|
||||||
: "clipStringIfNecessary",
|
: "clipStringIfNecessary",
|
||||||
new Class[] { JComponent.class, FontMetrics.class, String.class, int.class } );
|
mt );
|
||||||
} catch( Exception ex ) {
|
} catch( Exception ex ) {
|
||||||
LoggingFacade.INSTANCE.logSevere( null, ex );
|
LoggingFacade.INSTANCE.logSevere( null, ex );
|
||||||
throw new RuntimeException( ex );
|
throw new RuntimeException( ex );
|
||||||
@@ -98,8 +101,8 @@ public class JavaCompatibility
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return (String) getClippedStringMethod.invoke( null, c, fm, string, availTextWidth );
|
return (String) getClippedStringMethod.invoke( c, fm, string, availTextWidth );
|
||||||
} catch( IllegalAccessException | IllegalArgumentException | InvocationTargetException ex ) {
|
} catch( Throwable ex ) {
|
||||||
LoggingFacade.INSTANCE.logSevere( null, ex );
|
LoggingFacade.INSTANCE.logSevere( null, ex );
|
||||||
throw new RuntimeException( ex );
|
throw new RuntimeException( ex );
|
||||||
}
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user