Compare commits

...

94 Commits
2.0.2 ... 2.4

Author SHA1 Message Date
Karl Tauber
f842530537 release 2.4 2022-07-13 23:43:02 +02:00
Karl Tauber
63077bbb19 Merge PR #565: Window title bar usability improvements (Windows 10/11 only) 2022-07-13 23:28:38 +02:00
Karl Tauber
4dad337377 Window decorations: fixed app icon hit test bounds if icon is shown beside title 2022-07-13 23:11:32 +02:00
Karl Tauber
10a965d765 Window decorations: option to show window icon beside window title, if menu bar is embedded or title is centered 2022-07-13 17:58:25 +02:00
Karl Tauber
3e9c9c9066 execute FlatLaf.initialize() and uninitialize() only for current laf
For NetBeans GUI builder, which invokes `FlatLaf.initialize()` multiple times for preview, but never invokes `FlatLaf.uninitialize()`.

(https://github.com/apache/netbeans/issues/4231)
2022-07-12 12:13:19 +02:00
Karl Tauber
8b5a738e65 Menus: avoid that SubMenuUsabilityHelper can be installed multiple times, which can freeze the application caused pushing multiple event queues and popping wrong event queue first
(e.g. NetBeans Form Editor invokes `FlatLaf.initialize()` but not `uninitialize()`)

(PR #490; https://github.com/apache/netbeans/issues/4231)
2022-07-12 10:33:53 +02:00
Karl Tauber
2c041dce3a Window decorations: add small resize area at top of embedded menu bar only if frame is resizable 2022-07-11 17:47:04 +02:00
Karl Tauber
ef151c68f4 Window decorations:
- improved title bar usability by using larger gaps and minimum sizes
- added minimum gap between embedded menu bar and window title
- fixed oscillating title while resizing window width
- fixed lost right-to-left component orientation in title bar when switching Laf
2022-07-11 17:28:30 +02:00
Karl Tauber
52feaac92a Window decorations: no longer reduce height of window title bar if it has an embedded menu bar and is maximized 2022-07-10 14:03:45 +02:00
Karl Tauber
cddbb3d7d4 Window decorations: make sure that a horizontal glue in embedded menu bar has a minimum width and is always visible 2022-07-10 13:57:37 +02:00
Karl Tauber
42764550e6 Window decorations: improved window title bar layout for small window widths:
- width of iconify/maximize/close buttons is reduced to give more space to embedded menu bar and title
- window title now has a minimum width to always allow moving window
2022-07-09 19:54:29 +02:00
Karl Tauber
6ee737b314 Window decorations: small area at top of embedded menu bar to resize window 2022-07-09 10:30:33 +02:00
Karl Tauber
f460ef7685 UI defaults dumps updated for commits b82ee2ef61 and 93e0496fd2 2022-07-09 10:19:54 +02:00
Karl Tauber
9977bcb468 Window decorations: 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) 2022-07-09 00:04:51 +02:00
Karl Tauber
7437d984c7 Theme Editor: accept @ as identifier character, which includes it in selection when double clicking e.g. on @background 2022-07-08 17:53:49 +02:00
Karl Tauber
5cd0b2403c Theme Editor: find/replace bar improvements:
- always use editor selection to search if `Ctrl+F` is pressed
- keep find/replace bar open if switching to another editor
- mark matches when switching to another editor
2022-07-08 17:52:08 +02:00
Karl Tauber
a372da22f3 Extras: FlatInspector:
- support embedding into SWT
- added "MigLayout visual padding" to tooltip

fixed typo in MigLayoutVisualPadding.java
2022-07-04 11:09:06 +02:00
Karl Tauber
8b10d3ba5a Native window decorations: fixed missing top window border in dark themes if window drop shadows are disabled in system settings (issue #554) 2022-07-02 23:26:34 +02:00
Karl Tauber
a8b15c6a12 MenuItem: fixed sometimes wrapped HTML text on HiDPI screens on Windows 2022-07-02 22:38:37 +02:00
Karl Tauber
23bac7e5fd Native window decorations: do not use window decorations if system property sun.java2d.opengl is true on Windows 10 (issue #540) 2022-07-02 00:29:29 +02:00
Karl Tauber
b82ee2ef61 Typography: no longer use Consolas or Courier New as monospaced font on Windows because they have bad vertically placement 2022-07-02 00:25:07 +02:00
Karl Tauber
b7761f4b71 HiDPIUtils: support rotated graphics (issue #557) 2022-07-01 15:34:04 +02:00
Karl Tauber
f9a4f9771c Testing: FlatPaintingStringTest:
- added "Fonts" combobox to test various fonts
- reworked/fixed text painting/sizing to get correct results
2022-06-07 11:03:34 +02:00
Karl Tauber
d2acb2c98a HiDPIUtils: reimplemented HiDPIUtils.scale() to make it easier to read and more understandable
(no longer re-using dx1 and dy2 variables for different kind of values)
2022-06-05 23:43:13 +02:00
Karl Tauber
d60bd5df14 FlatEmptyBorder: fixed possible NPE if passed component is null 2022-06-05 00:46:43 +02:00
Karl Tauber
73b6ca3762 ComboBox: fixed vertical alignment of text in popup list with text in combo box in IntelliJ/Darcula themes 2022-06-04 20:15:31 +02:00
Karl Tauber
6c18431a30 TableHeader: fixed exception when changing table structure (e.g. removing column) from a table header popup menu action (issue #532) 2022-05-31 18:56:06 +02:00
Karl Tauber
a49d20249f Gradle: do not set Multi-Release: true in META-INF/MANIFEST.MF if not needed 2022-05-31 15:35:56 +02:00
Karl Tauber
ad384acd57 updated sigtest for FlatLaf 2.3
(generated in clean workspace with gradle task `sigtestGenerate`)
2022-05-28 18:43:37 +02:00
Karl Tauber
69851b7f3a release 2.3 2022-05-28 17:53:54 +02:00
Karl Tauber
92b53bf0df Merge PR #522: File chooser shortcuts panel 2022-05-28 17:46:12 +02:00
Karl Tauber
93e0496fd2 ToggleButton: button style "tab": added missing foreground colors for hover, focused and selected states (issue #535) 2022-05-28 15:09:04 +02:00
Karl Tauber
5151951f46 Button and ToggleButton: added missing foreground colors for hover, pressed, focused and selected states (issue #535) 2022-05-28 14:54:31 +02:00
Karl Tauber
58dbccec2d Table: optionally paint alternating rows below table if table is smaller than scroll pane (issue #504) 2022-05-25 11:18:22 +02:00
Karl Tauber
90de14d013 Native library: refactored loading of Windows native library from FlatWindowsNativeWindowBorder to FlatNativeLibrary to make it easier to add native libraries for other platforms (for issues #204 and #482) 2022-05-17 20:24:28 +02:00
Karl Tauber
5f961618bf Demo and Theme Editor: updated macOS related comments 2022-05-13 13:54:10 +02:00
Karl Tauber
37c375e2fa Theme Editor:
- support "themes" sub-directory
- added "generate Java class" checkbox to "New" dialog
2022-05-10 11:01:45 +02:00
Karl Tauber
1758c175ed FlatLafUIKeys.txt: added some missing UI defaults 2022-05-09 23:30:34 +02:00
Karl Tauber
96f2a02cfa UIDefaultsLoader: added over() color function to convert a translucent color into a solid color based on any background color 2022-05-09 23:28:40 +02:00
Karl Tauber
96d4bda6c8 Demo: hide accent color buttons (instead of disabling them) if not supported by selected theme 2022-05-09 22:51:38 +02:00
Karl Tauber
02cf6050a1 updates for PR #530:
- added @since tags
- changed `FlatToggleButton.setTabUnderlinePlacement()` implementation so that is behaves similar to `FlatTabbedPane.setTabIconPlacement()`
2022-05-09 22:28:34 +02:00
Karl Tauber
38cf32a2e9 Merge PR #530: ToggleButton: made underline placement configurable 2022-05-09 22:08:42 +02:00
Karl Tauber
2ae7589d14 Merge PR #525: Create Bundle_es.properties 2022-05-09 21:28:47 +02:00
Julien Fischer
bcb2e1f0a1 ToggleButton: made underline placement configurable
Supported values: TOP, LEFT, BOTTOM, or RIGHT
2022-05-06 12:07:50 +02:00
Karl Tauber
14932d3f07 Theme Editor: on macOS use apple.awt.fullWindowContent and apple.awt.transparentTitleBar 2022-05-05 13:20:23 +02:00
Karl Tauber
c3b9dc397d Demo: on macOS use apple.awt.fullWindowContent and apple.awt.transparentTitleBar 2022-05-01 11:45:36 +02:00
Karl Tauber
58b653f55d updated sigtest for FlatLaf 2.2
(generated in clean workspace with gradle task `sigtestGenerate`)
2022-05-01 11:44:51 +02:00
Jesús Marín
1dcdc42dde Create Bundle_es.properties
Flatlaf Spanish translation
2022-04-29 14:51:11 +01:00
Karl Tauber
58a0a16985 IntelliJ Themes: fixed TitledBorder text color in "Monokai Pro" theme (issue #524) 2022-04-28 15:16:26 +02:00
Karl Tauber
a117243f14 FileChooser: use large system icons in shortcuts panel also in Java 8 to 16 2022-04-26 22:50:34 +02:00
Karl Tauber
22411060be FileChooser: improve layout for shortcuts panel (give it full height) 2022-04-25 23:49:15 +02:00
Karl Tauber
045263ae58 FileChooser: added (optional) shortcuts panel (issue #100) 2022-04-25 23:06:10 +02:00
Karl Tauber
024b6daaf6 release 2.2 2022-04-25 19:36:43 +02:00
Karl Tauber
bd5512c121 SplitPane: allow limiting one-touch expanding to a single side (issue #355) 2022-04-23 17:13:32 +02:00
Karl Tauber
9afce83a02 SplitPane: added missing BasicSplitPaneDivider properties to javadoc 2022-04-23 16:19:04 +02:00
Karl Tauber
07a8bd9486 ComboBox: added missing BasicComboPopup properties to javadoc 2022-04-22 10:55:25 +02:00
Karl Tauber
bcdc0a8fce IntelliJ Themes: added "Monokai Pro" and "Xcode-Dark" themes 2022-04-21 22:03:05 +02:00
Karl Tauber
b295809432 IntelliJ Themes: updated themes to newest versions (used IJThemesUpdater) 2022-04-21 22:02:09 +02:00
Karl Tauber
52763ab932 GitHub Actions:
- natives.yml: include core natives in artifacts
- updated versions of used actions
2022-04-21 14:18:27 +02:00
Karl Tauber
99666265c9 gradle:
- use gradle `cpp-library` plugin instead of 3rd party plugin
- build natives only via task `build-natives`
2022-04-21 12:58:16 +02:00
Karl Tauber
af3e280d74 Table: slightly changed grid colors to make grid better recognizable (issue #514) 2022-04-19 23:00:01 +02:00
Karl Tauber
b57e4c0565 TabbedPane: selected tab underline color now changes depending on whether the focus is within the tab content (issue #398) 2022-04-19 22:19:47 +02:00
Karl Tauber
aca9931560 IntelliJ Themes: TabbedPane: use DefaultTabs.underlinedTabBackground and DefaultTabs.underlinedTabForeground from JSON themes for selected tab background/foreground 2022-04-19 16:50:27 +02:00
Karl Tauber
d09e166e4a SplitPane: fixed StackOverflowError caused by layout loop that may occur under special circumstances (issue #513) 2022-04-12 13:47:04 +02:00
Karl Tauber
68a7a60ff2 FileChooser: enabled full row selection for details view to fix alternate row coloring (issue #512) 2022-04-12 13:28:39 +02:00
Karl Tauber
f21261914b gradle: build target flatlaf-natives-windows only on Windows
(to fix build error on macOS)
2022-04-09 18:34:36 +02:00
Karl Tauber
7b11339fdc update to Gradle 7.4.2
./gradlew wrapper --gradle-version=7.4.2
2022-04-09 18:18:45 +02:00
Karl Tauber
081fd43d98 IntelliJ Themes: Component.accentColor UI property now has useful theme specific values (issue #507) 2022-04-07 18:07:09 +02:00
Karl Tauber
ef2eedfc7c Button: fixed icon layout and preferred width of default buttons that use bold font (issue #506) 2022-04-06 23:36:58 +02:00
Karl Tauber
0dba9265be ToolBar: fixed endless loop in focus navigation that may occur under special circumstances (issue #505) 2022-04-06 18:53:45 +02:00
Karl Tauber
301aae9b8e NativeLibrary: use System.mapLibraryName() instead of own implementation 2022-03-19 11:07:46 +01:00
Karl Tauber
c63f4e9662 Window decorations on Linux: limit window resizing/moving to left mouse button (issue #482) 2022-03-18 00:05:15 +01:00
Karl Tauber
47508dc6ac Native window decorations: updated DLLs (issue #502)
built by GitHub Actions:
https://github.com/JFormDesigner/FlatLaf/actions/runs/2000978687
2022-03-17 22:48:01 +01:00
Karl Tauber
3a8879608a Native window decorations: fixed wrong window title character encoding used in Windows taskbar (issue #502) 2022-03-17 22:31:18 +01:00
Karl Tauber
b221889549 updated sigtest for FlatLaf 2.1
(generated in clean workspace with gradle task `sigtestGenerate`)
2022-03-17 18:34:44 +01:00
Karl Tauber
c00d99b85f release 2.1 2022-03-17 12:52:49 +01:00
Karl Tauber
0bf87b753d TabbedPane: disable all items in "Show Hidden Tabs" popup menu if tabbed pane is disabled 2022-03-17 12:42:49 +01:00
Karl Tauber
53f2730064 TextArea, TextPane and EditorPane: no longer select all text when component is focused for the first time (issue #498; regression in FlatLaf 2.0) 2022-03-17 12:21:06 +01:00
Karl Tauber
d487c3b005 JIDE: use FlatLaf menu scrolling for JidePopupMenu (issue #225) 2022-03-14 12:23:52 +01:00
Karl Tauber
fef6ae7ff7 Menus: scroll large menus using mouse wheel or up/down arrows (issue #225) 2022-03-14 11:41:05 +01:00
Karl Tauber
f6b42754de Testing: FlatScreenshotsBackground: also make title bar white/black 2022-03-14 11:20:09 +01:00
Karl Tauber
2ae9bb381d Menus: fixed IllegalComponentStateException: component must be showing on the screen to determine its location when submenu is empty (PR #490; issue #247) 2022-03-14 00:23:53 +01:00
Karl Tauber
53bde84710 fixed compiler warning 2022-03-13 19:17:41 +01:00
Karl Tauber
d006ac27ff Merge PR #490: Menus: improved usability of submenus 2022-03-13 19:07:59 +01:00
Karl Tauber
c478d28b71 PasswordField: fixed reveal button appearance in IntelliJ themes (issue #494) 2022-03-13 18:39:12 +01:00
Karl Tauber
99f7b9ad84 ScrollBar:
- added `ScrollBar.minimumButtonSize` to specify minimum scroll arrow button size
- center and scale arrows in scroll up/down buttons

(issue #493)
2022-03-13 10:58:27 +01:00
Karl Tauber
ab58101ce3 SwingX: test JXStatusBar (issue #492) 2022-03-06 11:41:54 +01:00
Karl Tauber
d8f3682dc0 Menus: improved usability of submenus (issue #247) 2022-02-28 14:45:57 +01:00
Karl Tauber
1fec7ba553 Linux: support using custom window decorations (issue #482) 2022-02-26 23:07:16 +01:00
Karl Tauber
418f55f34e Window decorations: fixed window resizing on Linux (issue #482) 2022-02-26 14:00:16 +01:00
Karl Tauber
05d795b2ae Window decorations: use special fix for maximized bounds only on Windows (issue #469) 2022-02-26 13:33:55 +01:00
Karl Tauber
a365b750d9 core: minor code cleanup:
- add final where possible
- removed "public" from interface methods
- simplified conditional expressions
- removed unnecessary unboxing
- removed unused assignements
- removed redundant casts

(used IntelliJ IDEA 2021.3 inspections)
2022-02-25 21:49:15 +01:00
Karl Tauber
0aecfb565f IntelliJ Themes: removed duplicate key and trailing spaces 2022-02-25 21:12:48 +01:00
Karl Tauber
0cf4edd9e5 core: fixed typos/grammar in comments 2022-02-25 20:40:37 +01:00
162 changed files with 19919 additions and 3435 deletions

View File

@@ -28,13 +28,13 @@ jobs:
- 17 # LTS
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- uses: gradle/wrapper-validation-action@v1
if: matrix.java == '8'
- name: Setup Java ${{ matrix.java }}
uses: actions/setup-java@v2
uses: actions/setup-java@v3
with:
java-version: ${{ matrix.java }}
distribution: adopt # Java 8 and 11 are pre-installed on ubuntu-latest
@@ -44,7 +44,7 @@ jobs:
run: ./gradlew build
- name: Upload artifacts
uses: actions/upload-artifact@v2
uses: actions/upload-artifact@v3
if: matrix.java == '11'
with:
name: FlatLaf-build-artifacts
@@ -63,10 +63,10 @@ jobs:
github.repository == 'JFormDesigner/FlatLaf'
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- name: Setup Java 11
uses: actions/setup-java@v2
uses: actions/setup-java@v3
with:
java-version: 11
distribution: adopt # pre-installed on ubuntu-latest
@@ -99,10 +99,10 @@ jobs:
github.repository == 'JFormDesigner/FlatLaf'
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- name: Setup Java 11
uses: actions/setup-java@v2
uses: actions/setup-java@v3
with:
java-version: 11
distribution: adopt # pre-installed on ubuntu-latest

View File

@@ -25,12 +25,12 @@ jobs:
runs-on: windows-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- uses: gradle/wrapper-validation-action@v1
- name: Setup Java 11
uses: actions/setup-java@v2
uses: actions/setup-java@v3
with:
java-version: 11
distribution: adopt
@@ -39,11 +39,12 @@ jobs:
- name: Build with Gradle
# --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
run: ./gradlew :flatlaf-natives-windows:build --no-daemon
run: ./gradlew :flatlaf-natives-windows:build-natives --no-daemon
- name: Upload artifacts
uses: actions/upload-artifact@v2
uses: actions/upload-artifact@v3
with:
name: FlatLaf-natives-windows-build-artifacts
path: |
flatlaf-core/src/main/resources/com/formdev/flatlaf/natives
flatlaf-natives/flatlaf-natives-windows/build

View File

@@ -1,6 +1,133 @@
FlatLaf Change Log
==================
## 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
@@ -80,7 +207,7 @@ FlatLaf Change Log
- Possibility to hide window title bar icon (for single window set client
property `JRootPane.titleBarShowIcon` to `false`; for all windows set UI
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)
- No longer show the Java "duke/cup" icon if no window icon image is set.
(issue #416)

View File

@@ -14,8 +14,8 @@
* limitations under the License.
*/
val releaseVersion = "2.0.2"
val developmentVersion = "2.1-SNAPSHOT"
val releaseVersion = "2.4"
val developmentVersion = "2.5-SNAPSHOT"
version = if( java.lang.Boolean.getBoolean( "release" ) ) releaseVersion else developmentVersion

View File

@@ -61,8 +61,6 @@ if( JavaVersion.current() >= JavaVersion.VERSION_1_9 ) {
}
jar {
manifest.attributes( "Multi-Release" to "true" )
from( sourceSets["module-info"].output ) {
include( "module-info.class" )
}

View File

@@ -43,11 +43,6 @@ tasks {
options.headerOutputDirectory.set( buildDir.resolve( "generated/jni-headers" ) )
}
processResources {
// build native libraries
dependsOn( ":flatlaf-natives-windows:assemble" )
}
jar {
archiveBaseName.set( "flatlaf" )

View File

@@ -1,5 +1,5 @@
#Signature file v4.1
#Version 2.0
#Version 2.3
CLSS public abstract interface com.formdev.flatlaf.FlatClientProperties
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_NEVER = "never"
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 STYLE = "FlatLaf.style"
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_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_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_LEADING_COMPONENT = "JTextField.leadingComponent"
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 unregisterUIDefaultsGetter(java.util.function.Function<java.lang.Object,java.lang.Object>)
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,subMenuUsabilityHelper,uiDefaultsGetters,updateUIPending
hcls ActiveFont,FlatUIDefaults,ImageIconUIResource
CLSS public abstract interface static com.formdev.flatlaf.FlatLaf$DisabledIconProvider
@@ -662,6 +666,7 @@ CLSS public com.formdev.flatlaf.util.SystemInfo
cons public init()
fld public final static boolean isAARCH64
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_17_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 isKDE
fld public final static boolean isLinux
fld public final static boolean isMacFullWindowContentSupported
fld public final static boolean isMacOS
fld public final static boolean isMacOS_10_11_ElCapitan_orLater
fld public final static boolean isMacOS_10_14_Mojave_orLater

View File

@@ -391,6 +391,40 @@ public interface FlatClientProperties
*/
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 --------------------------------------------------------
/**
@@ -958,7 +992,22 @@ public interface FlatClientProperties
//---- 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>
* <strong>Component</strong> {@link javax.swing.JToggleButton}<br>
* <strong>Value type</strong> {@link java.lang.Integer}

View File

@@ -26,7 +26,7 @@ import javax.swing.UIDefaults;
* Allows loading of additional .properties files from addon JARs.
* {@link java.util.ServiceLoader} is used to load extensions of this class from addon JARs.
* <p>
* If you extend this class in a addon JAR, you also have to add a text file named
* If you extend this class in an addon JAR, you also have to add a text file named
* {@code META-INF/services/com.formdev.flatlaf.FlatDefaultsAddon}
* to the addon JAR. The file must contain a single line with the class name.
* <p>
@@ -61,7 +61,7 @@ public abstract class FlatDefaultsAddon
/**
* 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.
* Returns 10000 by default.
*/

View File

@@ -19,7 +19,7 @@ package com.formdev.flatlaf;
/**
* Default color palette for action icons and object icons.
* <p>
* The idea is to use only this well defined set of colors in SVG icons and
* The idea is to use only this well-defined set of colors in SVG icons, and
* then they are replaced at runtime to dark variants or to other theme colors.
* Then a single SVG icon (light variant) can be used for dark themes too.
* IntelliJ Platform uses this mechanism to allow themes to change IntelliJ Platform icons.
@@ -35,7 +35,7 @@ package com.formdev.flatlaf;
* <p>
* You may use these colors also in your application (outside of SVG icons), but do
* not use the RGB values defined in this enum.<br>
* Instead use {@code UIManager.getColor( FlatIconColors.ACTIONS_GREY.key )}.
* Instead, use {@code UIManager.getColor( FlatIconColors.ACTIONS_GREY.key )}.
*
* @author Karl Tauber
*/

View File

@@ -596,7 +596,7 @@ class FlatInputMaps
//---- class LazyInputMapEx -----------------------------------------------
/**
* Lazily creates a input map.
* Lazily creates an input map.
* Similar to {@link UIDefaults.LazyInputMap}, but can use multiple bindings arrays.
*/
private static class LazyInputMapEx

View File

@@ -103,6 +103,7 @@ public abstract class FlatLaf
private PopupFactory oldPopupFactory;
private MnemonicHandler mnemonicHandler;
private boolean subMenuUsabilityHelperInstalled;
private Consumer<UIDefaults> postInitialization;
private List<Function<Object, Object>> uiDefaultsGetters;
@@ -166,18 +167,19 @@ public abstract class FlatLaf
* Returns whether FlatLaf supports custom window decorations.
* This depends on the operating system and on the used Java runtime.
* <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>
* Returns also {@code false} on Windows 10 if:
* Returns also {@code false} on Windows 10/11 if:
* <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
* <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>)
* and JBR supports custom window decorations
* </li>
* </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
* {@link JDialog#setDefaultLookAndFeelDecorated(boolean)} is not necessary.
*/
@@ -190,7 +192,7 @@ public abstract class FlatLaf
FlatNativeWindowBorder.isSupported() )
return false;
return SystemInfo.isWindows_10_orLater;
return SystemInfo.isWindows_10_orLater || SystemInfo.isLinux;
}
@Override
@@ -230,6 +232,15 @@ public abstract class FlatLaf
@Override
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 )
initializeAqua();
@@ -243,6 +254,9 @@ public abstract class FlatLaf
mnemonicHandler = new MnemonicHandler();
mnemonicHandler.install();
// install submenu usability helper
subMenuUsabilityHelperInstalled = SubMenuUsabilityHelper.install();
// listen to desktop property changes to update UI if system font or scaling changes
if( SystemInfo.isWindows ) {
// Windows 10 allows increasing font size independent of scaling:
@@ -298,6 +312,10 @@ public abstract class FlatLaf
@Override
public void uninitialize() {
// do not uninitialize if this is not the current look and feel
if( UIManager.getLookAndFeel() != this )
return;
// remove desktop property listener
if( desktopPropertyListener != null ) {
Toolkit toolkit = Toolkit.getDefaultToolkit();
@@ -322,6 +340,12 @@ public abstract class FlatLaf
mnemonicHandler = null;
}
// uninstall submenu usability helper
if( subMenuUsabilityHelperInstalled ) {
SubMenuUsabilityHelper.uninstall();
subMenuUsabilityHelperInstalled = false;
}
// restore default link color
new HTMLEditorKit().getStyleSheet().addRule( "a, address { color: blue; }" );
postInitialization = null;
@@ -605,7 +629,7 @@ public abstract class FlatLaf
uiFont = ((ActiveFont)defaultFont).derive( baseFont, fontSize -> {
return Math.round( fontSize * UIScale.computeFontScaleFactor( baseFont ) );
} );
};
}
// increase font size if system property "flatlaf.uiScale" is set
uiFont = UIScale.applyCustomScaleFactor( uiFont );
@@ -760,7 +784,7 @@ public abstract class FlatLaf
* Invoke this method before setting the look and feel.
* <p>
* 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")
*/
@@ -862,7 +886,7 @@ public abstract class FlatLaf
* E.g. using {@link UIManager#setLookAndFeel(LookAndFeel)} or {@link #setup(LookAndFeel)}.
* <p>
* 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)}.
* <p>
* The keys and values are strings in same format as in FlatLaf properties files.
@@ -894,7 +918,7 @@ public abstract class FlatLaf
* E.g. using {@link UIManager#setLookAndFeel(LookAndFeel)} or {@link #setup(LookAndFeel)}.
* <p>
* 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)}.
* <p>
* The keys and values are strings in same format as in FlatLaf properties files.
@@ -951,7 +975,7 @@ public abstract class FlatLaf
// re-set current LaF
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 );
for( PropertyChangeListener l : UIManager.getPropertyChangeListeners() )
l.propertyChange( e );
@@ -995,7 +1019,7 @@ public abstract class FlatLaf
/**
* Returns whether native window decorations are supported on current platform.
* <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.
* If system property {@link FlatSystemProperties#USE_WINDOW_DECORATIONS} is set to
* {@code false}, then this method also returns {@code false}.

View File

@@ -34,7 +34,7 @@ public interface FlatSystemProperties
* To replace the Java 9+ system scale factor, use system property "sun.java2d.uiScale",
* which has the same syntax as this one.
* <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>
* <strong>Allowed Values</strong> e.g. {@code 1.5}, {@code 1.5x}, {@code 150%} or {@code 144dpi} (96dpi is 100%)<br>
*/

View File

@@ -37,6 +37,7 @@ import com.formdev.flatlaf.json.ParseException;
import com.formdev.flatlaf.util.ColorFunctions;
import com.formdev.flatlaf.util.LoggingFacade;
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.
@@ -162,8 +163,11 @@ public class IntelliJTheme
applyCheckBoxColors( defaults );
// copy values
for( Map.Entry<String, String> e : uiKeyCopying.entrySet() )
defaults.put( e.getKey(), defaults.get( e.getValue() ) );
for( Map.Entry<String, String> e : uiKeyCopying.entrySet() ) {
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
Object panelBackground = defaults.get( "Panel.background" );
@@ -175,7 +179,7 @@ public class IntelliJTheme
defaults.put( "Button.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 helpButtonBorderColor = defaults.get( "Button.startBorderColor" );
if( helpButtonBackground == null )
@@ -242,7 +246,19 @@ public class IntelliJTheme
defaults.put( "Tree.rowHeight", 22 );
// 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 ) {
@@ -299,8 +315,19 @@ public class IntelliJTheme
@SuppressWarnings( "unchecked" )
private void apply( String key, Object value, UIDefaults defaults, ArrayList<Object> defaultsKeysCache, Set<String> uiKeys ) {
if( value instanceof Map ) {
for( Map.Entry<String, Object> e : ((Map<String, Object>)value).entrySet() )
apply( key + '.' + e.getKey(), e.getValue(), defaults, defaultsKeysCache, uiKeys );
Map<String, Object> map = (Map<String, Object>)value;
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 {
if( "".equals( value ) )
return; // ignore empty value
@@ -534,12 +561,12 @@ public class IntelliJTheme
}
/** 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). */
private static Map<String, String> uiKeyCopying = new HashMap<>();
private static Map<String, String> uiKeyInverseMapping = new HashMap<>();
private static Map<String, String> checkboxKeyMapping = new HashMap<>();
private static Map<String, String> checkboxDuplicateColors = new HashMap<>();
private static final Map<String, String> uiKeyCopying = new HashMap<>();
private static final Map<String, String> uiKeyInverseMapping = new HashMap<>();
private static final Map<String, String> checkboxKeyMapping = new HashMap<>();
private static final Map<String, String> checkboxDuplicateColors = new HashMap<>();
static {
// ComboBox
@@ -600,6 +627,11 @@ public class IntelliJTheme
uiKeyCopying.put( "Spinner.buttonSeparatorColor", "Component.borderColor" );
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
uiKeyCopying.put( "TitlePane.inactiveBackground", "TitlePane.background" );
uiKeyMapping.put( "TitlePane.infoForeground", "TitlePane.foreground" );

View File

@@ -170,7 +170,7 @@ class LinuxFontPolicy
Object value = Toolkit.getDefaultToolkit().getDesktopProperty( "gnome.Xft/DPI" );
if( value instanceof Integer ) {
int dpi = ((Integer)value).intValue() / 1024;
int dpi = (Integer) value / 1024;
if( dpi == -1 )
dpi = 96;
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
* (System Settings > Fonts > Force Font DPI). A application restart is necessary.
@@ -278,7 +278,7 @@ class LinuxFontPolicy
// read config file
ArrayList<String> lines = new ArrayList<>( 200 );
try( BufferedReader reader = new BufferedReader( new FileReader( file ) ) ) {
String line = null;
String line;
while( (line = reader.readLine()) != null )
lines.add( line );
} catch( IOException ex ) {

View File

@@ -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, new Integer( 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() );
}
}
}

View File

@@ -350,7 +350,7 @@ class UIDefaultsLoader
enum ValueType { UNKNOWN, STRING, BOOLEAN, CHARACTER, INTEGER, INTEGERORFLOAT, FLOAT, BORDER, ICON, INSETS, DIMENSION, COLOR, FONT,
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<String, ValueType> knownValueTypes;
@@ -466,6 +466,10 @@ class UIDefaultsLoader
if( knownValueTypes == null ) {
// create lazy
knownValueTypes = new HashMap<>();
// system colors
knownValueTypes.put( "activeCaptionBorder", ValueType.COLOR );
knownValueTypes.put( "inactiveCaptionBorder", ValueType.COLOR );
knownValueTypes.put( "windowBorder", ValueType.COLOR );
// SplitPane
knownValueTypes.put( "SplitPane.dividerSize", ValueType.INTEGER );
knownValueTypes.put( "SplitPaneDivider.gripDotSize", ValueType.INTEGER );
@@ -780,6 +784,7 @@ class UIDefaultsLoader
case "tint": return parseColorMix( "#fff", params, resolver, reportError );
case "shade": return parseColorMix( "#000", params, resolver, reportError );
case "contrast": return parseColorContrast( params, resolver, reportError );
case "over": return parseColorOver( params, resolver, reportError );
}
} finally {
parseColorDepth--;
@@ -792,7 +797,7 @@ class UIDefaultsLoader
* Syntax: if(condition,trueValue,falseValue)
* <p>
* 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 ) {
if( params.size() != 3 )
@@ -847,7 +852,7 @@ class UIDefaultsLoader
int lightness = parsePercentage( params.get( 2 ) );
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 ) );
}
@@ -1041,6 +1046,33 @@ class UIDefaultsLoader
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 Object 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;
Color foreground2 = new Color( 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,
boolean derived, Function<String, String> resolver, boolean reportError )
{
@@ -1209,7 +1241,7 @@ class UIDefaultsLoader
}
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 + ')' );
return integer;
}
@@ -1250,28 +1282,28 @@ class UIDefaultsLoader
private static ActiveValue parseScaledInteger( String value ) {
int val = parseInteger( value, true );
return (ActiveValue) t -> {
return t -> {
return UIScale.scale( val );
};
}
private static ActiveValue parseScaledFloat( String value ) {
float val = parseFloat( value, true );
return (ActiveValue) t -> {
return t -> {
return UIScale.scale( val );
};
}
private static ActiveValue parseScaledInsets( String value ) {
Insets insets = parseInsets( value );
return (ActiveValue) t -> {
return t -> {
return UIScale.scale( insets );
};
}
private static ActiveValue parseScaledDimension( String value ) {
Dimension dimension = parseDimension( value );
return (ActiveValue) t -> {
return t -> {
return UIScale.scale( dimension );
};
}

View File

@@ -23,13 +23,13 @@ import java.awt.Graphics2D;
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.
* <p>
* Subclasses do not need to scale icon painting.
* <p>
* 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.
*
* @author Karl Tauber

View File

@@ -51,7 +51,7 @@ public class FlatAscendingSortIcon
boolean chevron = this.chevron;
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.
JTableHeader tableHeader = (JTableHeader) SwingUtilities.getAncestorOfClass( JTableHeader.class, c );
if( tableHeader != null ) {

View File

@@ -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.
*/
static <T> T getStyleFromTreeUI( Component c, Function<FlatTreeUI, T> f ) {

View File

@@ -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.
* Otherwise the compiler outputs a warning because this package is opend in module-info.java.
* Also when using --patch-module (e.g. from an IDE), an error would occur for empty packages.
* 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.
*
* @author Karl Tauber
*/

View File

@@ -37,7 +37,7 @@ public class FlatArrowButton
extends BasicArrowButton
implements UIResource
{
public static final int DEFAULT_ARROW_WIDTH = 8;
public static final int DEFAULT_ARROW_WIDTH = 9;
protected boolean chevron;
protected Color foreground;
@@ -211,6 +211,6 @@ public class FlatArrowButton
if( vert && parent instanceof JComponent && FlatUIUtils.hasRoundBorder( (JComponent) parent ) )
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() );
}
}

View File

@@ -159,7 +159,7 @@ public class FlatButtonBorder
public Insets getBorderInsets( Component c, Insets insets ) {
if( FlatButtonUI.isToolBarButton( c ) ) {
// 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)
? ((AbstractButton)c).getMargin()
: null;

View File

@@ -42,6 +42,7 @@ import javax.swing.JTextField;
import javax.swing.JToggleButton;
import javax.swing.JToolBar;
import javax.swing.LookAndFeel;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
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.endBackground Color optional; if set, a gradient paint is used
* @uiDefault Button.focusedBackground Color optional
* @uiDefault Button.focusedForeground Color optional
* @uiDefault Button.hoverBackground Color optional
* @uiDefault Button.hoverForeground Color optional
* @uiDefault Button.pressedBackground Color optional
* @uiDefault Button.pressedForeground Color optional
* @uiDefault Button.selectedBackground Color
* @uiDefault Button.selectedForeground Color
* @uiDefault Button.disabledBackground Color optional
* @uiDefault Button.disabledText Color
* @uiDefault Button.disabledSelectedBackground Color
* @uiDefault Button.disabledSelectedForeground Color optional
* @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.endBackground Color optional; if set, a gradient paint is used
* @uiDefault Button.default.foreground Color
* @uiDefault Button.default.focusedBackground Color optional
* @uiDefault Button.default.focusedForeground Color optional
* @uiDefault Button.default.hoverBackground Color optional
* @uiDefault Button.default.hoverForeground Color optional
* @uiDefault Button.default.pressedBackground Color optional
* @uiDefault Button.default.pressedForeground Color optional
* @uiDefault Button.default.boldText boolean
* @uiDefault Button.paintShadow boolean default is false
* @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.toolbar.spacingInsets Insets
* @uiDefault Button.toolbar.hoverBackground Color
* @uiDefault Button.toolbar.hoverForeground Color optional
* @uiDefault Button.toolbar.pressedBackground Color
* @uiDefault Button.toolbar.pressedForeground Color optional
* @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
*/
@@ -116,20 +129,27 @@ public class FlatButtonUI
protected Color startBackground;
protected Color endBackground;
@Styleable protected Color focusedBackground;
/** @since 2.3 */ @Styleable protected Color focusedForeground;
@Styleable protected Color hoverBackground;
/** @since 2.3 */ @Styleable protected Color hoverForeground;
@Styleable protected Color pressedBackground;
/** @since 2.3 */ @Styleable protected Color pressedForeground;
@Styleable protected Color selectedBackground;
@Styleable protected Color selectedForeground;
@Styleable protected Color disabledBackground;
@Styleable protected Color disabledText;
@Styleable protected Color disabledSelectedBackground;
/** @since 2.3 */ @Styleable protected Color disabledSelectedForeground;
@Styleable(dot=true) protected Color defaultBackground;
protected Color defaultEndBackground;
@Styleable(dot=true) protected Color defaultForeground;
@Styleable(dot=true) protected Color defaultFocusedBackground;
/** @since 2.3 */ @Styleable(dot=true) protected Color defaultFocusedForeground;
@Styleable(dot=true) protected Color defaultHoverBackground;
/** @since 2.3 */ @Styleable(dot=true) protected Color defaultHoverForeground;
@Styleable(dot=true) protected Color defaultPressedBackground;
/** @since 2.3 */ @Styleable(dot=true) protected Color defaultPressedForeground;
@Styleable(dot=true) protected boolean defaultBoldText;
@Styleable protected boolean paintShadow;
@@ -138,8 +158,13 @@ public class FlatButtonUI
@Styleable(dot=true) protected Color defaultShadowColor;
@Styleable(dot=true) protected Color toolbarHoverBackground;
/** @since 2.3 */ @Styleable(dot=true) protected Color toolbarHoverForeground;
@Styleable(dot=true) protected Color toolbarPressedBackground;
/** @since 2.3 */ @Styleable(dot=true) protected Color toolbarPressedForeground;
@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)
/** @since 2 */ @Styleable protected String buttonType;
@@ -189,20 +214,27 @@ public class FlatButtonUI
startBackground = UIManager.getColor( prefix + "startBackground" );
endBackground = UIManager.getColor( prefix + "endBackground" );
focusedBackground = UIManager.getColor( prefix + "focusedBackground" );
focusedForeground = UIManager.getColor( prefix + "focusedForeground" );
hoverBackground = UIManager.getColor( prefix + "hoverBackground" );
hoverForeground = UIManager.getColor( prefix + "hoverForeground" );
pressedBackground = UIManager.getColor( prefix + "pressedBackground" );
pressedForeground = UIManager.getColor( prefix + "pressedForeground" );
selectedBackground = UIManager.getColor( prefix + "selectedBackground" );
selectedForeground = UIManager.getColor( prefix + "selectedForeground" );
disabledBackground = UIManager.getColor( prefix + "disabledBackground" );
disabledText = UIManager.getColor( prefix + "disabledText" );
disabledSelectedBackground = UIManager.getColor( prefix + "disabledSelectedBackground" );
disabledSelectedForeground = UIManager.getColor( prefix + "disabledSelectedForeground" );
defaultBackground = FlatUIUtils.getUIColor( "Button.default.startBackground", "Button.default.background" );
defaultEndBackground = UIManager.getColor( "Button.default.endBackground" );
defaultForeground = UIManager.getColor( "Button.default.foreground" );
defaultFocusedBackground = UIManager.getColor( "Button.default.focusedBackground" );
defaultFocusedForeground = UIManager.getColor( "Button.default.focusedForeground" );
defaultHoverBackground = UIManager.getColor( "Button.default.hoverBackground" );
defaultHoverForeground = UIManager.getColor( "Button.default.hoverForeground" );
defaultPressedBackground = UIManager.getColor( "Button.default.pressedBackground" );
defaultPressedForeground = UIManager.getColor( "Button.default.pressedForeground" );
defaultBoldText = UIManager.getBoolean( "Button.default.boldText" );
paintShadow = UIManager.getBoolean( "Button.paintShadow" );
@@ -211,8 +243,13 @@ public class FlatButtonUI
defaultShadowColor = UIManager.getColor( "Button.default.shadowColor" );
toolbarHoverBackground = UIManager.getColor( prefix + "toolbar.hoverBackground" );
toolbarHoverForeground = UIManager.getColor( prefix + "toolbar.hoverForeground" );
toolbarPressedBackground = UIManager.getColor( prefix + "toolbar.pressedBackground" );
toolbarPressedForeground = UIManager.getColor( prefix + "toolbar.pressedForeground" );
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" );
defaultMargin = UIManager.getInsets( prefix + "margin" );
@@ -343,7 +380,7 @@ public class FlatButtonUI
/**
* 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 ) {
if( !(c instanceof JButton) && !(c instanceof JToggleButton) )
@@ -494,6 +531,23 @@ public class FlatButtonUI
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
protected void paintText( Graphics g, AbstractButton b, Rectangle textRect, String text ) {
if( isHelpButton( b ) )
@@ -514,6 +568,8 @@ public class FlatButtonUI
}
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() );
int mnemonicIndex = FlatLaf.isShowMnemonics() ? b.getDisplayedMnemonicIndex() : -1;
@@ -527,11 +583,14 @@ public class FlatButtonUI
// selected state
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
return buttonStateColor( c,
toolBarButton ? toolbarSelectedBackground : selectedBackground,
toolBarButton ? toolbarSelectedBackground : disabledSelectedBackground,
toolBarButton
? (toolbarDisabledSelectedBackground != null ? toolbarDisabledSelectedBackground : toolbarSelectedBackground)
: disabledSelectedBackground,
null,
null,
toolBarButton ? toolbarPressedBackground : pressedBackground );
@@ -596,18 +655,48 @@ public class FlatButtonUI
}
protected Color getForeground( JComponent c ) {
if( !c.isEnabled() )
return disabledText;
boolean toolBarButton = isToolBarButton( c ) || isBorderlessButton( c );
if( ((AbstractButton)c).isSelected() && !(isToolBarButton( c ) || isBorderlessButton( c )) )
return selectedForeground;
// selected state
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
Color fg = c.getForeground();
if( isCustomForeground( fg ) )
return fg;
boolean def = isDefaultButton( c );
return def ? defaultForeground : fg;
}
@@ -624,6 +713,9 @@ public class FlatButtonUI
if( prefSize == null )
return null;
// increase width when using bold font for default button
prefSize.width += defaultBoldPlainWidthDiff( c );
// make square or apply minimum width/height
boolean isIconOnlyOrSingleCharacter = isIconOnlyOrSingleCharacterButton( c );
if( clientPropertyBoolean( c, SQUARE_SIZE, squareSize ) ) {
@@ -644,6 +736,23 @@ public class FlatButtonUI
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 ) {
Insets margin = ((AbstractButton)c).getMargin();
return margin instanceof UIResource && Objects.equals( margin, defaultMargin );

View File

@@ -96,7 +96,7 @@ public class FlatCaret
// if text component is focused, then caret and selection are visible,
// 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
// adds selection highlights to the text component highlighter
if( isSelectionVisible() ) {
@@ -236,7 +236,7 @@ public class FlatCaret
if( selectAllOnFocusPolicy == null )
selectAllOnFocusPolicy = this.selectAllOnFocusPolicy;
if( SELECT_ALL_ON_FOCUS_POLICY_NEVER.equals( selectAllOnFocusPolicy ) )
if( selectAllOnFocusPolicy == null || SELECT_ALL_ON_FOCUS_POLICY_NEVER.equals( selectAllOnFocusPolicy ) )
return;
if( !SELECT_ALL_ON_FOCUS_POLICY_ALWAYS.equals( selectAllOnFocusPolicy ) ) {

View File

@@ -86,6 +86,11 @@ import com.formdev.flatlaf.util.SystemInfo;
* @uiDefault ComboBox.padding Insets
* @uiDefault ComboBox.squareButton boolean default is true
*
* <!-- BasicComboPopup -->
*
* @uiDefault ComboBox.selectionBackground Color
* @uiDefault ComboBox.selectionForeground Color
*
* <!-- FlatComboBoxUI -->
*
* @uiDefault ComboBox.minimumWidth int
@@ -603,7 +608,7 @@ public class FlatComboBoxUI
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 );
paddingBorder.uninstall();
@@ -677,7 +682,7 @@ public class FlatComboBoxUI
@Override
protected Dimension getSizeForComponent( Component comp ) {
paddingBorder.install( comp );
paddingBorder.install( comp, 0 );
Dimension size = super.getSizeForComponent( comp );
paddingBorder.uninstall();
return size;
@@ -700,7 +705,7 @@ public class FlatComboBoxUI
return true;
Component editorComponent = comboBox.getEditor().getEditorComponent();
return (editorComponent != null) ? FlatUIUtils.isPermanentFocusOwner( editorComponent ) : false;
return editorComponent != null && FlatUIUtils.isPermanentFocusOwner( editorComponent );
} else
return FlatUIUtils.isPermanentFocusOwner( comboBox );
}
@@ -895,7 +900,7 @@ public class FlatComboBoxUI
Component c = renderer.getListCellRendererComponent( list, value, index, isSelected, cellHasFocus );
c.applyComponentOrientation( comboBox.getComponentOrientation() );
paddingBorder.install( c );
paddingBorder.install( c, Math.round( FlatUIUtils.getBorderFocusWidth( comboBox ) ) );
return c;
}
@@ -918,6 +923,7 @@ public class FlatComboBoxUI
private Insets padding;
private JComponent rendererComponent;
private Border rendererBorder;
private int focusWidth;
CellPaddingBorder( Insets padding ) {
this.padding = padding;
@@ -925,10 +931,12 @@ public class FlatComboBoxUI
// using synchronized to avoid problems with code that modifies combo box
// (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) )
return;
this.focusWidth = focusWidth;
JComponent jc = (JComponent) c;
Border oldBorder = jc.getBorder();
if( oldBorder == this )
@@ -982,6 +990,12 @@ public class FlatComboBoxUI
insets.bottom = padding.bottom;
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;
}

View File

@@ -56,7 +56,7 @@ public class FlatEmptyBorder
protected static Insets scaleInsets( Component c, Insets insets,
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.top = scale( top );
insets.right = scale( leftToRight ? right : left );

View File

@@ -19,11 +19,21 @@ package com.formdev.flatlaf.ui;
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
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.lang.reflect.Method;
import java.util.function.Function;
import javax.swing.AbstractButton;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.ButtonGroup;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JButton;
@@ -34,12 +44,16 @@ import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JToggleButton;
import javax.swing.JToolBar;
import javax.swing.SwingConstants;
import javax.swing.UIManager;
import javax.swing.filechooser.FileSystemView;
import javax.swing.filechooser.FileView;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.metal.MetalFileChooserUI;
import javax.swing.table.TableCellRenderer;
import com.formdev.flatlaf.FlatClientProperties;
import com.formdev.flatlaf.util.LoggingFacade;
import com.formdev.flatlaf.util.ScaledImageIcon;
import com.formdev.flatlaf.util.SystemInfo;
import com.formdev.flatlaf.util.UIScale;
@@ -133,12 +147,21 @@ import com.formdev.flatlaf.util.UIScale;
* @uiDefault FileChooser.listViewActionLabelText 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
*/
public class FlatFileChooserUI
extends MetalFileChooserUI
{
private final FlatFileView fileView = new FlatFileView();
private FlatShortcutsPanel shortcutsPanel;
public static ComponentUI createUI( JComponent c ) {
return new FlatFileChooserUI( (JFileChooser) c );
@@ -153,6 +176,25 @@ public class FlatFileChooserUI
super.installComponents( 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 ) {
@@ -192,6 +234,25 @@ public class FlatFileChooserUI
} catch( ArrayIndexOutOfBoundsException ex ) {
// 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
@@ -250,9 +311,19 @@ public class FlatFileChooserUI
return p;
}
/** @since 2.3 */
protected FlatShortcutsPanel createShortcutsPanel( JFileChooser fc ) {
return new FlatShortcutsPanel( fc );
}
@Override
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
@@ -316,4 +387,234 @@ public class FlatFileChooserUI
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 );
}
}
}

View File

@@ -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.
*/
static <T> T getStyleFromListUI( Component c, Function<FlatListUI, T> f ) {

View File

@@ -212,7 +212,7 @@ public class FlatListUI
/**
* 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
* already used in applications. Then either the inactive colors are not used,

View File

@@ -18,8 +18,10 @@ package com.formdev.flatlaf.ui;
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Graphics;
import java.awt.Insets;
import java.awt.LayoutManager;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.beans.PropertyChangeListener;
@@ -27,6 +29,7 @@ import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;
import javax.swing.AbstractAction;
import javax.swing.ActionMap;
import javax.swing.BoxLayout;
import javax.swing.JComponent;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
@@ -40,11 +43,13 @@ import javax.swing.plaf.ActionMapUIResource;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.UIResource;
import javax.swing.plaf.basic.BasicMenuBarUI;
import javax.swing.plaf.basic.DefaultMenuLayout;
import com.formdev.flatlaf.FlatLaf;
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
import com.formdev.flatlaf.util.LoggingFacade;
import com.formdev.flatlaf.util.SystemInfo;
import com.formdev.flatlaf.util.UIScale;
/**
* Provides the Flat LaF UI delegate for {@link javax.swing.JMenuBar}.
@@ -100,6 +105,10 @@ public class FlatMenuBarUI
super.installDefaults();
LookAndFeel.installProperty( menuBar, "opaque", false );
LayoutManager layout = menuBar.getLayout();
if( layout == null || layout instanceof UIResource )
menuBar.setLayout( new FlatMenuBarLayout( menuBar ) );
}
@Override
@@ -210,7 +219,7 @@ public class FlatMenuBarUI
// check whether:
// - TitlePane.unifiedBackground is true and
// - menu bar is the "main" menu bar and
// - window has custom decorations enabled
// - window root pane has custom decoration style
JRootPane rootPane;
// (not storing value of "TitlePane.unifiedBackground" in class to allow changing at runtime)
@@ -218,7 +227,131 @@ public class FlatMenuBarUI
(rootPane = SwingUtilities.getRootPane( c )) != null &&
rootPane.getParent() instanceof Window &&
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 ----------------------------------------------------

View File

@@ -35,7 +35,7 @@ import javax.swing.plaf.MenuBarUI;
public class FlatMenuItemBorder
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" );
@Override

View File

@@ -446,6 +446,19 @@ debug*/
protected static void paintHTMLText( Graphics g, JMenuItem menuItem,
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 )
g = new GraphicsProxyWithTextColor( (Graphics2D) g, selectionForeground );

View File

@@ -0,0 +1,99 @@
/*
* 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
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() {
if( SystemInfo.isJava_9_orLater )
return;
// 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.
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 );
}
}
}

View File

@@ -42,11 +42,13 @@ import com.formdev.flatlaf.util.SystemInfo;
public class FlatNativeWindowBorder
{
// 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 disabled via system property
private static final boolean canUseWindowDecorations =
SystemInfo.isWindows_10_orLater &&
(SystemInfo.isWindows_11_orLater || !FlatSystemProperties.getBoolean( "sun.java2d.opengl", false )) &&
!SystemInfo.isProjector &&
!SystemInfo.isWebswing &&
!SystemInfo.isWinPE &&

View File

@@ -143,7 +143,7 @@ public class FlatPopupFactory
* <p>
* 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.
* 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>
* This is a workaround for https://bugs.openjdk.java.net/browse/JDK-8224608
*/

View File

@@ -16,18 +16,58 @@
package com.formdev.flatlaf.ui;
import java.awt.BorderLayout;
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.GraphicsConfiguration;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.Insets;
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.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;
import javax.swing.BoxLayout;
import javax.swing.JButton;
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.UIResource;
import javax.swing.plaf.basic.BasicComboPopup;
import javax.swing.plaf.basic.BasicMenuItemUI;
import javax.swing.plaf.basic.BasicPopupMenuUI;
import javax.swing.plaf.basic.DefaultMenuLayout;
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
import com.formdev.flatlaf.util.LoggingFacade;
@@ -41,12 +81,22 @@ import com.formdev.flatlaf.util.LoggingFacade;
* @uiDefault PopupMenu.foreground Color
* @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
*/
public class FlatPopupMenuUI
extends BasicPopupMenuUI
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 Map<String, Object> oldStyleValues;
private AtomicBoolean borderShared;
@@ -74,9 +124,21 @@ public class FlatPopupMenuUI
public void installDefaults() {
super.installDefaults();
arrowType = UIManager.getString( "Component.arrowType" );
scrollArrowColor = UIManager.getColor( "PopupMenu.scrollArrowColor" );
hoverScrollArrowBackground = UIManager.getColor( "PopupMenu.hoverScrollArrowBackground" );
LayoutManager layout = popupMenu.getLayout();
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
@@ -122,12 +184,61 @@ public class FlatPopupMenuUI
return FlatStylingSupport.getAnnotatedStyleableInfos( this, popupMenu.getBorder() );
}
//---- 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 );
protected static class FlatMenuLayout
// 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
{
public FlatMenuLayout( Container target, int axis ) {
public FlatPopupMenuLayout( Container target, int axis ) {
super( target, axis );
}
@@ -138,4 +249,206 @@ public class FlatPopupMenuUI
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 );
}
}
}
}

View File

@@ -199,7 +199,7 @@ public class FlatRadioButtonUI
return infos;
}
private static Insets tempInsets = new Insets( 0, 0, 0, 0 );
private static final Insets tempInsets = new Insets( 0, 0, 0, 0 );
@Override
public Dimension getPreferredSize( JComponent c ) {
@@ -212,7 +212,7 @@ public class FlatRadioButtonUI
if( focusWidth > 0 ) {
// 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.
// 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 );
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 );

View File

@@ -179,8 +179,8 @@ public class FlatRootPaneUI
super.installListeners( root );
if( SystemInfo.isJava_9_orLater ) {
// On HiDPI screens, where scaling is used, there may be white lines at the
// bottom and at the right side of the window when it is initially shown.
// On HiDPI screens, where scaling is used, there may be white lines on the
// 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.
// Seems to be a rounding issue when Swing adds dirty region of window
// using RepaintManager.nativeAddDirtyRegion().
@@ -364,6 +364,12 @@ public class FlatRootPaneUI
((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 -----------------------------------------------
protected class FlatRootLayout
@@ -510,7 +516,7 @@ public class FlatRootPaneUI
return;
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 ) );
HiDPIUtils.paintAtScale1x( (Graphics2D) g, x, y, width, height, this::paintImpl );
@@ -522,9 +528,7 @@ public class FlatRootPaneUI
protected boolean isWindowMaximized( Component c ) {
Container parent = c.getParent();
return parent instanceof Frame
? (((Frame)parent).getExtendedState() & Frame.MAXIMIZED_BOTH) != 0
: false;
return parent instanceof Frame && (((Frame)parent).getExtendedState() & Frame.MAXIMIZED_BOTH) != 0;
}
}

View File

@@ -57,6 +57,7 @@ import com.formdev.flatlaf.util.UIScale;
*
* <!-- FlatScrollBarUI -->
*
* @uiDefault ScrollBar.minimumButtonSize Dimension
* @uiDefault ScrollBar.trackInsets Insets
* @uiDefault ScrollBar.thumbInsets Insets
* @uiDefault ScrollBar.trackArc int
@@ -83,6 +84,7 @@ public class FlatScrollBarUI
// overrides BasicScrollBarUI.supportsAbsolutePositioning (which is private)
@Styleable protected boolean allowsAbsolutePositioning;
/** @since 2.1 */ @Styleable protected Dimension minimumButtonSize;
@Styleable protected Insets trackInsets;
@Styleable protected Insets thumbInsets;
@Styleable protected int trackArc;
@@ -142,6 +144,7 @@ public class FlatScrollBarUI
allowsAbsolutePositioning = super.getSupportsAbsolutePositioning();
minimumButtonSize = UIManager.getDimension( "ScrollBar.minimumButtonSize" );
trackInsets = UIManager.getInsets( "ScrollBar.trackInsets" );
thumbInsets = UIManager.getInsets( "ScrollBar.thumbInsets" );
trackArc = UIManager.getInt( "ScrollBar.trackArc" );
@@ -171,6 +174,7 @@ public class FlatScrollBarUI
protected void uninstallDefaults() {
super.uninstallDefaults();
minimumButtonSize = null;
trackInsets = null;
thumbInsets = null;
hoverTrackColor = null;
@@ -451,7 +455,6 @@ public class FlatScrollBarUI
super( direction, type, foreground, disabledForeground,
hoverForeground, hoverBackground, pressedForeground, pressedBackground );
setArrowWidth( FlatArrowButton.DEFAULT_ARROW_WIDTH - 2 );
setFocusable( false );
setRequestFocusEnabled( false );
}
@@ -461,6 +464,18 @@ public class FlatScrollBarUI
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
protected Color deriveBackground( Color background ) {
return FlatUIUtils.deriveColor( background, scrollbar.getBackground() );
@@ -469,8 +484,9 @@ public class FlatScrollBarUI
@Override
public Dimension getPreferredSize() {
if( isShowButtons() ) {
int w = UIScale.scale( scrollBarWidth );
return new Dimension( w, w );
int w = UIScale.scale( Math.max( scrollBarWidth, (minimumButtonSize != null) ? minimumButtonSize.width : 0 ) );
int h = UIScale.scale( Math.max( scrollBarWidth, (minimumButtonSize != null) ? minimumButtonSize.height : 0 ) );
return new Dimension( w, h );
} else
return new Dimension();
}

View File

@@ -234,7 +234,7 @@ public class FlatSliderUI
return -1;
// 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" );
if( font == null )
font = slider.getFont();

View File

@@ -293,9 +293,7 @@ public class FlatSpinnerUI
return true;
JTextField textField = getEditorTextField( spinner.getEditor() );
return (textField != null)
? FlatUIUtils.isPermanentFocusOwner( textField )
: false;
return textField != null && FlatUIUtils.isPermanentFocusOwner( textField );
}
protected Color getBackground( boolean enabled ) {
@@ -447,7 +445,7 @@ public class FlatSpinnerUI
Insets padding = scale( FlatSpinnerUI.this.padding );
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 innerHeight = editorSize.height + padding.top + padding.bottom;
float focusWidth = FlatUIUtils.getBorderFocusWidth( spinner );

View File

@@ -34,6 +34,7 @@ import javax.swing.UIManager;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.basic.BasicSplitPaneDivider;
import javax.swing.plaf.basic.BasicSplitPaneUI;
import com.formdev.flatlaf.FlatClientProperties;
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
import com.formdev.flatlaf.ui.FlatStylingSupport.UnknownStyleException;
@@ -52,6 +53,13 @@ import com.formdev.flatlaf.util.UIScale;
* @uiDefault SplitPaneDivider.border Border
* @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 -->
*
* @uiDefault SplitPane.continuousLayout boolean
@@ -235,7 +243,7 @@ public class FlatSplitPaneUI
switch( e.getPropertyName() ) {
case JSplitPane.DIVIDER_LOCATION_PROPERTY:
// necessary to show/hide one-touch buttons on expand/collapse
revalidate();
doLayout();
break;
}
}
@@ -345,7 +353,7 @@ public class FlatSplitPaneUI
if( leftButton == null || rightButton == null || !splitPane.isOneTouchExpandable() )
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
int extraSize = UIScale.scale( 4 );
if( orientation == JSplitPane.VERTICAL_SPLIT ) {
@@ -360,10 +368,19 @@ public class FlatSplitPaneUI
// hide buttons if not applicable
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() );
leftButton.setVisible( !leftCollapsed );
rightButton.setVisible( !isRightCollapsed() );
}
}
}

View File

@@ -359,7 +359,7 @@ public class FlatStylingSupport
* @param key the name of the field
* @param value the new value
* @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
*/
public static Object applyToAnnotatedObject( Object obj, String key, Object value )
@@ -517,7 +517,7 @@ public class FlatStylingSupport
* @param key the name of the field
* @param value the new value
* @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
*/
public static Object applyToAnnotatedObjectOrComponent( Object obj, Object comp, String key, Object value )

View File

@@ -59,6 +59,7 @@ import java.util.Map;
import java.util.Set;
import java.util.function.BiConsumer;
import java.util.function.IntConsumer;
import java.util.function.Predicate;
import javax.accessibility.Accessible;
import javax.accessibility.AccessibleContext;
import javax.swing.Action;
@@ -82,10 +83,12 @@ import javax.swing.event.ChangeListener;
import javax.swing.event.PopupMenuEvent;
import javax.swing.event.PopupMenuListener;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.TabbedPaneUI;
import javax.swing.plaf.UIResource;
import javax.swing.plaf.basic.BasicTabbedPaneUI;
import javax.swing.text.JTextComponent;
import javax.swing.text.View;
import com.formdev.flatlaf.FlatClientProperties;
import com.formdev.flatlaf.FlatLaf;
import com.formdev.flatlaf.icons.FlatTabbedPaneCloseIcon;
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
@@ -127,6 +130,7 @@ import com.formdev.flatlaf.util.UIScale;
* @uiDefault TabbedPane.selectedBackground Color optional
* @uiDefault TabbedPane.selectedForeground Color
* @uiDefault TabbedPane.underlineColor Color
* @uiDefault TabbedPane.inactiveUnderlineColor Color
* @uiDefault TabbedPane.disabledUnderlineColor Color
* @uiDefault TabbedPane.hoverColor Color
* @uiDefault TabbedPane.focusColor Color
@@ -198,6 +202,7 @@ public class FlatTabbedPaneUI
@Styleable protected Color selectedBackground;
@Styleable protected Color selectedForeground;
@Styleable protected Color underlineColor;
/** @since 2.2 */ @Styleable protected Color inactiveUnderlineColor;
@Styleable protected Color disabledUnderlineColor;
@Styleable protected Color hoverColor;
@Styleable protected Color focusColor;
@@ -288,6 +293,7 @@ public class FlatTabbedPaneUI
super.installUI( c );
FlatSelectedTabRepainter.install();
installStyle();
}
@@ -318,6 +324,7 @@ public class FlatTabbedPaneUI
selectedBackground = UIManager.getColor( "TabbedPane.selectedBackground" );
selectedForeground = UIManager.getColor( "TabbedPane.selectedForeground" );
underlineColor = UIManager.getColor( "TabbedPane.underlineColor" );
inactiveUnderlineColor = FlatUIUtils.getUIColor( "TabbedPane.inactiveUnderlineColor", underlineColor );
disabledUnderlineColor = UIManager.getColor( "TabbedPane.disabledUnderlineColor" );
hoverColor = UIManager.getColor( "TabbedPane.hoverColor" );
focusColor = UIManager.getColor( "TabbedPane.focusColor" );
@@ -385,6 +392,7 @@ public class FlatTabbedPaneUI
selectedBackground = null;
selectedForeground = null;
underlineColor = null;
inactiveUnderlineColor = null;
disabledUnderlineColor = null;
hoverColor = null;
focusColor = null;
@@ -650,7 +658,7 @@ public class FlatTabbedPaneUI
case "tabIconPlacement": value = parseTabIconPlacement( (String) value ); break;
}
} else {
Object oldValue = null;
Object oldValue;
switch( key ) {
// BasicTabbedPaneUI
case "tabInsets": oldValue = tabInsets; tabInsets = (Insets) value; return oldValue;
@@ -733,7 +741,6 @@ public class FlatTabbedPaneUI
// increase size of repaint region to include part of content border
if( contentSeparatorHeight > 0 &&
getTabType() == TAB_TYPE_CARD &&
clientPropertyBoolean( tabPane, TABBED_PANE_SHOW_CONTENT_SEPARATOR, true ) )
{
int sh = scale( contentSeparatorHeight );
@@ -1205,7 +1212,9 @@ public class FlatTabbedPaneUI
}
protected void paintTabSelection( Graphics g, int tabPlacement, 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
boolean atBottom = (getTabType() != TAB_TYPE_CARD);
@@ -1236,6 +1245,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
* - not using UIManager.getColor("TabbedPane.contentAreaColor") to be GUI builder friendly
@@ -1841,7 +1867,7 @@ public class FlatTabbedPaneUI
super( direction, arrowType,
FlatTabbedPaneUI.this.foreground, FlatTabbedPaneUI.this.disabledForeground,
null, buttonHoverBackground, null, buttonPressedBackground );
setArrowWidth( 10 );
setArrowWidth( 11 );
}
protected void updateStyle() {
@@ -1983,7 +2009,7 @@ public class FlatTabbedPaneUI
}
protected JMenuItem createTabMenuItem( int tabIndex ) {
// search for tab name in this places
// search for tab name in these places
// 1. tab title
// 2. text of label or text component in custom tab component (including children)
// 3. accessible name of tab
@@ -2016,7 +2042,7 @@ public class FlatTabbedPaneUI
menuItem.setOpaque( true );
}
if( !tabPane.isEnabledAt( tabIndex ) )
if( !tabPane.isEnabled() || !tabPane.isEnabledAt( tabIndex ) )
menuItem.setEnabled( false );
menuItem.addActionListener( e -> selectTab( tabIndex ) );
@@ -2412,7 +2438,7 @@ public class FlatTabbedPaneUI
if( tabPane == null || tabViewport == null )
return;
if( !scrolled || tabViewport == null )
if( !scrolled )
return;
scrolled = false;
@@ -2525,9 +2551,7 @@ public class FlatTabbedPaneUI
setRolloverTab( tabIndex );
// check whether mouse hit tab close area
boolean hitClose = isTabClosable( tabIndex )
? getTabCloseHitArea( tabIndex ).contains( x, y )
: false;
boolean hitClose = isTabClosable( tabIndex ) && getTabCloseHitArea( tabIndex ).contains( x, y );
if( e.getID() == MouseEvent.MOUSE_PRESSED )
pressedTabIndex = hitClose ? tabIndex : -1;
setRolloverTabClose( hitClose );
@@ -2550,8 +2574,7 @@ public class FlatTabbedPaneUI
if( tabIndex == lastTipTabIndex )
return; // closeTip already set
if( tabIndex != lastTipTabIndex )
restoreTabToolTip();
restoreTabToolTip();
lastTipTabIndex = tabIndex;
lastTip = tabPane.getToolTipTextAt( lastTipTabIndex );
@@ -3344,4 +3367,77 @@ public class FlatTabbedPaneUI
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() );
}
}
}

View File

@@ -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.
*/
static <T> T getStyleFromTableUI( Component c, Function<FlatTableUI, T> f ) {

View File

@@ -39,7 +39,9 @@ import javax.swing.event.MouseInputListener;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.UIResource;
import javax.swing.plaf.basic.BasicTableHeaderUI;
import javax.swing.table.JTableHeader;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableColumn;
import javax.swing.table.TableColumnModel;
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
@@ -195,6 +197,8 @@ public class FlatTableHeaderUI
@Override
public void paint( Graphics g, JComponent c ) {
fixDraggedAndResizingColumns( header );
TableColumnModel columnModel = header.getColumnModel();
if( columnModel.getColumnCount() <= 0 )
return;
@@ -269,6 +273,32 @@ public class FlatTableHeaderUI
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 ----------------------------------
/**

View File

@@ -80,6 +80,7 @@ import com.formdev.flatlaf.util.UIScale;
* @uiDefault Table.intercellSpacing Dimension
* @uiDefault Table.selectionInactiveBackground Color
* @uiDefault Table.selectionInactiveForeground Color
* @uiDefault Table.paintOutsideAlternateRows boolean
*
* <!-- FlatTableCellBorder -->
*
@@ -95,7 +96,7 @@ import com.formdev.flatlaf.util.UIScale;
*/
public class FlatTableUI
extends BasicTableUI
implements StyleableUI
implements StyleableUI, FlatViewportUI.ViewportPainter
{
protected boolean showHorizontalLines;
protected boolean showVerticalLines;
@@ -288,7 +289,7 @@ public class FlatTableUI
/**
* 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
* already used in applications. Then either the inactive colors are not used,
@@ -313,6 +314,8 @@ public class FlatTableUI
@Override
public void paint( Graphics g, JComponent c ) {
FlatTableHeaderUI.fixDraggedAndResizingColumns( table.getTableHeader() );
boolean horizontalLines = table.getShowHorizontalLines();
boolean verticalLines = table.getShowVerticalLines();
if( horizontalLines || verticalLines ) {
@@ -421,4 +424,38 @@ public class FlatTableUI
? (viewport != rowHeader)
: (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 );
}
}
}
}
}

View File

@@ -369,7 +369,7 @@ public class FlatTextFieldUI
if( !(oldBackground instanceof UIResource) )
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 &&
oldBackground != disabledBackground &&
oldBackground != inactiveBackground &&
@@ -610,7 +610,7 @@ debug*/
* Returns the rectangle used to paint leading and trailing icons.
* It invokes {@code super.getVisibleEditorRect()} and reduces left and/or
* 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
*/

View File

@@ -24,6 +24,7 @@ import java.awt.Container;
import java.awt.Dialog;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.FontMetrics;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.GraphicsConfiguration;
@@ -50,13 +51,13 @@ import javax.accessibility.AccessibleContext;
import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.Icon;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JMenuBar;
import javax.swing.JPanel;
import javax.swing.JRootPane;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.border.AbstractBorder;
@@ -83,10 +84,14 @@ import com.formdev.flatlaf.util.UIScale;
* @uiDefault TitlePane.iconMargins Insets
* @uiDefault TitlePane.titleMargins Insets
* @uiDefault TitlePane.menuBarEmbedded boolean
* @uiDefault TitlePane.titleMinimumWidth int
* @uiDefault TitlePane.buttonMinimumWidth int
* @uiDefault TitlePane.buttonMaximizedHeight int
* @uiDefault TitlePane.centerTitle boolean
* @uiDefault TitlePane.centerTitleIfMenuBarEmbedded boolean
* @uiDefault TitlePane.showIconBesideTitle boolean
* @uiDefault TitlePane.menuBarTitleGap int
* @uiDefault TitlePane.menuBarResizeHeight int
* @uiDefault TitlePane.closeIcon Icon
* @uiDefault TitlePane.iconifyIcon Icon
* @uiDefault TitlePane.maximizeIcon Icon
@@ -107,10 +112,15 @@ public class FlatTitlePane
/** @since 2 */ protected final boolean showIcon = FlatUIUtils.getUIBoolean( "TitlePane.showIcon", true );
/** @since 2 */ protected final int noIconLeftGap = FlatUIUtils.getUIInt( "TitlePane.noIconLeftGap", 8 );
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 boolean centerTitle = UIManager.getBoolean( "TitlePane.centerTitle" );
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;
@@ -142,6 +152,8 @@ public class FlatTitlePane
// necessary for closing window with double-click on icon
iconLabel.addMouseListener( handler );
applyComponentOrientation( rootPane.getComponentOrientation() );
}
protected FlatTitlePaneBorder createTitlePaneBorder() {
@@ -163,7 +175,6 @@ public class FlatTitlePane
};
iconLabel.setBorder( new FlatEmptyBorder( UIManager.getInsets( "TitlePane.iconMargins" ) ) );
titleLabel.setBorder( new FlatEmptyBorder( UIManager.getInsets( "TitlePane.titleMargins" ) ) );
titleLabel.setHorizontalAlignment( SwingConstants.CENTER );
leftPanel.setLayout( new BoxLayout( leftPanel, BoxLayout.LINE_AXIS ) );
leftPanel.setOpaque( false );
@@ -183,18 +194,52 @@ public class FlatTitlePane
setLayout( new BorderLayout() {
@Override
public void layoutContainer( Container target ) {
super.layoutContainer( target );
// make left panel (with embedded menu bar) smaller if horizontal space is rare
// to avoid that embedded menu bar overlaps button bar
// compute available bounds
Insets insets = target.getInsets();
int width = target.getWidth() - insets.left - insets.right;
if( leftPanel.getWidth() + buttonPanel.getWidth() > width ) {
int oldWidth = leftPanel.getWidth();
int newWidth = Math.max( width - buttonPanel.getWidth(), 0 );
leftPanel.setSize( newWidth, leftPanel.getHeight() );
if( !getComponentOrientation().isLeftToRight() )
leftPanel.setLocation( leftPanel.getX() + (oldWidth - newWidth), leftPanel.getY() );
int x = insets.left;
int y = insets.top;
int w = target.getWidth() - insets.left - insets.right;
int h = target.getHeight() - insets.top - insets.bottom;
// compute widths
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,
@@ -233,10 +278,7 @@ public class FlatTitlePane
@Override
public Dimension getPreferredSize() {
Dimension size = super.getPreferredSize();
if( buttonMaximizedHeight > 0 &&
window instanceof Frame &&
(((Frame)window).getExtendedState() & Frame.MAXIMIZED_BOTH) != 0 )
{
if( buttonMaximizedHeight > 0 && isWindowMaximized() && !hasVisibleEmbeddedMenuBar( rootPane.getJMenuBar() ) ) {
// make title pane height smaller when frame is maximized
size = new Dimension( size.width, Math.min( size.height, UIScale.scale( buttonMaximizedHeight ) ) );
}
@@ -248,7 +290,7 @@ public class FlatTitlePane
if( rootPane.getWindowDecorationStyle() == JRootPane.FRAME ) {
// JRootPane.FRAME works only for frames (and not for dialogs)
// 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()
buttonPanel.add( iconifyButton );
@@ -259,7 +301,13 @@ public class FlatTitlePane
}
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.setContentAreaFilled( false );
button.setBorder( BorderFactory.createEmptyBorder() );
@@ -356,11 +404,12 @@ public class FlatTitlePane
boolean hasIcon = (images != null && !images.isEmpty());
// 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
iconLabel.setVisible( hasIcon );
leftPanel.setBorder( hasIcon ? null : FlatUIUtils.nonUIResource( new FlatEmptyBorder( 0, noIconLeftGap, 0, 0 ) ) );
iconLabel.setVisible( hasIcon && !showIconBesideTitle );
leftPanel.setBorder( hasIcon && !showIconBesideTitle ? null : FlatUIUtils.nonUIResource( new FlatEmptyBorder( 0, noIconLeftGap, 0, 0 ) ) );
updateNativeTitleBarHeightAndHitTestSpotsLater();
}
@@ -420,7 +469,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 ) {
return menuBar != null && menuBar.isVisible() && isMenuBarEmbedded();
@@ -567,6 +616,11 @@ debug*/
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.
*/
@@ -615,7 +669,7 @@ debug*/
int maximizedWidth = screenBounds.width;
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
maximizedX = 0;
maximizedY = 0;
@@ -729,7 +783,7 @@ debug*/
List<Rectangle> hitTestSpots = new ArrayList<>();
Rectangle appIconBounds = null;
if( iconLabel.isVisible() ) {
if( !showIconBesideTitle && iconLabel.isVisible() ) {
// compute real icon size (without insets; 1px larger for easier hitting)
Point location = SwingUtilities.convertPoint( iconLabel, 0, 0, window );
Insets iconInsets = iconLabel.getInsets();
@@ -741,9 +795,7 @@ debug*/
// if frame is maximized, increase icon bounds to upper-left corner
// of window to allow closing window via double-click in upper-left corner
if( window instanceof Frame &&
(((Frame)window).getExtendedState() & Frame.MAXIMIZED_BOTH) != 0 )
{
if( isWindowMaximized() ) {
iconBounds.height += iconBounds.y;
iconBounds.y = 0;
@@ -758,6 +810,38 @@ debug*/
hitTestSpots.add( iconBounds );
else
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 );
@@ -768,11 +852,20 @@ debug*/
if( hasVisibleEmbeddedMenuBar( menuBar ) ) {
r = getNativeHitTestSpot( menuBar );
if( r != null ) {
// if frame is resizable and not maximized, make menu bar hit test spot smaller at top
// to have a small area above the menu bar to resize the window
if( window instanceof Frame && ((Frame)window).isResizable() && !isWindowMaximized() ) {
// limit to 8, because Windows does not use a larger height
int resizeHeight = UIScale.scale( Math.min( menuBarResizeHeight, 8 ) );
r.y += resizeHeight;
r.height -= resizeHeight;
}
Component horizontalGlue = findHorizontalGlue( menuBar );
if( horizontalGlue != null ) {
// If menu bar is embedded and contains a horizontal glue component,
// then split the hit test spot into two spots so that
// the glue component area can used to move the window.
// the glue component area can be used to move the window.
Point glueLocation = SwingUtilities.convertPoint( horizontalGlue, 0, 0, window );
int x2 = glueLocation.x + horizontalGlue.getWidth();
@@ -854,7 +947,7 @@ debug*/
} else if( borderColor != null && (rootPane.getJMenuBar() == null || !rootPane.getJMenuBar().isVisible()) )
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() );
return insets;
@@ -873,7 +966,7 @@ debug*/
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 );
}
@@ -881,12 +974,6 @@ debug*/
JMenuBar menuBar = rootPane.getJMenuBar();
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 ---------------------------------------------
@@ -900,32 +987,101 @@ debug*/
}
@Override
protected void paintEnabledText( JLabel l, Graphics g, String s, int textX, int textY ) {
boolean hasEmbeddedMenuBar = hasVisibleEmbeddedMenuBar( rootPane.getJMenuBar() );
int labelWidth = l.getWidth();
int textWidth = labelWidth - (textX * 2);
int gap = UIScale.scale( menuBarTitleGap );
protected String layoutCL( JLabel label, FontMetrics fontMetrics, String text, Icon icon,
Rectangle viewR, Rectangle iconR, Rectangle textR )
{
JMenuBar menuBar = rootPane.getJMenuBar();
boolean hasEmbeddedMenuBar = hasVisibleEmbeddedMenuBar( menuBar );
boolean hasEmbeddedLeadingMenus = hasEmbeddedMenuBar && hasLeadingMenus( menuBar );
boolean leftToRight = getComponentOrientation().isLeftToRight();
// The passed in textX coordinate is always to horizontally center the text within the label bounds.
// Modify textX so that the text is painted either centered within the window bounds or leading aligned.
boolean center = hasEmbeddedMenuBar ? centerTitleIfMenuBarEmbedded : centerTitle;
if( center ) {
// If window is wide enough, center title within window bounds.
// Otherwise leave it centered within free space (label bounds).
int centeredTextX = ((l.getParent().getWidth() - textWidth) / 2) - l.getX();
if( centeredTextX >= gap && centeredTextX + textWidth <= labelWidth - gap )
textX = centeredTextX;
} else {
// leading aligned
boolean leftToRight = getComponentOrientation().isLeftToRight();
Insets insets = l.getInsets();
int leadingInset = hasEmbeddedMenuBar ? gap : (leftToRight ? insets.left : insets.right);
int leadingTextX = leftToRight ? leadingInset : labelWidth - leadingInset - textWidth;
if( leftToRight ? leadingTextX < textX : leadingTextX > textX )
textX = leadingTextX;
if( hasEmbeddedMenuBar ) {
int minGap = UIScale.scale( menuBarTitleMinimumGap );
// 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;
}
}
@@ -1018,6 +1174,9 @@ debug*/
if( window == null )
return; // should newer occur
if( !SwingUtilities.isLeftMouseButton( e ) )
return;
dragOffset = SwingUtilities.convertPoint( FlatTitlePane.this, e.getPoint(), window );
}
@@ -1032,6 +1191,9 @@ debug*/
if( window == null )
return; // should newer occur
if( !SwingUtilities.isLeftMouseButton( e ) )
return;
if( hasNativeCustomDecoration() )
return; // do nothing if having native window border

View File

@@ -21,12 +21,8 @@ import java.awt.Color;
import java.awt.Component;
import java.awt.Graphics;
import java.beans.PropertyChangeEvent;
import java.util.Iterator;
import java.util.Map;
import javax.swing.AbstractButton;
import javax.swing.JComponent;
import javax.swing.JToggleButton;
import javax.swing.UIManager;
import javax.swing.*;
import javax.swing.plaf.ComponentUI;
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
import com.formdev.flatlaf.ui.FlatStylingSupport.UnknownStyleException;
@@ -52,15 +48,29 @@ import com.formdev.flatlaf.util.UIScale;
* @uiDefault ToggleButton.iconTextGap int
* @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.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.selectedForeground Color
* @uiDefault ToggleButton.disabledBackground Color optional
* @uiDefault ToggleButton.disabledText 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.hoverForeground Color optional
* @uiDefault ToggleButton.toolbar.pressedBackground Color
* @uiDefault ToggleButton.toolbar.pressedForeground Color optional
* @uiDefault ToggleButton.toolbar.selectedBackground Color
* @uiDefault ToggleButton.toolbar.selectedForeground Color optional
* @uiDefault ToggleButton.toolbar.disabledSelectedBackground Color optional
* @uiDefault ToggleButton.toolbar.disabledSelectedForeground Color optional
*
* <!-- FlatToggleButtonUI -->
*
@@ -68,8 +78,11 @@ import com.formdev.flatlaf.util.UIScale;
* @uiDefault ToggleButton.tab.underlineColor Color
* @uiDefault ToggleButton.tab.disabledUnderlineColor Color
* @uiDefault ToggleButton.tab.selectedBackground Color optional
* @uiDefault ToggleButton.tab.selectedForeground Color optional
* @uiDefault ToggleButton.tab.hoverBackground Color
* @uiDefault ToggleButton.tab.hoverForeground Color optional
* @uiDefault ToggleButton.tab.focusBackground Color
* @uiDefault ToggleButton.tab.focusForeground Color optional
*
*
* @author Karl Tauber
@@ -81,8 +94,11 @@ public class FlatToggleButtonUI
@Styleable(dot=true) protected Color tabUnderlineColor;
@Styleable(dot=true) protected Color tabDisabledUnderlineColor;
@Styleable(dot=true) protected Color tabSelectedBackground;
/** @since 2.3 */ @Styleable(dot=true) protected Color tabSelectedForeground;
@Styleable(dot=true) protected Color tabHoverBackground;
/** @since 2.3 */ @Styleable(dot=true) protected Color tabHoverForeground;
@Styleable(dot=true) protected Color tabFocusBackground;
/** @since 2.3 */ @Styleable(dot=true) protected Color tabFocusForeground;
private boolean defaults_initialized = false;
@@ -115,8 +131,11 @@ public class FlatToggleButtonUI
tabUnderlineColor = UIManager.getColor( "ToggleButton.tab.underlineColor" );
tabDisabledUnderlineColor = UIManager.getColor( "ToggleButton.tab.disabledUnderlineColor" );
tabSelectedBackground = UIManager.getColor( "ToggleButton.tab.selectedBackground" );
tabSelectedForeground = UIManager.getColor( "ToggleButton.tab.selectedForeground" );
tabHoverBackground = UIManager.getColor( "ToggleButton.tab.hoverBackground" );
tabHoverForeground = UIManager.getColor( "ToggleButton.tab.hoverForeground" );
tabFocusBackground = UIManager.getColor( "ToggleButton.tab.focusBackground" );
tabFocusForeground = UIManager.getColor( "ToggleButton.tab.focusForeground" );
defaults_initialized = true;
}
@@ -143,6 +162,7 @@ public class FlatToggleButtonUI
b.repaint();
break;
case TAB_BUTTON_UNDERLINE_PLACEMENT:
case TAB_BUTTON_UNDERLINE_HEIGHT:
case TAB_BUTTON_UNDERLINE_COLOR:
case TAB_BUTTON_SELECTED_BACKGROUND:
@@ -164,11 +184,7 @@ public class FlatToggleButtonUI
@Override
public Map<String, Class<?>> getStyleableInfos( JComponent c ) {
Map<String, Class<?>> infos = super.getStyleableInfos( c );
Iterator<String> it = infos.keySet().iterator();
while( it.hasNext() ) {
if( it.next().startsWith( "help." ) )
it.remove();
}
infos.keySet().removeIf( s -> s.startsWith( "help." ) );
return infos;
}
@@ -201,13 +217,42 @@ public class FlatToggleButtonUI
// paint underline 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()
? clientPropertyColor( c, TAB_BUTTON_UNDERLINE_COLOR, tabUnderlineColor )
: 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
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 );
}
}

View File

@@ -256,11 +256,15 @@ public class FlatToolBarUI
default: return;
}
for( int i = focusedCompIndex + add; i != focusedCompIndex; i += add ) {
int i = focusedCompIndex;
for(;;) {
i += add;
if( i < 0 )
i = count - 1;
else if( i >= count )
i = 0;
if( i == focusedCompIndex )
break;
Component c = toolBar.getComponentAtIndex( i );
if( canBeFocusOwner( c ) ) {
@@ -282,7 +286,7 @@ public class FlatToolBarUI
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)
// see LayoutFocusTraversalPolicy.accept()
if( c instanceof JComponent ) {

View File

@@ -71,7 +71,7 @@ public class FlatUIUtils
public static final boolean MAC_USE_QUARTZ = Boolean.getBoolean( "apple.awt.graphics.UseQuartz" );
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 ) {
return new Rectangle(
@@ -245,7 +245,7 @@ public class FlatUIUtils
isInActiveWindow( c, keyboardFocusManager.getActiveWindow() );
}
private static boolean isInActiveWindow( Component c, Window activeWindow ) {
static boolean isInActiveWindow( Component c, Window activeWindow ) {
Window window = SwingUtilities.windowForComponent( c );
return window == activeWindow ||
(window != null && window.getType() == Window.Type.POPUP && window.getOwner() == activeWindow);
@@ -777,8 +777,8 @@ public class FlatUIUtils
* {@link SwingConstants#WEST} or {@link SwingConstants#EAST})
* @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 xOffset a 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 xOffset an offset added to the x 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
*/
@@ -786,13 +786,15 @@ public class FlatUIUtils
int direction, boolean chevron, int arrowSize, float xOffset, float yOffset )
{
// compute arrow width/height
int aw = UIScale.scale( arrowSize + (chevron ? 0 : 1) );
int ah = UIScale.scale( (arrowSize / 2) + (chevron ? 0 : 1) );
// - make chevron arrows one pixel smaller because coordinates are based on center of pixels (0.5/0.5)
// - 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
boolean vert = (direction == SwingConstants.NORTH || direction == SwingConstants.SOUTH);
if( !vert ) {
int temp = aw;
float temp = aw;
aw = ah;
ah = temp;
}
@@ -804,19 +806,19 @@ public class FlatUIUtils
// compute arrow location
float ox = ((width - (aw + extra)) / 2f) + UIScale.scale( xOffset );
float oy = ((height - (ah + extra)) / 2f) + UIScale.scale( yOffset );
int ax = x + ((direction == SwingConstants.WEST) ? -Math.round( -ox ) : Math.round( ox ));
int ay = y + ((direction == SwingConstants.NORTH) ? -Math.round( -oy ) : Math.round( oy ));
float ax = x + ((direction == SwingConstants.WEST) ? -Math.round( -(ox + aw) ) - aw : Math.round( ox ));
float ay = y + ((direction == SwingConstants.NORTH) ? -Math.round( -(oy + ah) ) - ah : Math.round( oy ));
// paint arrow
g.translate( ax, ay );
/*debug
debugPaintArrow( g, Color.red, vert, aw + extra, ah + extra );
debugPaintArrow( g, Color.red, vert, Math.round( aw + extra ), Math.round( ah + extra ) );
debug*/
Shape arrowShape = createArrowShape( direction, chevron, aw, ah );
if( chevron ) {
Stroke oldStroke = g.getStroke();
g.setStroke( new BasicStroke( UIScale.scale( 1f ) ) );
g.draw( arrowShape );
drawShapePure( g, arrowShape );
g.setStroke( oldStroke );
} else {
// triangle
@@ -828,7 +830,7 @@ debug*/
/**
* Creates a chevron or triangle arrow shape for the given direction and size.
* <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)}.
*
* @param direction the arrow direction ({@link SwingConstants#NORTH}, {@link SwingConstants#SOUTH}
@@ -880,7 +882,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 ) {
Path2D path = new Path2D.Float();
@@ -892,6 +894,23 @@ debug*/
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.
* The provided component is used to query text properties and anti-aliasing hints.

View File

@@ -18,8 +18,8 @@ package com.formdev.flatlaf.ui;
import java.awt.Component;
import java.awt.Graphics;
import java.lang.reflect.Method;
import javax.swing.JComponent;
import javax.swing.JTable;
import javax.swing.JViewport;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.basic.BasicViewportUI;
@@ -43,15 +43,28 @@ public class FlatViewportUI
}
@Override
public void update( Graphics g, JComponent c ) {
Component view = ((JViewport)c).getView();
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() );
public void paint( Graphics g, JComponent c ) {
super.paint( g, c );
paint( g, c );
} else
super.update( g, c );
Component view = ((JViewport)c).getView();
if( view instanceof JComponent ) {
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 );
}
}

View File

@@ -23,6 +23,7 @@ import java.awt.Dialog;
import java.awt.Dimension;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.GraphicsConfiguration;
import java.awt.Insets;
import java.awt.Rectangle;
import java.awt.Toolkit;
@@ -42,7 +43,9 @@ import javax.swing.JComponent;
import javax.swing.JInternalFrame;
import javax.swing.JLayeredPane;
import javax.swing.JRootPane;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import com.formdev.flatlaf.util.SystemInfo;
import com.formdev.flatlaf.util.UIScale;
/**
@@ -231,8 +234,15 @@ public abstract class FlatWindowResizer
{
protected Window window;
private final boolean limitResizeToScreenBounds;
public WindowResizer( JRootPane 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
@@ -289,11 +299,19 @@ public abstract class FlatWindowResizer
@Override
protected boolean limitToParentBounds() {
return false;
return limitResizeToScreenBounds && window != null;
}
@Override
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;
}
@@ -385,7 +403,7 @@ public abstract class FlatWindowResizer
@Override
protected Rectangle getParentBounds() {
return getFrame().getParent().getBounds();
return new Rectangle( getFrame().getParent().getSize() );
}
@Override
@@ -504,7 +522,7 @@ debug*/
@Override
public void mousePressed( MouseEvent e ) {
if( !isWindowResizable() )
if( !SwingUtilities.isLeftMouseButton( e ) || !isWindowResizable() )
return;
int xOnScreen = e.getXOnScreen();
@@ -533,7 +551,7 @@ debug*/
@Override
public void mouseReleased( MouseEvent e ) {
if( !isWindowResizable() )
if( !SwingUtilities.isLeftMouseButton( e ) || !isWindowResizable() )
return;
dragLeftOffset = dragRightOffset = dragTopOffset = dragBottomOffset = 0;
@@ -559,7 +577,7 @@ debug*/
@Override
public void mouseDragged( MouseEvent e ) {
if( !isWindowResizable() )
if( !SwingUtilities.isLeftMouseButton( e ) || !isWindowResizable() )
return;
int xOnScreen = e.getXOnScreen();
@@ -579,8 +597,8 @@ debug*/
// top
if( resizeDir == N_RESIZE_CURSOR || resizeDir == NW_RESIZE_CURSOR || resizeDir == NE_RESIZE_CURSOR ) {
newBounds.y = yOnScreen - dragTopOffset;
if( limitToParentBounds() && newBounds.y < 0 )
newBounds.y = 0;
if( limitToParentBounds() )
newBounds.y = Math.max( newBounds.y, getParentBounds().y );
newBounds.height += (oldBounds.y - newBounds.y);
}
@@ -597,8 +615,8 @@ debug*/
// left
if( resizeDir == W_RESIZE_CURSOR || resizeDir == NW_RESIZE_CURSOR || resizeDir == SW_RESIZE_CURSOR ) {
newBounds.x = xOnScreen - dragLeftOffset;
if( limitToParentBounds() && newBounds.x < 0 )
newBounds.x = 0;
if( limitToParentBounds() )
newBounds.x = Math.max( newBounds.x, getParentBounds().x );
newBounds.width += (oldBounds.x - newBounds.x);
}

View File

@@ -27,7 +27,6 @@ import java.awt.Window;
import java.awt.geom.AffineTransform;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.File;
import java.util.Collections;
import java.util.IdentityHashMap;
import java.util.List;
@@ -38,9 +37,7 @@ import javax.swing.Timer;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.event.EventListenerList;
import com.formdev.flatlaf.FlatSystemProperties;
import com.formdev.flatlaf.util.LoggingFacade;
import com.formdev.flatlaf.util.NativeLibrary;
import com.formdev.flatlaf.util.SystemInfo;
//
@@ -84,7 +81,6 @@ class FlatWindowsNativeWindowBorder
private Color colorizationColor;
private int colorizationColorBalance;
private static NativeLibrary nativeLibrary;
private static FlatWindowsNativeWindowBorder instance;
static FlatNativeWindowBorder.Provider getInstance() {
@@ -96,31 +92,8 @@ class FlatWindowsNativeWindowBorder
if( !SystemInfo.isX86 && !SystemInfo.isX86_64 )
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
if( !nativeLibrary.isLoaded() )
if( !FlatNativeLibrary.isLoaded() )
return null;
// create new instance
@@ -129,23 +102,6 @@ class FlatWindowsNativeWindowBorder
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() {
}
@@ -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,
* maximize and close buttons), but not the resize borders (including drop shadow).
*/

View File

@@ -195,11 +195,13 @@ public class JBRCustomDecorations
{
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 inactiveDarkColor = new Color( 0x494A4B );
private boolean colorizationAffectsBorders;
private Color activeColor = defaultActiveBorder;
private Color activeColor;
static JBRWindowTopBorder getInstance() {
if( instance == null )
@@ -250,7 +252,7 @@ public class JBRCustomDecorations
private Color calculateActiveBorderColor() {
if( !colorizationAffectsBorders )
return defaultActiveBorder;
return null;
Color colorizationColor = getColorizationColor();
if( colorizationColor != null ) {
@@ -284,16 +286,12 @@ public class JBRCustomDecorations
@Override
public void paintBorder( Component c, Graphics g, int x, int y, int width, int height ) {
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
// - in light themes
// - in dark themes only for active windows if colorization affects borders
boolean paintTopBorder = !FlatLaf.isLafDark() || (active && colorizationAffectsBorders);
if( !paintTopBorder )
return;
g.setColor( active ? activeColor : inactiveLightColor );
g.setColor( active
? (activeColor != null ? activeColor : (dark ? activeDarkColor : activeLightColor))
: (dark ? inactiveDarkColor : inactiveLightColor) );
HiDPIUtils.paintAtScale1x( (Graphics2D) g, x, y, width, height, this::paintImpl );
}

View File

@@ -143,8 +143,8 @@ public class MigLayoutVisualPadding
//---- class FlatMigInsets ------------------------------------------------
/**
* Marker class to identify our visual paddings and leaf paddings,
* which were set from outside, untouched.
* Marker class to identify our visual paddings and leave paddings
* set from outside untouched.
*/
private static class FlatMigInsets
extends Insets

View File

@@ -59,7 +59,7 @@ import com.formdev.flatlaf.util.Animator.Interpolator;
* </pre>
*
* 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.
*
* @author Karl Tauber
@@ -68,7 +68,7 @@ public interface AnimatedIcon
extends Icon
{
@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 );
}

View File

@@ -47,7 +47,7 @@ public class DerivedColor
Color result = ColorFunctions.applyFunctions( baseColor, functions );
// if the result is equal to the default color, then the original base color
// was passed and we can cache this to avoid color calculations
// was passed, and we can cache this to avoid color calculations
if( !hasBaseOfDefaultColor && result.getRGB() == this.getRGB() ) {
hasBaseOfDefaultColor = true;
baseOfDefaultColorRGB = baseColor.getRGB();

View File

@@ -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.
*
* @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.
*
* @param hsl array containing HSL values
@@ -291,7 +291,7 @@ public class HSLColor
// Calculate the Saturation
float s = 0;
float s;
if (max == min)
s = 0;
@@ -386,7 +386,7 @@ public class HSLColor
s /= 100f;
l /= 100f;
float q = 0;
float q;
if (l < 0.5)
q = l * (1 + s);

View File

@@ -31,7 +31,7 @@ import com.formdev.flatlaf.FlatSystemProperties;
public class HiDPIUtils
{
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 ) {
@@ -48,30 +48,55 @@ public class HiDPIUtils
*/
public static void paintAtScale1x( Graphics2D g, int x, int y, int width, int height, Painter painter ) {
// 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
if( transform.getScaleX() == 1 && transform.getScaleY() == 1 ) {
if( scaleX == 1 && scaleY == 1 ) {
painter.paint( g, x, y, width, height, 1 );
return;
}
// scale rectangle
Rectangle2D.Double scaledRect = scale( transform, x, y, width, height );
Rectangle2D.Double scaledRect = scale( scaleX, scaleY, t, x, y, width, height );
try {
// unscale to factor 1.0 and move origin (to whole numbers)
g.setTransform( new AffineTransform( 1, 0, 0, 1,
Math.floor( scaledRect.x ), Math.floor( scaledRect.y ) ) );
// unscale to factor 1.0, keep rotation and move origin (to whole numbers)
AffineTransform t1x;
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 sheight = (int) scaledRect.height;
// paint
painter.paint( g, 0, 0, swidth, sheight, transform.getScaleX() );
painter.paint( g, 0, 0, swidth, sheight, scaleX );
} finally {
// restore original transform
g.setTransform( transform );
g.setTransform( t );
}
}
@@ -80,20 +105,16 @@ public class HiDPIUtils
* sun.java2d.pipe.PixelToParallelogramConverter.fillRectangle(),
* which is used by Graphics.fillRect().
*/
private static Rectangle2D.Double scale( AffineTransform transform, int x, int y, int width, int height ) {
double dx1 = transform.getScaleX();
double dy2 = transform.getScaleY();
double px = x * dx1 + transform.getTranslateX();
double py = y * dy2 + transform.getTranslateY();
dx1 *= width;
dy2 *= height;
private static Rectangle2D.Double scale( double scaleX, double scaleY, AffineTransform t, int x, int y, int width, int height ) {
double px = (x * scaleX) + t.getTranslateX();
double py = (y * scaleY) + t.getTranslateY();
double newx = normalize( px );
double newy = normalize( py );
dx1 = normalize( px + dx1 ) - newx;
dy2 = normalize( py + dy2 ) - newy;
double newX = normalize( px );
double newY = normalize( py );
double newWidth = normalize( px + (width * scaleX) ) - newX;
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 ) {
@@ -114,7 +135,7 @@ public class HiDPIUtils
* painted too far down on some operating systems.
* The higher the system scale factor is, the more.
* <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 ) {
if( !useTextYCorrection() || !SystemInfo.isWindows )

View File

@@ -89,7 +89,7 @@ public class JavaCompatibility
getClippedStringMethod = cls.getMethod( SystemInfo.isJava_9_orLater
? "getClippedString"
: "clipStringIfNecessary",
new Class[] { JComponent.class, FontMetrics.class, String.class, int.class } );
JComponent.class, FontMetrics.class, String.class, int.class );
} catch( Exception ex ) {
LoggingFacade.INSTANCE.logSevere( null, ex );
throw new RuntimeException( ex );

View File

@@ -72,7 +72,7 @@ public class MultiResolutionImageSupport
* <p>
* The given dimensions array is only used for {@link #getResolutionVariants(Image)}.
* The producer function may be invoked with any dimension (that is not contained in
* dimensions array) and is expected to produce a image for the passed in dimension.
* dimensions array) and is expected to produce an image for the passed in dimension.
*
* @param baseImageIndex index of the base image in the dimensions array
* @param dimensions dimensions of resolution variants (sorted by size; smallest first)
@@ -92,7 +92,7 @@ public class MultiResolutionImageSupport
* for "disabled" state.
*
* @param image a multi-resolution image that is mapped using the given mapper function
* @param mapper mapper function that maps a single resolution variant to a new image (e.g. applying an filter)
* @param mapper mapper function that maps a single resolution variant to a new image (e.g. applying a filter)
* @return a multi-resolution image on Java 9 or later; a mapped image on Java 8
*/
public static Image map( Image image, Function<Image, Image> mapper ) {
@@ -104,7 +104,7 @@ public class MultiResolutionImageSupport
* <p>
* If the given image is a multi-resolution image then invokes
* {@code java.awt.image.MultiResolutionImage.getResolutionVariant(destImageWidth, destImageHeight)}.
* Otherwise returns the given image.
* Otherwise, returns the given image.
*/
public static Image getResolutionVariant( Image image, int destImageWidth, int destImageHeight ) {
return image;
@@ -115,7 +115,7 @@ public class MultiResolutionImageSupport
* <p>
* If the given image is a multi-resolution image then invokes
* {@code java.awt.image.MultiResolutionImage.getResolutionVariants()}.
* Otherwise returns a list containing only the given image.
* Otherwise, returns a list containing only the given image.
*/
public static List<Image> getResolutionVariants( Image image ) {
return Collections.singletonList( image );

View File

@@ -143,16 +143,19 @@ public class NativeLibrary
}
}
/**
* Add prefix and suffix to library name.
* <ul>
* <li>Windows: libraryName + ".dll"
* <li>macOS: "lib" + libraryName + ".dylib"
* <li>Linux: "lib" + libraryName + ".so"
* </ul>
*/
private static String decorateLibraryName( String libraryName ) {
if( SystemInfo.isWindows )
return libraryName.concat( ".dll" );
String suffix = SystemInfo.isMacOS ? ".dylib" : ".so";
int sep = libraryName.lastIndexOf( '/' );
return (sep >= 0)
? libraryName.substring( 0, sep + 1 ) + "lib" + libraryName.substring( sep + 1 ) + suffix
: "lib" + libraryName + suffix;
? libraryName.substring( 0, sep + 1 ) + System.mapLibraryName( libraryName.substring( sep + 1 ) )
: System.mapLibraryName( libraryName );
}
private static void log( String msg, Throwable thrown ) {
@@ -195,7 +198,7 @@ public class NativeLibrary
// for loaded native libraries, they will be deleted on next application startup.
// The default temporary directory may contain hundreds or thousands of files.
// To make searching for "marked for deletion" files as fast as possible,
// use a sub directory that contains only our temporary native libraries.
// use a subdirectory that contains only our temporary native libraries.
tmpdir += "\\flatlaf.temp";
}

View File

@@ -50,6 +50,7 @@ public class SystemInfo
public static final long javaVersion;
public static final boolean isJava_9_orLater;
public static final boolean isJava_11_orLater;
/** @since 2.3 */ public static final boolean isJava_12_orLater;
public static final boolean isJava_15_orLater;
/** @since 2 */ public static final boolean isJava_17_orLater;
/** @since 2 */ public static final boolean isJava_18_orLater;
@@ -66,6 +67,9 @@ public class SystemInfo
/** @since 1.1.2 */ public static final boolean isWebswing;
/** @since 1.1.1 */ public static final boolean isWinPE;
// features
/** @since 2.3 */ public static final boolean isMacFullWindowContentSupported;
static {
// platforms
String osName = System.getProperty( "os.name" ).toLowerCase( Locale.ENGLISH );
@@ -92,6 +96,7 @@ public class SystemInfo
javaVersion = scanVersion( System.getProperty( "java.version" ) );
isJava_9_orLater = (javaVersion >= toVersion( 9, 0, 0, 0 ));
isJava_11_orLater = (javaVersion >= toVersion( 11, 0, 0, 0 ));
isJava_12_orLater = (javaVersion >= toVersion( 12, 0, 0, 0 ));
isJava_15_orLater = (javaVersion >= toVersion( 15, 0, 0, 0 ));
isJava_17_orLater = (javaVersion >= toVersion( 17, 0, 0, 0 ));
isJava_18_orLater = (javaVersion >= toVersion( 18, 0, 0, 0 ));
@@ -108,6 +113,12 @@ public class SystemInfo
isProjector = Boolean.getBoolean( "org.jetbrains.projector.server.enable" );
isWebswing = (System.getProperty( "webswing.rootDir" ) != null);
isWinPE = isWindows && "X:\\Windows\\System32".equalsIgnoreCase( System.getProperty( "user.dir" ) );
// features
// available since Java 12; backported to Java 11.0.8 and 8u292
isMacFullWindowContentSupported =
javaVersion >= toVersion( 11, 0, 8, 0 ) ||
(javaVersion >= toVersion( 1, 8, 0, 292 ) && !isJava_9_orLater);
}
public static long scanVersion( String version ) {

View File

@@ -103,7 +103,7 @@ public class UIScale
// Java 9 and later supports per-monitor scaling
jreHiDPI = true;
} else if( SystemInfo.isJetBrainsJVM ) {
// IntelliJ IDEA ships its own JetBrains Java 8 JRE that may supports per-monitor scaling
// IntelliJ IDEA ships its own JetBrains Java 8 JRE that may support per-monitor scaling
// see com.intellij.ui.JreHiDpiUtil.isJreHiDPIEnabled()
try {
GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
@@ -411,7 +411,7 @@ public class UIScale
* Scales the given dimension with the user scale factor.
* <p>
* If user scale factor is 1, then the given dimension is simply returned.
* Otherwise a new instance of {@link Dimension} or {@link DimensionUIResource}
* Otherwise, a new instance of {@link Dimension} or {@link DimensionUIResource}
* is returned, depending on whether the passed dimension implements {@link UIResource}.
*/
public static Dimension scale( Dimension dimension ) {
@@ -427,7 +427,7 @@ public class UIScale
* Scales the given insets with the user scale factor.
* <p>
* If user scale factor is 1, then the given insets is simply returned.
* Otherwise a new instance of {@link Insets} or {@link InsetsUIResource}
* Otherwise, a new instance of {@link Insets} or {@link InsetsUIResource}
* is returned, depending on whether the passed dimension implements {@link UIResource}.
*/
public static Insets scale( Insets insets ) {

View File

@@ -24,7 +24,7 @@
#
# NOTE: Avoid copying the whole content of this file to own properties files.
# This will make upgrading to newer FlatLaf versions complex and error-prone.
# Instead copy and modify only those properties that you need to alter.
# Instead, copy and modify only those properties that you need to alter.
#
# Colors and style mostly based on Darcula theme from IntelliJ IDEA Community Edition,

View File

@@ -23,7 +23,7 @@
#
# NOTE: Avoid copying the whole content of this file to own properties files.
# This will make upgrading to newer FlatLaf versions complex and error-prone.
# Instead copy and modify only those properties that you need to alter.
# Instead, copy and modify only those properties that you need to alter.
#
# Colors and style mostly based on Darcula theme from IntelliJ IDEA Community Edition,
@@ -240,6 +240,7 @@ MenuBar.borderColor = $Separator.foreground
#---- PasswordField ----
PasswordField.capsLockIconColor = #ffffff64
PasswordField.revealIconColor = @foreground
#---- Popup ----
@@ -251,6 +252,7 @@ Popup.dropShadowOpacity = 0.25
#---- PopupMenu ----
PopupMenu.borderColor = tint(@background,17%)
PopupMenu.hoverScrollArrowBackground = lighten(@background,5%)
#---- ProgressBar ----
@@ -304,6 +306,7 @@ SplitPaneDivider.draggingColor = $Component.borderColor
#---- TabbedPane ----
TabbedPane.underlineColor = @accentUnderlineColor
TabbedPane.inactiveUnderlineColor = mix(@accentUnderlineColor,$TabbedPane.background,60%)
TabbedPane.disabledUnderlineColor = lighten(@background,23%)
TabbedPane.hoverColor = darken($TabbedPane.background,5%,derived noAutoInverse)
TabbedPane.focusColor = mix(@selectionBackground,$TabbedPane.background,25%)
@@ -322,7 +325,7 @@ TabbedPane.closePressedForeground = $TabbedPane.closeHoverForeground
#---- Table ----
Table.gridColor = lighten($Table.background,5%)
Table.gridColor = lighten($Table.background,8%)
#---- TableHeader ----
@@ -367,6 +370,6 @@ Tree.hash = lighten($Tree.background,5%)
focusable: false; \
toolbar.margin: 1,1,1,1; \
toolbar.spacingInsets: 1,1,1,1; \
toolbar.hoverBackground: fade(Actions.GreyInline,30%,lazy); \
toolbar.pressedBackground: fade(Actions.GreyInline,40%,lazy); \
toolbar.selectedBackground: fade(Actions.GreyInline,50%,lazy)
toolbar.hoverBackground: lighten($TextField.background,5%); \
toolbar.pressedBackground: lighten($TextField.background,10%); \
toolbar.selectedBackground: lighten($TextField.background,15%)

View File

@@ -24,7 +24,7 @@
#
# NOTE: Avoid copying the whole content of this file to own properties files.
# This will make upgrading to newer FlatLaf versions complex and error-prone.
# Instead copy and modify only those properties that you need to alter.
# Instead, copy and modify only those properties that you need to alter.
#
# Colors and style mostly based on IntelliJ theme from IntelliJ IDEA Community Edition,

View File

@@ -23,7 +23,7 @@
#
# NOTE: Avoid copying the whole content of this file to own properties files.
# This will make upgrading to newer FlatLaf versions complex and error-prone.
# Instead copy and modify only those properties that you need to alter.
# Instead, copy and modify only those properties that you need to alter.
#
#---- typography / fonts ----
@@ -64,7 +64,7 @@ light.font = +0
semibold.font = +0
# monospaced
[win]monospaced.font = Consolas, "Courier New", Monospaced
[win]monospaced.font = Monospaced
[mac]monospaced.font = Menlo, Monospaced
[linux]monospaced.font = "Liberation Mono", "Ubuntu Mono", Monospaced
monospaced.font = Monospaced
@@ -332,6 +332,7 @@ FileView.fileIcon = com.formdev.flatlaf.icons.FlatFileViewFileIcon
FileView.computerIcon = com.formdev.flatlaf.icons.FlatFileViewComputerIcon
FileView.hardDriveIcon = com.formdev.flatlaf.icons.FlatFileViewHardDriveIcon
FileView.floppyDriveIcon = com.formdev.flatlaf.icons.FlatFileViewFloppyDriveIcon
FileView.fullRowSelection = true
#---- FormattedTextField ----
@@ -487,7 +488,6 @@ PasswordField.showCapsLock = true
PasswordField.showRevealButton = false
PasswordField.capsLockIcon = com.formdev.flatlaf.icons.FlatCapsLockIcon
PasswordField.revealIcon = com.formdev.flatlaf.icons.FlatRevealIcon
PasswordField.revealIconColor = lazy(Actions.Grey)
#---- Popup ----
@@ -501,6 +501,7 @@ Popup.dropShadowInsets = -4,-4,4,4
PopupMenu.border = com.formdev.flatlaf.ui.FlatPopupMenuBorder
PopupMenu.borderInsets = 4,1,4,1
PopupMenu.background = @menuBackground
PopupMenu.scrollArrowColor = @buttonArrowColor
#---- PopupMenuSeparator ----
@@ -555,6 +556,7 @@ RootPane.honorDialogMinimumSizeOnResize = true
#---- ScrollBar ----
ScrollBar.width = 10
ScrollBar.minimumButtonSize = 12,12
ScrollBar.minimumThumbSize = 10,10
ScrollBar.maximumThumbSize = 100000,100000
ScrollBar.trackInsets = 0,0,0,0
@@ -787,11 +789,16 @@ TitlePane.noIconLeftGap = 8
TitlePane.iconSize = 16,16
TitlePane.iconMargins = 3,8,3,8
TitlePane.titleMargins = 3,0,3,0
TitlePane.titleMinimumWidth = 60
TitlePane.buttonSize = 44,30
TitlePane.buttonMinimumWidth = 30
TitlePane.buttonMaximizedHeight = 22
TitlePane.centerTitle = false
TitlePane.centerTitleIfMenuBarEmbedded = true
TitlePane.menuBarTitleGap = 20
TitlePane.showIconBesideTitle = false
TitlePane.menuBarTitleGap = 40
TitlePane.menuBarTitleMinimumGap = 12
TitlePane.menuBarResizeHeight = 4
TitlePane.closeIcon = com.formdev.flatlaf.icons.FlatWindowCloseIcon
TitlePane.iconifyIcon = com.formdev.flatlaf.icons.FlatWindowIconifyIcon
TitlePane.maximizeIcon = com.formdev.flatlaf.icons.FlatWindowMaximizeIcon
@@ -827,6 +834,7 @@ ToggleButton.tab.underlineHeight = 2
ToggleButton.tab.underlineColor = $TabbedPane.underlineColor
ToggleButton.tab.disabledUnderlineColor = $TabbedPane.disabledUnderlineColor
ToggleButton.tab.selectedBackground = $?TabbedPane.selectedBackground
ToggleButton.tab.selectedForeground = $?TabbedPane.selectedForeground
ToggleButton.tab.hoverBackground = $TabbedPane.hoverColor
ToggleButton.tab.focusBackground = $TabbedPane.focusColor

View File

@@ -23,7 +23,7 @@
#
# NOTE: Avoid copying the whole content of this file to own properties files.
# This will make upgrading to newer FlatLaf versions complex and error-prone.
# Instead copy and modify only those properties that you need to alter.
# Instead, copy and modify only those properties that you need to alter.
#
# Colors and style mostly based on IntelliJ theme from IntelliJ IDEA Community Edition,
@@ -247,6 +247,7 @@ MenuBar.borderColor = $Separator.foreground
#---- PasswordField ----
PasswordField.capsLockIconColor = #00000064
PasswordField.revealIconColor = tint(@foreground,40%)
#---- Popup ----
@@ -258,6 +259,7 @@ Popup.dropShadowOpacity = 0.15
#---- PopupMenu ----
PopupMenu.borderColor = shade(@background,28%)
PopupMenu.hoverScrollArrowBackground = darken(@background,5%)
#---- ProgressBar ----
@@ -311,6 +313,7 @@ SplitPaneDivider.draggingColor = $Component.borderColor
#---- TabbedPane ----
TabbedPane.underlineColor = @accentUnderlineColor
TabbedPane.inactiveUnderlineColor = mix(@accentUnderlineColor,$TabbedPane.background,50%)
TabbedPane.disabledUnderlineColor = darken(@background,28%)
TabbedPane.hoverColor = darken($TabbedPane.background,7%,derived)
TabbedPane.focusColor = mix(@selectionBackground,$TabbedPane.background,10%)
@@ -329,7 +332,7 @@ TabbedPane.closePressedForeground = $TabbedPane.closeHoverForeground
#---- Table ----
Table.gridColor = darken($Table.background,5%)
Table.gridColor = darken($Table.background,8%)
#---- TableHeader ----
@@ -374,6 +377,6 @@ Tree.hash = darken($Tree.background,10%)
focusable: false; \
toolbar.margin: 1,1,1,1; \
toolbar.spacingInsets: 1,1,1,1; \
toolbar.hoverBackground: fade(Actions.GreyInline,10%,lazy); \
toolbar.pressedBackground: fade(Actions.GreyInline,20%,lazy); \
toolbar.selectedBackground: fade(Actions.GreyInline,30%,lazy)
toolbar.hoverBackground: darken($TextField.background,4%); \
toolbar.pressedBackground: darken($TextField.background,8%); \
toolbar.selectedBackground: darken($TextField.background,12%)

View File

@@ -67,6 +67,11 @@ Button.default.hoverBorderColor = null
[dark]CheckBoxMenuItem.icon.checkmarkColor=#fff9
#---- Component ----
Component.accentColor = lazy(ProgressBar.foreground)
#---- HelpButton ----
HelpButton.hoverBorderColor = null
@@ -77,6 +82,13 @@ HelpButton.hoverBorderColor = null
Slider.focusedColor = fade($Component.focusColor,40%,derived)
#---- TabbedPane ----
# colors from JBUI.CurrentTheme.DefaultTabs.inactiveUnderlineColor()
[light]TabbedPane.inactiveUnderlineColor = #9ca7b8
[dark]TabbedPane.inactiveUnderlineColor = #747a80
#---- ToggleButton ----
ToggleButton.startBackground = $ToggleButton.background
@@ -91,36 +103,37 @@ ToggleButton.endBackground = $ToggleButton.background
@ijMenuCheckBackgroundL20 = lighten(@selectionBackground,20%,derived noAutoInverse)
@ijMenuCheckBackgroundD10 = darken(@selectionBackground,10%,derived noAutoInverse)
[Arc_Theme]CheckBoxMenuItem.foreground = lazy(MenuItem.foreground)
[Arc_Theme]PopupMenu.foreground = lazy(MenuItem.foreground)
[Arc_Theme]RadioButtonMenuItem.foreground = lazy(MenuItem.foreground)
[Arc_Theme]CheckBoxMenuItem.foreground = lazy(MenuItem.foreground)
[Arc_Theme]PopupMenu.foreground = lazy(MenuItem.foreground)
[Arc_Theme]RadioButtonMenuItem.foreground = lazy(MenuItem.foreground)
[Arc_Theme]ProgressBar.selectionBackground = #000
[Arc_Theme]ProgressBar.selectionForeground = #fff
[Arc_Theme]List.selectionInactiveForeground = #fff
[Arc_Theme]Table.selectionInactiveForeground = #fff
[Arc_Theme]Tree.selectionInactiveForeground = #fff
[Arc_Theme_-_Orange]CheckBoxMenuItem.foreground = lazy(MenuItem.foreground)
[Arc_Theme_-_Orange]PopupMenu.foreground = lazy(MenuItem.foreground)
[Arc_Theme_-_Orange]RadioButtonMenuItem.foreground = lazy(MenuItem.foreground)
[Arc_Theme_-_Orange]CheckBoxMenuItem.foreground = lazy(MenuItem.foreground)
[Arc_Theme_-_Orange]PopupMenu.foreground = lazy(MenuItem.foreground)
[Arc_Theme_-_Orange]RadioButtonMenuItem.foreground = lazy(MenuItem.foreground)
[Arc_Theme_-_Orange]ProgressBar.selectionBackground = #000
[Arc_Theme_-_Orange]ProgressBar.selectionForeground = #fff
[Arc_Theme_-_Orange]List.selectionInactiveForeground = #fff
[Arc_Theme_-_Orange]Table.selectionInactiveForeground = #fff
[Arc_Theme_-_Orange]Tree.selectionInactiveForeground = #fff
[Arc_Theme_Dark]CheckBoxMenuItem.foreground = lazy(MenuItem.foreground)
[Arc_Theme_Dark]PopupMenu.foreground = lazy(MenuItem.foreground)
[Arc_Theme_Dark]RadioButtonMenuItem.foreground = lazy(MenuItem.foreground)
[Arc_Theme_Dark]CheckBoxMenuItem.foreground = lazy(MenuItem.foreground)
[Arc_Theme_Dark]PopupMenu.foreground = lazy(MenuItem.foreground)
[Arc_Theme_Dark]RadioButtonMenuItem.foreground = lazy(MenuItem.foreground)
[Arc_Theme_Dark]ProgressBar.selectionBackground = #ddd
[Arc_Theme_Dark]ProgressBar.selectionForeground = #ddd
[Arc_Theme_Dark_-_Orange]CheckBoxMenuItem.foreground = lazy(MenuItem.foreground)
[Arc_Theme_Dark_-_Orange]PopupMenu.foreground = lazy(MenuItem.foreground)
[Arc_Theme_Dark_-_Orange]RadioButtonMenuItem.foreground = lazy(MenuItem.foreground)
[Arc_Theme_Dark_-_Orange]CheckBoxMenuItem.foreground = lazy(MenuItem.foreground)
[Arc_Theme_Dark_-_Orange]PopupMenu.foreground = lazy(MenuItem.foreground)
[Arc_Theme_Dark_-_Orange]RadioButtonMenuItem.foreground = lazy(MenuItem.foreground)
[Arc_Theme_Dark_-_Orange]ProgressBar.selectionBackground = #ddd
[Arc_Theme_Dark_-_Orange]ProgressBar.selectionForeground = #fff
[Cobalt_2]Component.accentColor = lazy(Component.focusColor)
[Cobalt_2]CheckBox.icon.background = #002946
[Cobalt_2]CheckBox.icon.checkmarkColor = #002946
[Cobalt_2]MenuItem.checkBackground = @ijMenuCheckBackgroundL10
@@ -129,37 +142,50 @@ ToggleButton.endBackground = $ToggleButton.background
[Cyan_light]MenuItem.checkBackground = @ijMenuCheckBackgroundL20
[Cyan_light]MenuItem.underlineSelectionCheckBackground = @ijMenuCheckBackgroundL20
[Dark_Flat_Theme]Component.accentColor = lazy(List.selectionBackground)
[Dark_Flat_Theme]TableHeader.background = #3B3B3B
[Dark_purple]Slider.focusedColor = fade($Component.focusColor,70%,derived)
[Dracula---Zihan_Ma]Component.accentColor = lazy(Component.focusColor)
[Dracula---Zihan_Ma]ProgressBar.selectionBackground = #fff
[Dracula---Zihan_Ma]ProgressBar.selectionForeground = #fff
[Gradianto_Dark_Fuchsia]MenuItem.checkBackground = @ijMenuCheckBackgroundL10
[Gradianto_Dark_Fuchsia]MenuItem.underlineSelectionCheckBackground = @ijMenuCheckBackgroundL10
[Gruvbox_Dark_Hard]Component.accentColor = lazy(TabbedPane.underlineColor)
[Gruvbox_Dark_Hard]ToggleButton.selectedBackground = $ToggleButton.selectedBackground
[Gruvbox_Dark_Hard]ToggleButton.toolbar.selectedBackground = $ToggleButton.toolbar.selectedBackground
[Gruvbox_Dark_Medium]Component.accentColor = lazy(TabbedPane.underlineColor)
[Gruvbox_Dark_Medium]ToggleButton.selectedBackground = $ToggleButton.selectedBackground
[Gruvbox_Dark_Medium]ToggleButton.toolbar.selectedBackground = $ToggleButton.toolbar.selectedBackground
[Gruvbox_Dark_Soft]Component.accentColor = lazy(TabbedPane.underlineColor)
[Gruvbox_Dark_Soft]MenuItem.checkBackground = @ijMenuCheckBackgroundL10
[Gruvbox_Dark_Soft]MenuItem.underlineSelectionCheckBackground = @ijMenuCheckBackgroundL10
[Gruvbox_Dark_Soft]ToggleButton.selectedBackground = $ToggleButton.selectedBackground
[Gruvbox_Dark_Soft]ToggleButton.toolbar.selectedBackground = $ToggleButton.toolbar.selectedBackground
[Hiberbee_Dark]TabbedPane.focusColor = #5A5A5A
[Hiberbee_Dark]ToggleButton.selectedBackground = $ToggleButton.selectedBackground
[Hiberbee_Dark]TabbedPane.selectedBackground = #434241
[Hiberbee_Dark]TabbedPane.selectedForeground = #70D7FF
[Hiberbee_Dark]ToggleButton.selectedBackground = $ToggleButton.selectedBackground
[Hiberbee_Dark]ToggleButton.toolbar.selectedBackground = $ToggleButton.toolbar.selectedBackground
[High_contrast]Component.accentColor = lazy(Component.focusColor)
[High_contrast]ToggleButton.selectedBackground = #fff
[High_contrast]ToggleButton.selectedForeground = #000
[High_contrast]ToggleButton.disabledSelectedBackground = #444
[High_contrast]ToggleButton.toolbar.selectedBackground = #fff
[High_contrast][style]Button.inTextField = \
toolbar.hoverBackground: #444; \
toolbar.pressedBackground: #666; \
toolbar.selectedBackground: #fff
[High_contrast][style]ToggleButton.inTextField = $[High_contrast][style]Button.inTextField
[Light_Flat]Component.accentColor = lazy(TabbedPane.underlineColor)
[Light_Flat]TableHeader.background = #E5E5E9
[Monocai]MenuItem.checkBackground = @ijMenuCheckBackgroundL10
@@ -175,6 +201,8 @@ ToggleButton.endBackground = $ToggleButton.background
[Monocai]RadioButtonMenuItem.acceleratorForeground = @Monocai.acceleratorForeground
[Monocai]RadioButtonMenuItem.acceleratorSelectionForeground = @Monocai.acceleratorSelectionForeground
[Monokai_Pro]TitledBorder.titleColor = @foreground
[Nord]MenuItem.checkBackground = @ijMenuCheckBackgroundL10
[Nord]MenuItem.underlineSelectionCheckBackground = @ijMenuCheckBackgroundL10
@@ -182,8 +210,12 @@ ToggleButton.endBackground = $ToggleButton.background
[One_Dark]MenuItem.underlineSelectionCheckBackground = @ijMenuCheckBackgroundL10
[One_Dark]Slider.focusedColor = fade(#568af2,40%)
[Solarized_Dark---4lex4]Component.accentColor = lazy(TabbedPane.underlineColor)
[Solarized_Dark---4lex4]Slider.focusedColor = fade($Component.focusColor,80%,derived)
[Solarized_Light---4lex4]Component.accentColor = lazy(TabbedPane.underlineColor)
[vuesion-theme]Component.accentColor = lazy(Button.default.endBackground)
[vuesion-theme]MenuItem.checkBackground = @ijMenuCheckBackgroundL10
[vuesion-theme]MenuItem.underlineSelectionCheckBackground = @ijMenuCheckBackgroundL10
[vuesion-theme]Slider.trackValueColor = #ececee

View File

@@ -0,0 +1,65 @@
#
# 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.
#
#---- FileChooser ----
#fields
FileChooser.lookInLabel.textAndMnemonic = Buscar &en:
FileChooser.saveInLabelText = Guardar en:
FileChooser.fileNameLabel.textAndMnemonic = &Nombre de fichero:
FileChooser.folderNameLabel.textAndMnemonic = &Nombre de carpeta:
FileChooser.filesOfTypeLabel.textAndMnemonic = Ficheros de &Tipo:
# toolbar
FileChooser.upFolderToolTipText = Subir un nivel
FileChooser.upFolderAccessibleName = Subir
FileChooser.homeFolderToolTipText = Inicio
FileChooser.homeFolderAccessibleName = Inicio
FileChooser.newFolderToolTipText = Crear nueva carpeta
FileChooser.newFolderAccessibleName = Nueva carpeta
FileChooser.listViewButtonToolTipText = Lista
FileChooser.listViewButtonAccessibleName = Lista
FileChooser.detailsViewButtonToolTipText = Detalles
FileChooser.detailsViewButtonAccessibleName = Detalles
# details table header
FileChooser.fileNameHeaderText = Nombre
FileChooser.fileSizeHeaderText = Tama\u00F1o
FileChooser.fileTypeHeaderText = Tipo
FileChooser.fileDateHeaderText = Modificado
FileChooser.fileAttrHeaderText = Atributos
# popup menu
FileChooser.viewMenuLabelText = Ver
FileChooser.refreshActionLabelText = Refrescar
FileChooser.newFolderActionLabelText = Nueva carpeta
FileChooser.listViewActionLabelText = Lista
FileChooser.detailsViewActionLabelText = Detalles
#---- SplitPaneDivider ----
SplitPaneDivider.collapseLeftToolTipText = Contraer Panel Izquierdo
SplitPaneDivider.collapseRightToolTipText = Contraer panel Derecho
SplitPaneDivider.collapseTopToolTipText = Contraer panel Superior
SplitPaneDivider.collapseBottomToolTipText = Contraer Panel Inferior
SplitPaneDivider.expandLeftToolTipText = Expandir Panel Izquierdo
SplitPaneDivider.expandRightToolTipText = Expandir Panel Derecho
SplitPaneDivider.expandTopToolTipText = Expandir Panel Superior
SplitPaneDivider.expandBottomToolTipText = Expandir Panel Inferior
#---- TabbedPane ----
TabbedPane.moreTabsButtonToolTipText = Mostrar Pesta\u00F1as Ocultas

View File

@@ -96,19 +96,26 @@ public class TestFlatStyleableInfo
"minimumWidth", int.class,
"focusedBackground", Color.class,
"focusedForeground", Color.class,
"hoverBackground", Color.class,
"hoverForeground", Color.class,
"pressedBackground", Color.class,
"pressedForeground", Color.class,
"selectedBackground", Color.class,
"selectedForeground", Color.class,
"disabledBackground", Color.class,
"disabledText", Color.class,
"disabledSelectedBackground", Color.class,
"disabledSelectedForeground", Color.class,
"default.background", Color.class,
"default.foreground", Color.class,
"default.focusedBackground", Color.class,
"default.focusedForeground", Color.class,
"default.hoverBackground", Color.class,
"default.hoverForeground", Color.class,
"default.pressedBackground", Color.class,
"default.pressedForeground", Color.class,
"default.boldText", boolean.class,
"paintShadow", boolean.class,
@@ -118,8 +125,13 @@ public class TestFlatStyleableInfo
"toolbar.spacingInsets", Insets.class,
"toolbar.hoverBackground", Color.class,
"toolbar.hoverForeground", Color.class,
"toolbar.pressedBackground", Color.class,
"toolbar.pressedForeground", Color.class,
"toolbar.selectedBackground", Color.class,
"toolbar.selectedForeground", Color.class,
"toolbar.disabledSelectedBackground", Color.class,
"toolbar.disabledSelectedForeground", Color.class,
"buttonType", String.class,
"squareSize", boolean.class,
@@ -422,6 +434,10 @@ public class TestFlatStyleableInfo
FlatPopupMenuUI ui = (FlatPopupMenuUI) c.getUI();
Map<String, Class<?>> expected = expectedMap(
"arrowType", String.class,
"scrollArrowColor", Color.class,
"hoverScrollArrowBackground", Color.class,
"borderInsets", Insets.class,
"borderColor", Color.class
);
@@ -539,6 +555,7 @@ public class TestFlatStyleableInfo
"maximumThumbSize", Dimension.class,
"allowsAbsolutePositioning", boolean.class,
"minimumButtonSize", Dimension.class,
"trackInsets", Insets.class,
"thumbInsets", Insets.class,
"trackArc", int.class,
@@ -688,6 +705,7 @@ public class TestFlatStyleableInfo
"selectedBackground", Color.class,
"selectedForeground", Color.class,
"underlineColor", Color.class,
"inactiveUnderlineColor", Color.class,
"disabledUnderlineColor", Color.class,
"hoverColor", Color.class,
"focusColor", Color.class,
@@ -857,8 +875,11 @@ public class TestFlatStyleableInfo
"tab.underlineColor", Color.class,
"tab.disabledUnderlineColor", Color.class,
"tab.selectedBackground", Color.class,
"tab.selectedForeground", Color.class,
"tab.hoverBackground", Color.class,
"tab.focusBackground", Color.class
"tab.hoverForeground", Color.class,
"tab.focusBackground", Color.class,
"tab.focusForeground", Color.class
);
// FlatToggleButtonUI extends FlatButtonUI

View File

@@ -221,19 +221,26 @@ public class TestFlatStyling
ui.applyStyle( b, "minimumWidth: 100" );
ui.applyStyle( b, "focusedBackground: #fff" );
ui.applyStyle( b, "focusedForeground: #fff" );
ui.applyStyle( b, "hoverBackground: #fff" );
ui.applyStyle( b, "hoverForeground: #fff" );
ui.applyStyle( b, "pressedBackground: #fff" );
ui.applyStyle( b, "pressedForeground: #fff" );
ui.applyStyle( b, "selectedBackground: #fff" );
ui.applyStyle( b, "selectedForeground: #fff" );
ui.applyStyle( b, "disabledBackground: #fff" );
ui.applyStyle( b, "disabledText: #fff" );
ui.applyStyle( b, "disabledSelectedBackground: #fff" );
ui.applyStyle( b, "disabledSelectedForeground: #fff" );
ui.applyStyle( b, "default.background: #fff" );
ui.applyStyle( b, "default.foreground: #fff" );
ui.applyStyle( b, "default.focusedBackground: #fff" );
ui.applyStyle( b, "default.focusedForeground: #fff" );
ui.applyStyle( b, "default.hoverBackground: #fff" );
ui.applyStyle( b, "default.hoverForeground: #fff" );
ui.applyStyle( b, "default.pressedBackground: #fff" );
ui.applyStyle( b, "default.pressedForeground: #fff" );
ui.applyStyle( b, "default.boldText: true" );
ui.applyStyle( b, "paintShadow: true" );
@@ -243,8 +250,13 @@ public class TestFlatStyling
ui.applyStyle( b, "toolbar.spacingInsets: 1,2,3,4" );
ui.applyStyle( b, "toolbar.hoverBackground: #fff" );
ui.applyStyle( b, "toolbar.hoverForeground: #fff" );
ui.applyStyle( b, "toolbar.pressedBackground: #fff" );
ui.applyStyle( b, "toolbar.pressedForeground: #fff" );
ui.applyStyle( b, "toolbar.selectedBackground: #fff" );
ui.applyStyle( b, "toolbar.selectedForeground: #fff" );
ui.applyStyle( b, "toolbar.disabledSelectedBackground: #fff" );
ui.applyStyle( b, "toolbar.disabledSelectedForeground: #fff" );
ui.applyStyle( b, "buttonType: help" );
ui.applyStyle( b, "squareSize: true" );
@@ -565,6 +577,10 @@ public class TestFlatStyling
JPopupMenu c = new JPopupMenu();
FlatPopupMenuUI ui = (FlatPopupMenuUI) c.getUI();
ui.applyStyle( "arrowType: chevron" );
ui.applyStyle( "scrollArrowColor: #fff" );
ui.applyStyle( "hoverScrollArrowBackground: #fff" );
ui.applyStyle( "borderInsets: 1,2,3,4" );
ui.applyStyle( "borderColor: #fff" );
@@ -684,6 +700,7 @@ public class TestFlatStyling
ui.applyStyle( "maximumThumbSize: 1,2" );
ui.applyStyle( "allowsAbsolutePositioning: true" );
ui.applyStyle( "minimumButtonSize: 1,2" );
ui.applyStyle( "trackInsets: 1,2,3,4" );
ui.applyStyle( "thumbInsets: 1,2,3,4" );
ui.applyStyle( "trackArc: 5" );
@@ -857,6 +874,7 @@ public class TestFlatStyling
ui.applyStyle( "selectedBackground: #fff" );
ui.applyStyle( "selectedForeground: #fff" );
ui.applyStyle( "underlineColor: #fff" );
ui.applyStyle( "inactiveUnderlineColor: #fff" );
ui.applyStyle( "disabledUnderlineColor: #fff" );
ui.applyStyle( "hoverColor: #fff" );
ui.applyStyle( "focusColor: #fff" );
@@ -1067,8 +1085,11 @@ public class TestFlatStyling
ui.applyStyle( b, "tab.underlineColor: #fff" );
ui.applyStyle( b, "tab.disabledUnderlineColor: #fff" );
ui.applyStyle( b, "tab.selectedBackground: #fff" );
ui.applyStyle( b, "tab.selectedForeground: #fff" );
ui.applyStyle( b, "tab.hoverBackground: #fff" );
ui.applyStyle( b, "tab.hoverForeground: #fff" );
ui.applyStyle( b, "tab.focusBackground: #fff" );
ui.applyStyle( b, "tab.focusForeground: #fff" );
}
@Test

View File

@@ -38,8 +38,8 @@ import com.formdev.flatlaf.FlatLightLaf;
import com.formdev.flatlaf.demo.HintManager.Hint;
import com.formdev.flatlaf.demo.extras.*;
import com.formdev.flatlaf.demo.intellijthemes.*;
import com.formdev.flatlaf.extras.FlatDesktop;
import com.formdev.flatlaf.extras.FlatAnimatedLafChange;
import com.formdev.flatlaf.extras.FlatDesktop;
import com.formdev.flatlaf.extras.FlatSVGIcon;
import com.formdev.flatlaf.extras.FlatUIDefaultsInspector;
import com.formdev.flatlaf.extras.components.FlatButton;
@@ -82,13 +82,33 @@ class DemoFrame
if( tabIndex >= 0 && tabIndex < tabbedPane.getTabCount() && tabIndex != tabbedPane.getSelectedIndex() )
tabbedPane.setSelectedIndex( tabIndex );
// hide some menu items on macOS
// macOS (see https://www.formdev.com/flatlaf/macos/)
if( SystemInfo.isMacOS ) {
// hide menu items that are in macOS application menu
exitMenuItem.setVisible( false );
aboutMenuItem.setVisible( false );
// do not use HTML text on macOS
// do not use HTML text in menu items because this is not supported in macOS screen menu
htmlMenuItem.setText( "some text" );
if( SystemInfo.isMacFullWindowContentSupported ) {
// expand window content into window title bar and make title bar transparent
getRootPane().putClientProperty( "apple.awt.fullWindowContent", true );
getRootPane().putClientProperty( "apple.awt.transparentTitleBar", true );
// hide window title
if( SystemInfo.isJava_17_orLater )
getRootPane().putClientProperty( "apple.awt.windowTitleVisible", false );
else
setTitle( null );
// add gap to left side of toolbar
toolBar.add( Box.createHorizontalStrut( 70 ), 0 );
}
// enable full screen mode for this window (for Java 8 - 10; not necessary for Java 11+)
if( !SystemInfo.isJava_11_orLater )
getRootPane().putClientProperty( "apple.awt.fullscreenable", true );
}
// integrate into macOS screen menu
@@ -322,8 +342,8 @@ class DemoFrame
// add font families
fontMenu.addSeparator();
ArrayList<String> families = new ArrayList<>( Arrays.asList(
"Arial", "Cantarell", "Comic Sans MS", "Courier New", "DejaVu Sans",
"Dialog", "Liberation Sans", "Monospaced", "Noto Sans", "Roboto",
"Arial", "Cantarell", "Comic Sans MS", "DejaVu Sans",
"Dialog", "Liberation Sans", "Noto Sans", "Roboto",
"SansSerif", "Segoe UI", "Serif", "Tahoma", "Ubuntu", "Verdana" ) );
if( !families.contains( currentFamily ) )
families.add( currentFamily );
@@ -433,9 +453,9 @@ class DemoFrame
lafClass == FlatIntelliJLaf.class ||
lafClass == FlatDarculaLaf.class;
accentColorLabel.setEnabled( isAccentColorSupported );
accentColorLabel.setVisible( isAccentColorSupported );
for( int i = 0; i < accentColorButtons.length; i++ )
accentColorButtons[i].setEnabled( isAccentColorSupported );
accentColorButtons[i].setVisible( isAccentColorSupported );
}
private void initComponents() {
@@ -464,6 +484,7 @@ class DemoFrame
JMenuItem projectViewMenuItem = new JMenuItem();
JMenuItem structureViewMenuItem = new JMenuItem();
JMenuItem propertiesViewMenuItem = new JMenuItem();
scrollingPopupMenu = new JMenu();
JMenuItem menuItem2 = new JMenuItem();
htmlMenuItem = new JMenuItem();
JRadioButtonMenuItem radioButtonMenuItem1 = new JRadioButtonMenuItem();
@@ -668,6 +689,12 @@ class DemoFrame
}
viewMenu.add(menu1);
//======== scrollingPopupMenu ========
{
scrollingPopupMenu.setText("Scrolling Popup Menu");
}
viewMenu.add(scrollingPopupMenu);
//---- menuItem2 ----
menuItem2.setText("Disabled Item");
menuItem2.setEnabled(false);
@@ -889,8 +916,17 @@ class DemoFrame
copyMenuItem.addActionListener( new DefaultEditorKit.CopyAction() );
pasteMenuItem.addActionListener( new DefaultEditorKit.PasteAction() );
if( FlatLaf.supportsNativeWindowDecorations() ) {
windowDecorationsCheckBoxMenuItem.setSelected( FlatLaf.isUseNativeWindowDecorations() );
scrollingPopupMenu.add( "Large menus are scrollable" );
scrollingPopupMenu.add( "Use mouse wheel to scroll" );
scrollingPopupMenu.add( "Or use up/down arrows at top/bottom" );
for( int i = 1; i <= 100; i++ )
scrollingPopupMenu.add( "Item " + i );
if( FlatLaf.supportsNativeWindowDecorations() || (SystemInfo.isLinux && JFrame.isDefaultLookAndFeelDecorated()) ) {
if( SystemInfo.isLinux )
unsupported( windowDecorationsCheckBoxMenuItem );
else
windowDecorationsCheckBoxMenuItem.setSelected( FlatLaf.isUseNativeWindowDecorations() );
menuBarEmbeddedCheckBoxMenuItem.setSelected( UIManager.getBoolean( "TitlePane.menuBarEmbedded" ) );
unifiedTitleBarMenuItem.setSelected( UIManager.getBoolean( "TitlePane.unifiedBackground" ) );
showTitleBarIconMenuItem.setSelected( UIManager.getBoolean( "TitlePane.showIcon" ) );
@@ -931,6 +967,7 @@ class DemoFrame
// JFormDesigner - Variables declaration - DO NOT MODIFY //GEN-BEGIN:variables
private JMenuItem exitMenuItem;
private JMenu scrollingPopupMenu;
private JMenuItem htmlMenuItem;
private JMenu fontMenu;
private JMenu optionsMenu;

View File

@@ -282,6 +282,13 @@ new FormModel {
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "menuItemActionPerformed", true ) )
} )
} )
add( new FormContainer( "javax.swing.JMenu", new FormLayoutManager( class javax.swing.JMenu ) ) {
name: "scrollingPopupMenu"
"text": "Scrolling Popup Menu"
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
} )
add( new FormComponent( "javax.swing.JMenuItem" ) {
name: "menuItem2"
"text": "Disabled Item"

View File

@@ -17,6 +17,8 @@
package com.formdev.flatlaf.demo;
import java.awt.Dimension;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
import com.formdev.flatlaf.FlatLaf;
import com.formdev.flatlaf.extras.FlatInspector;
@@ -34,7 +36,7 @@ public class FlatLafDemo
static boolean screenshotsMode = Boolean.parseBoolean( System.getProperty( "flatlaf.demo.screenshotsMode" ) );
public static void main( String[] args ) {
// macOS
// macOS (see https://www.formdev.com/flatlaf/macos/)
if( SystemInfo.isMacOS ) {
// enable screen menu bar
// (moves menu bar from JFrame window to top of screen)
@@ -49,9 +51,17 @@ public class FlatLafDemo
// - "system": use current macOS appearance (light or dark)
// - "NSAppearanceNameAqua": use light appearance
// - "NSAppearanceNameDarkAqua": use dark appearance
// (needs to be set on main thread; setting it on AWT thread does not work)
System.setProperty( "apple.awt.application.appearance", "system" );
}
// Linux
if( SystemInfo.isLinux ) {
// enable custom window decorations
JFrame.setDefaultLookAndFeelDecorated( true );
JDialog.setDefaultLookAndFeelDecorated( true );
}
if( FlatLafDemo.screenshotsMode && !SystemInfo.isJava_9_orLater && System.getProperty( "flatlaf.uiScale" ) == null )
System.setProperty( "flatlaf.uiScale", "2x" );

View File

@@ -43,7 +43,7 @@ public class IJThemesClassGenerator
markdownTable.append( "-----|------\n" );
for( IJThemeInfo ti : themesManager.bundledThemes ) {
if( ti.sourceCodeUrl == null || ti.sourceCodePath == null )
if( ti.sourceCodeUrl == null )
continue;
generateClass( ti, toPath, allInfos, markdownTable );
@@ -77,7 +77,7 @@ public class IJThemesClassGenerator
themeName += " (Material)";
StringBuilder buf = new StringBuilder();
for( String n : name.split( " " ) ) {
for( String n : name.split( "[ \\-]" ) ) {
if( n.length() == 0 || n.equals( "-" ) )
continue;

View File

@@ -178,6 +178,13 @@
"sourceCodeUrl": "https://github.com/bmikaili/intellij-monocai-theme",
"sourceCodePath": "blob/master/resources/Monocai.theme.json"
},
"Monokai_Pro.default.theme.json": {
"name": "Monokai Pro",
"dark": true,
"license": "MIT",
"licenseFile": "Monokai_Pro.LICENSE.txt",
"sourceCodeUrl": "https://github.com/subtheme-dev/monokai-pro"
},
"nord.theme.json": {
"name": "Nord",
"dark": true,
@@ -225,6 +232,14 @@
"sourceCodeUrl": "https://github.com/vuesion/intellij-theme",
"sourceCodePath": "blob/master/resources/META-INF/vuesion_theme.theme.json"
},
"Xcode-Dark.theme.json": {
"name": "Xcode-Dark",
"dark": true,
"license": "MIT",
"licenseFile": "Xcode-Dark.LICENSE.txt",
"sourceCodeUrl": "https://github.com/antelle/intellij-xcode-dark-theme",
"sourceCodePath": "blob/master/resources/Xcode-Dark.theme.json"
},
"material-theme-ui-lite/Arc Dark.theme.json": {
"name": "Material Theme UI Lite / Arc Dark",

View File

@@ -65,6 +65,7 @@ import javax.swing.plaf.UIResource;
import javax.swing.text.JTextComponent;
import com.formdev.flatlaf.FlatClientProperties;
import com.formdev.flatlaf.ui.FlatUIUtils;
import com.formdev.flatlaf.ui.MigLayoutVisualPadding;
import com.formdev.flatlaf.util.SystemInfo;
import com.formdev.flatlaf.util.UIScale;
@@ -113,6 +114,7 @@ public class FlatInspector
private int inspectParentLevel;
private boolean wasModifierKeyPressed;
private boolean showClassHierarchy;
private long lastWhen;
private JComponent highlightFigure;
private Popup popup;
@@ -131,8 +133,22 @@ public class FlatInspector
(((KeyEvent)e).getModifiersEx() & KEY_MODIFIERS_MASK) == (keyStroke.getModifiers() & KEY_MODIFIERS_MASK) )
{
Window activeWindow = KeyboardFocusManager.getCurrentKeyboardFocusManager().getActiveWindow();
if( activeWindow instanceof RootPaneContainer ) {
JRootPane rootPane = ((RootPaneContainer)activeWindow).getRootPane();
RootPaneContainer rootPaneContainer = null;
if( activeWindow instanceof RootPaneContainer )
rootPaneContainer = (RootPaneContainer) activeWindow;
else {
// search for root pain container in children
// (e.g. for Swing embedded into SWT)
for( Component child : activeWindow.getComponents() ) {
if( child instanceof RootPaneContainer ) {
rootPaneContainer = (RootPaneContainer) child;
break;
}
}
}
if( rootPaneContainer != null ) {
JRootPane rootPane = rootPaneContainer.getRootPane();
FlatInspector inspector = (FlatInspector) rootPane.getClientProperty( FlatInspector.class );
if( inspector == null ) {
inspector = new FlatInspector( rootPane );
@@ -172,6 +188,11 @@ public class FlatInspector
if( keyCode == KeyEvent.VK_CONTROL || keyCode == KeyEvent.VK_SHIFT || keyCode == KeyEvent.VK_ALT )
wasModifierKeyPressed = true;
} else if( id == KeyEvent.KEY_RELEASED && wasModifierKeyPressed ) {
// ignore duplicate events (for Swing embedded into SWT)
if( (keyEvent.getWhen() - lastWhen) <= 5 )
return;
lastWhen = keyEvent.getWhen();
if( keyCode == KeyEvent.VK_CONTROL ) {
inspectParentLevel++;
int parentLevel = inspect( lastX, lastY );
@@ -464,6 +485,15 @@ public class FlatInspector
if( margin != null )
appendRow( buf, "Margin", toString( margin ) );
if( c instanceof JComponent ) {
Object value = ((JComponent)c).getClientProperty( MigLayoutVisualPadding.VISUAL_PADDING_PROPERTY );
Insets visualPadding = (value instanceof int[])
? new Insets( ((int[])value)[0], ((int[])value)[1], ((int[])value)[2], ((int[])value)[3] )
: (value instanceof Insets ? (Insets) value : null);
if( visualPadding != null )
appendRow( buf, "Mig visual padding", toString( visualPadding ) );
}
Dimension prefSize = c.getPreferredSize();
Dimension minSize = c.getMinimumSize();
Dimension maxSize = c.getMaximumSize();

View File

@@ -18,7 +18,7 @@ package com.formdev.flatlaf.extras.components;
import static com.formdev.flatlaf.FlatClientProperties.*;
import java.awt.Color;
import javax.swing.JToggleButton;
import javax.swing.*;
import com.formdev.flatlaf.extras.components.FlatButton.ButtonType;
/**
@@ -116,16 +116,37 @@ public class FlatToggleButton
putClientProperty( OUTLINE, outline );
}
/**
* Returns placement of underline if toggle button type is {@link ButtonType#tab}.
* If underline placement is not specified, returns {@link #BOTTOM} as the default
* value.
*
* @since 2.3
*/
public int getTabUnderlinePlacement() {
return getClientPropertyInt( TAB_BUTTON_UNDERLINE_PLACEMENT, BOTTOM );
}
/**
* Returns height of underline if toggle button type is {@link ButtonType#tab}.
* Specifies placement of underline if toggle button type is {@link ButtonType#tab}.
*
* @param placement One of the following constants defined in SwingConstants:
* {@link #TOP}, {@link #LEFT}, {@link #BOTTOM}, or {@link #RIGHT}.
* @since 2.3
*/
public void setTabUnderlinePlacement( int placement ) {
putClientProperty( TAB_BUTTON_UNDERLINE_PLACEMENT, (placement >= 0) ? placement : null );
}
/**
* Returns thickness of underline if toggle button type is {@link ButtonType#tab}.
*/
public int getTabUnderlineHeight() {
return getClientPropertyInt( TAB_BUTTON_UNDERLINE_HEIGHT, "ToggleButton.tab.underlineHeight" );
}
/**
* Specifies height of underline if toggle button type is {@link ButtonType#tab}.
* Specifies thickness of underline if toggle button type is {@link ButtonType#tab}.
*/
public void setTabUnderlineHeight( int tabUnderlineHeight ) {
putClientProperty( TAB_BUTTON_UNDERLINE_HEIGHT, (tabUnderlineHeight >= 0) ? tabUnderlineHeight : null );

View File

@@ -65,12 +65,14 @@ Name | Class
[Light Flat](https://github.com/nerzhulart/LightFlatTheme) | `com.formdev.flatlaf.intellijthemes.FlatLightFlatIJTheme`
[Material Design Dark](https://github.com/xinkunZ/NotReallyMDTheme) | `com.formdev.flatlaf.intellijthemes.FlatMaterialDesignDarkIJTheme`
[Monocai](https://github.com/bmikaili/intellij-monocai-theme) | `com.formdev.flatlaf.intellijthemes.FlatMonocaiIJTheme`
[Monokai Pro](https://github.com/subtheme-dev/monokai-pro) | `com.formdev.flatlaf.intellijthemes.FlatMonokaiProIJTheme`
[Nord](https://github.com/arcticicestudio/nord-jetbrains) | `com.formdev.flatlaf.intellijthemes.FlatNordIJTheme`
[One Dark](https://github.com/one-dark/jetbrains-one-dark-theme) | `com.formdev.flatlaf.intellijthemes.FlatOneDarkIJTheme`
[Solarized Dark](https://github.com/4lex4/intellij-platform-solarized) | `com.formdev.flatlaf.intellijthemes.FlatSolarizedDarkIJTheme`
[Solarized Light](https://github.com/4lex4/intellij-platform-solarized) | `com.formdev.flatlaf.intellijthemes.FlatSolarizedLightIJTheme`
[Spacegray](https://github.com/mturlo/intellij-spacegray) | `com.formdev.flatlaf.intellijthemes.FlatSpacegrayIJTheme`
[Vuesion](https://github.com/vuesion/intellij-theme) | `com.formdev.flatlaf.intellijthemes.FlatVuesionIJTheme`
[Xcode-Dark](https://github.com/antelle/intellij-xcode-dark-theme) | `com.formdev.flatlaf.intellijthemes.FlatXcodeDarkIJTheme`
Material Theme UI Lite:

View File

@@ -52,12 +52,14 @@ public class FlatAllIJThemes
new FlatIJLookAndFeelInfo( "Light Flat", "com.formdev.flatlaf.intellijthemes.FlatLightFlatIJTheme", false ),
new FlatIJLookAndFeelInfo( "Material Design Dark", "com.formdev.flatlaf.intellijthemes.FlatMaterialDesignDarkIJTheme", true ),
new FlatIJLookAndFeelInfo( "Monocai", "com.formdev.flatlaf.intellijthemes.FlatMonocaiIJTheme", true ),
new FlatIJLookAndFeelInfo( "Monokai Pro", "com.formdev.flatlaf.intellijthemes.FlatMonokaiProIJTheme", true ),
new FlatIJLookAndFeelInfo( "Nord", "com.formdev.flatlaf.intellijthemes.FlatNordIJTheme", true ),
new FlatIJLookAndFeelInfo( "One Dark", "com.formdev.flatlaf.intellijthemes.FlatOneDarkIJTheme", true ),
new FlatIJLookAndFeelInfo( "Solarized Dark", "com.formdev.flatlaf.intellijthemes.FlatSolarizedDarkIJTheme", true ),
new FlatIJLookAndFeelInfo( "Solarized Light", "com.formdev.flatlaf.intellijthemes.FlatSolarizedLightIJTheme", false ),
new FlatIJLookAndFeelInfo( "Spacegray", "com.formdev.flatlaf.intellijthemes.FlatSpacegrayIJTheme", true ),
new FlatIJLookAndFeelInfo( "Vuesion", "com.formdev.flatlaf.intellijthemes.FlatVuesionIJTheme", true ),
new FlatIJLookAndFeelInfo( "Xcode-Dark", "com.formdev.flatlaf.intellijthemes.FlatXcodeDarkIJTheme", true ),
new FlatIJLookAndFeelInfo( "Arc Dark (Material)", "com.formdev.flatlaf.intellijthemes.materialthemeuilite.FlatArcDarkIJTheme", true ),
new FlatIJLookAndFeelInfo( "Arc Dark Contrast (Material)", "com.formdev.flatlaf.intellijthemes.materialthemeuilite.FlatArcDarkContrastIJTheme", true ),
new FlatIJLookAndFeelInfo( "Atom One Dark (Material)", "com.formdev.flatlaf.intellijthemes.materialthemeuilite.FlatAtomOneDarkIJTheme", true ),

View File

@@ -0,0 +1,54 @@
/*
* Copyright 2020 FormDev Software GmbH
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.formdev.flatlaf.intellijthemes;
//
// DO NOT MODIFY
// Generated with com.formdev.flatlaf.demo.intellijthemes.IJThemesClassGenerator
//
import com.formdev.flatlaf.IntelliJTheme;
/**
* @author Karl Tauber
*/
public class FlatMonokaiProIJTheme
extends IntelliJTheme.ThemeLaf
{
public static final String NAME = "Monokai Pro";
public static boolean setup() {
try {
return setup( new FlatMonokaiProIJTheme() );
} catch( RuntimeException ex ) {
return false;
}
}
public static void installLafInfo() {
installLafInfo( NAME, FlatMonokaiProIJTheme.class );
}
public FlatMonokaiProIJTheme() {
super( Utils.loadTheme( "Monokai_Pro.default.theme.json" ) );
}
@Override
public String getName() {
return NAME;
}
}

View File

@@ -0,0 +1,54 @@
/*
* Copyright 2020 FormDev Software GmbH
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.formdev.flatlaf.intellijthemes;
//
// DO NOT MODIFY
// Generated with com.formdev.flatlaf.demo.intellijthemes.IJThemesClassGenerator
//
import com.formdev.flatlaf.IntelliJTheme;
/**
* @author Karl Tauber
*/
public class FlatXcodeDarkIJTheme
extends IntelliJTheme.ThemeLaf
{
public static final String NAME = "Xcode-Dark";
public static boolean setup() {
try {
return setup( new FlatXcodeDarkIJTheme() );
} catch( RuntimeException ex ) {
return false;
}
}
public static void installLafInfo() {
installLafInfo( NAME, FlatXcodeDarkIJTheme.class );
}
public FlatXcodeDarkIJTheme() {
super( Utils.loadTheme( "Xcode-Dark.theme.json" ) );
}
@Override
public String getName() {
return NAME;
}
}

View File

@@ -127,7 +127,11 @@
"secondaryForeground": "#7ac2cc"
},
"List.background": "#eef0f4",
"List": {
"background": "#eef0f4",
"hoverBackground": "#dae6eb",
"hoverInactiveBackground": "#dae6eb"
},
"Notification": {
"MoreButton.innerBorderColor": "#bec5cd",
@@ -143,6 +147,12 @@
}
},
"NotificationsToolwindow": {
"newNotification.background": "#dae6ebB3",
"newNotification.hoverBackground": "#dae6eb",
"Notification.hoverBackground": "#dae6eb"
},
"PasswordField.background": "#FFFFFF",
"Plugins": {
@@ -151,7 +161,8 @@
"SectionHeader.foreground": "#808080",
"SectionHeader.background": "#edeef2",
"Tab.selectedBackground": "#cacccf",
"Tab.hoverBackground": "#cacccf"
"Tab.hoverBackground": "#cacccf",
"hoverBackground": "#e6eef2"
},
"Popup": {
@@ -249,7 +260,12 @@
}
},
"Tree.background": "#eef0f4",
"Tree": {
"background": "#eef0f4",
"hoverBackground": "#dae6eb",
"hoverInactiveBackground": "#dae6eb",
"hash": "#d0d5db"
},
"WelcomeScreen": {
"Details.background": "#eef0f4",

View File

@@ -120,7 +120,8 @@
"DefaultTabs": {
"underlineColor": "#9649cc",
"inactiveUnderlineColor": "#877399",
"hoverBackground": "#dfb3ff1a"
"hoverBackground": "#dfb3ff1a",
"borderColor": "#1a1721"
},
"DragAndDrop": {
@@ -168,6 +169,11 @@
"visitedForeground": "#7094FF"
},
"List": {
"hoverBackground": "#00000033",
"hoverInactiveBackground": "#00000033"
},
"MenuBar.borderColor": "#1a1721",
"NavBar.borderColor": "#1a1721",
@@ -197,6 +203,12 @@
}
},
"NotificationsToolwindow": {
"newNotification.background": "#3D3952B3",
"newNotification.hoverBackground": "#3D3952",
"Notification.hoverBackground": "#3D3952"
},
"MemoryIndicator": {
"allocatedBackground": "#352140",
"usedBackground": "#533473"
@@ -211,6 +223,8 @@
},
"Plugins": {
"hoverBackground": "#00000022",
"Tab": {
"selectedForeground": "#D0D0D9",
"selectedBackground": "#593f73",
@@ -284,6 +298,13 @@
"SearchOption.selectedBackground": "#424885",
"SegmentedButton": {
"selectedButtonColor": "#45405C",
"focusedSelectedButtonColor": "#693687",
"selectedStartBorderColor": "#4E4C63",
"selectedEndBorderColor": "#4E4C63"
},
"SpeedSearch": {
"foreground": "#D0D0D9",
"borderColor": "#69418c",
@@ -310,7 +331,8 @@
"Table": {
"stripeColor": "#323242",
"hoverBackground": "#00000028"
"hoverBackground": "#00000028",
"gridColor": "#1a1721"
},
"TextArea": {
@@ -360,7 +382,12 @@
}
},
"Tree.rowHeight": 20,
"Tree": {
"rowHeight": 20,
"hash": "#4E4C63",
"hoverBackground": "#00000033",
"hoverInactiveBackground": "#00000033"
},
"ValidationTooltip": {
"errorBackground": "#802d43",

View File

@@ -38,6 +38,26 @@
"pressedBackground": "hoverBackground",
"pressedBorderColor": "hoverBackground"
},
"Bookmark": {
"iconBackground": "accentColor",
"Mnemonic": {
"iconForeground": "primaryForeground",
"iconBackground": "#8d6b81",
"iconBorderColor": "accentColor"
}
},
"BookmarkMnemonicAssigned": {
"foreground": "primaryForeground",
"background": "#786299",
"borderColor": "secondaryAccentColor"
},
"BookmarkMnemonicAvailable": {
},
"BookmarkMnemonicCurrent": {
"foreground": "primaryForeground",
"background": "#8d6b81",
"borderColor": "accentColor"
},
"Button": {
"foreground": "primaryForeground",
"startBorderColor": "selectionBackground",
@@ -90,7 +110,7 @@
"inactiveWarningFocusColor": "#f1fa8c"
},
"DragAndDrop": {
"areaBorderColor": "selectionBackground"
"borderColor": "selectionBackground"
},
"Editor": {
"background": "secondaryBackground",

View File

@@ -111,6 +111,11 @@
"secondaryForeground": "#77a8d9"
},
"List": {
"hoverBackground": "#00000010",
"hoverInactiveBackground": "#00000010"
},
"Notification": {
"background": "#f7f8fa",
"borderColor": "#D4D6D9",
@@ -131,6 +136,12 @@
}
},
"NotificationsToolwindow": {
"newNotification.background": "#dae9f7B3",
"newNotification.hoverBackground": "#00000009",
"Notification.hoverBackground": "#00000009"
},
"PasswordField.background": "#FFFFFF",
"Plugins": {
@@ -140,7 +151,7 @@
"Tab.selectedBackground": "#D5D6D7",
"Tab.hoverBackground": "#D5D6D7",
"lightSelectionBackground": "#E9EFF7",
"hoverBackground": "#f7faff"
"hoverBackground": "#F8F8F8"
},
"Popup": {
@@ -225,7 +236,11 @@
}
},
"Tree.rowHeight": 22,
"Tree": {
"rowHeight": 22,
"hoverBackground": "#00000010",
"hoverInactiveBackground": "#00000010"
},
"VersionControl": {
"Log.Commit.currentBranchBackground": "#e6f0f2",

View File

@@ -0,0 +1,19 @@
The MIT License (MIT)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -0,0 +1,684 @@
{
"name": "Monokai Pro",
"dark": true,
"author": "Subtheme",
"editorScheme": "/schemes/default.xml",
"ui": {
"*": {
"acceleratorSelectionForeground": "#ffd866",
"background": "#2d2a2e",
"borderColor": "#363337",
"disabledBackground": "#221f22",
"disabledForeground": "#5b595c",
"disabledText": "#5b595c",
"focusColor": "#5b595c",
"focusedBorderColor": "#ffd866",
"foreground": "#fcfcfa",
"inactiveBackground": "#221f22",
"inactiveForeground": "#939293",
"infoForeground": "#939293",
"selectionBackground": "#403e41",
"selectionBackgroundInactive": "#403e41",
"selectionForeground": "#ffd866",
"selectionInactiveBackground": "#403e41",
"selectionInactiveForeground": "#ffd866",
"separatorColor": "#2d2a2e"
},
"ActionButton": {
"hoverBackground": "#363337",
"hoverBorderColor": "#363337",
"pressedBackground": "#363337",
"pressedBorderColor": "#363337"
},
"Borders": {
"ContrastBorderColor": "#363337",
"color": "#363337"
},
"Button": {
"arc": 0,
"background": "#403e41",
"default": {
"endBackground": "#403e41",
"endBorderColor": "#403e41",
"foreground": "#ffd866",
"focusColor": "#ffd866",
"focusedBorderColor": "#403e41",
"shadowColor": "#403e41",
"startBackground": "#403e41",
"startBorderColor": "#403e41"
},
"disabledBorderColor": "#403e41",
"disabledText": "#5b595c",
"endBackground": "#403e41",
"endBorderColor": "#403e41",
"focusedBorderColor": "#403e41",
"foreground": "#fcfcfa",
"shadowColor": "#403e41",
"shadowWidth": 0,
"startBackground": "#403e41",
"startBorderColor": "#403e41"
},
"CheckBox": {
"background": "#2d2a2e",
"disabledText": "#5b595c",
"foreground": "#fcfcfa",
"select": "#ffd866"
},
"CheckBoxMenuItem": {
"acceleratorForeground": "#939293",
"acceleratorSelectionForeground": "#939293",
"background": "#2d2a2e",
"disabledBackground": "#2d2a2e",
"disabledForeground": "#5b595c",
"foreground": "#fcfcfa",
"selectionBackground": "#403e41",
"selectionForeground": "#ffd866"
},
"ColorChooser": {
"background": "#2d2a2e",
"foreground": "#fcfcfa",
"swatchesDefaultRecentColor": "#fcfcfa"
},
"ComboBoxButton": {
"background": "#363337"
},
"ComboBox": {
"ArrowButton": {
"background": "#403e41",
"disabledIconColor": "#5b595c",
"iconColor": "#fcfcfa",
"nonEditableBackground": "#2d2a2e"
},
"background": "#363337",
"disabledBackground": "#221f22",
"disabledForeground": "#5b595c",
"foreground": "#fcfcfa",
"modifiedItemForeground": "#ffd866",
"nonEditableBackground": "#363337",
"selectionBackground": "#2d2a2e",
"selectionForeground": "#ffd866"
},
"ComboPopup": {
"border": "#2d2a2e"
},
"CompletionPopup": {
"foreground": "#fcfcfa",
"matchForeground": "#ffd866",
"selectionInactiveBackground": "#2d2a2e",
"selectionBackground": "#2d2a2e"
},
"Component": {
"arc": 4,
"borderColor": "#363337",
"disabledBorderColor": "#363337",
"focusColor": "#ffd866",
"focusedBorderColor": "#ffd866",
"hoverIconColor": "#ffd866",
"infoForeground": "#939293",
"iconColor": "#fcfcfa"
},
"Counter": {
"background": "#ffd866",
"foreground": "#19181a"
},
"Debugger": {
"Variables": {
"collectingDataForeground": "#939293",
"changedValueForeground": "#ffd866",
"errorMessageForeground": "#ff6188",
"evaluatingExpressionForeground": "#939293",
"exceptionForeground": "#78dce8",
"modifyingValueForeground": "#ffd866",
"valueForeground": "#ffd866"
}
},
"DebuggerTabs": {
"underlinedTabBackground": "#5b595c"
},
"DebuggerPopup": {
"borderColor": "#5b595c"
},
"DefaultTabs": {
"background": "#2d2a2e",
"borderColor": "#2d2a2e",
"hoverBackground": "#363337",
"inactiveUnderlineColor": "#ffd866",
"underlineColor": "#ffd866",
"underlinedTabBackground": "#2d2a2e",
"underlineHeight": 2
},
"DragAndDrop": {
"areaBackground": "#2d2a2e",
"areaBorderColor": "#2d2a2e",
"areaForeground": "#fcfcfa"
},
"Editor": {
"background": "#221f22",
"foreground": "#fcfcfa",
"shortcutForeground": "#939293"
},
"EditorPane": {
"background": "#221f22",
"caretForeground": "#ffd866",
"foreground": "#fcfcfa",
"inactiveBackground": "#2d2a2e",
"inactiveForeground": "#5b595c",
"selectionBackground": "#403e41",
"selectionForeground": "#ffd866"
},
"EditorTabs": {
"borderColor": "#363337",
"inactiveUnderlineColor": "#ffd866",
"underlineColor": "#ffd866",
"underlinedTabBackground": "#363337",
"underlinedTabForeground": "#c1c0c0"
},
"FileColor": {
"Yellow": "00000000"
},
"FormattedTextField": {
"background": "#363337",
"caretForeground": "#ffd866",
"foreground": "#fcfcfa",
"inactiveBackground": "#403e41",
"inactiveForeground": "#5b595c",
"selectionForeground": "#ffd866",
"selectionBackground": "#403e41"
},
"Group": {
"disabledSeparatorColor": "#2d2a2e",
"separatorColor": "#2d2a2e"
},
"GutterTooltip": {
"infoForeground": "#fcfcfa",
"lineSeparatorColor": "#2d2a2e"
},
"HelpTooltip": {
"borderColor": "#2d2a2e",
"infoForeground": "#939293"
},
"InformationHint": {
"borderColor": "#2d2a2e"
},
"InplaceRefactoringPopup": {
"borderColor": "#2d2a2e"
},
"Label": {
"background": "#2d2a2e",
"disabledForeground": "#5b595c",
"disabledText": "#5b595c",
"foreground": "#939293",
"infoForeground": "#939293",
"selectedForeground": "#ffd866"
},
"Link": {
"activeForeground": "#ffd866",
"hoverForeground": "#ffd866",
"pressedForeground": "#ffd866",
"secondaryForeground": "#939293",
"visitedForeground": "#ffd866"
},
"List": {
"background": "#221f22",
"foreground": "#fcfcfa",
"selectionBackground": "#363337",
"selectionForeground": "#ffd866",
"selectionInactiveBackground": "#363337",
"selectionInactiveForeground": "#ffd866"
},
"MemoryIndicator": {
"allocatedBackground": "#403e41",
"usedBackground": "#5b595c"
},
"Menu": {
"acceleratorForeground": "#939293",
"acceleratorSelectionForeground": "#fcfcfa",
"background": "#2d2a2e",
"borderColor": "#363337",
"disabledBackground": "#403e41",
"disabledForeground": "#5b595c",
"foreground": "#fcfcfa",
"separatorColor": "#2d2a2e",
"selectionForeground": "#ffd866"
},
"MenuBar": {
"borderColor": "#363337",
"disabledBackground": "#2d2a2e",
"disabledForeground": "#5b595c",
"foreground": "#fcfcfa",
"highlight": "#2d2a2e",
"selectionBackground": "#403e41",
"selectionForeground": "#ffd866",
"shadow": "#221f22"
},
"MenuItem": {
"acceleratorForeground": "#939293",
"background": "#2d2a2e",
"disabledBackground": "#2d2a2e",
"disabledForeground": "#5b595c",
"foreground": "#fcfcfa",
"selectionBackground": "#403e41",
"selectionForeground": "#ffd866"
},
"NavBar": {
"borderColor": "#363337"
},
"Notification": {
"background": "#363337",
"borderColor": "#221f22",
"errorBackground": "#221f22",
"errorBorderColor": "#221f22",
"foreground": "#fcfcfa",
"MoreButton": {
"background": "#403e41",
"foreground": "#fcfcfa",
"innerBorderColor": "#363337"
},
"ToolWindow": {
"errorBackground": "#363337",
"errorBorderColor": "#363337",
"informativeBackground": "#363337",
"informativeBorderColor": "#363337",
"warningBackground": "#363337",
"warningBorderColor": "#363337"
}
},
"OptionPane": {
"background": "#2d2a2e",
"foreground": "#fcfcfa",
"messageForeground": "#fcfcfa"
},
"Panel": {
"background": "#2d2a2e",
"foreground": "#939293"
},
"ParameterInfo": {
"background": "#403e41",
"borderColor": "#363337",
"currentOverloadBackground": "#403e41",
"currentParameterForeground": "#ffd866",
"disabledForeground": "#5b595c",
"foreground": "#fcfcfa",
"infoForeground": "#939293",
"lineSeparatorColor": "#363337"
},
"PasswordField": {
"background": "#403e41",
"capsLockIconColor": "#ffd866",
"caretForeground": "#ffd866",
"foreground": "#fcfcfa",
"inactiveForeground": "#5b595c",
"selectionBackground": "#5b595c",
"selectionForeground": "#ffd866"
},
"Plugins": {
"background": "#2d2a2e",
"disabledForeground": "#939293",
"eapTagBackground": "#5b595c",
"lightSelectionBackground": "#363337",
"hoverBackground": "#363337",
"tagForeground": "#ffd866",
"tagBackground": "#5b595c",
"Button": {
"installBackground": "#221f22",
"installBorderColor": "#221f22",
"installForeground": "#fcfcfa",
"installFocusedBackground": "#221f22",
"installFillForeground": "#939293",
"installFillBackground": "#221f22",
"updateBackground": "#221f22",
"updateBorderColor": "#221f22",
"updateForeground": "#fcfcfa"
},
"SearchField": {
"background": "#363337",
"borderColor": "#363337"
},
"SectionHeader": {
"background": "#2d2a2e",
"foreground": "#939293"
},
"Tab": {
"hoverBackground": "#363337",
"selectedForeground": "#fcfcfa",
"selectedBackground": "#363337"
}
},
"Popup": {
"Advertiser": {
"background": "#2d2a2e",
"borderColor": "#2d2a2e",
"foreground": "#ffd866"
},
"borderColor": "#221f22",
"inactiveBorderColor": "#2d2a2e",
"innerBorderColor": "#2d2a2e",
"Header": {
"activeBackground": "#2d2a2e",
"inactiveBackground": "#221f22"
},
"paintBorder": true,
"separatorForeground": "#fcfcfa",
"separatorColor": "#2d2a2e",
"Toolbar": {
"background": "#221f22",
"borderColor": "#363337"
}
},
"PopupMenu": {
"background": "#2d2a2e",
"foreground": "#fcfcfa",
"translucentBackground": "#2d2a2e",
"selectionBackground": "#403e41"
},
"PopupMenuSeparator": {
"height": 10,
"stripeIndent": 5
},
"ProgressBar": {
"background": "#2d2a2e",
"foreground": "#ffd866",
"indeterminateEndColor": "#ffd866",
"indeterminateStartColor": "#ffd866",
"progressColor": "#ffd866",
"selectionBackground": "#5b595c",
"trackColor": "#5b595c"
},
"RadioButton": {
"background": "#2d2a2e",
"disabledText": "#5b595c",
"foreground": "#fcfcfa"
},
"RadioButtonMenuItem": {
"acceleratorForeground": "#939293",
"acceleratorSelectionForeground": "#939293",
"background": "#2d2a2e",
"disabledBackground": "#2d2a2e",
"disabledForeground": "#5b595c",
"foreground": "#fcfcfa",
"selectionBackground": "#403e41",
"selectionForeground": "#ffd866"
},
"SearchEverywhere": {
"Advertiser": {
"background": "#221f22",
"foreground": "#939293"
},
"Header": {
"background": "#2d2a2e"
},
"List": {
"separatorColor": "#2d2a2e",
"separatorForeground": "#939293"
},
"SearchField": {
"background": "#2d2a2e",
"borderColor": "#221f22",
"infoForeground": "#939293"
},
"Tab": {
"selectedForeground": "#ffd866",
"selectedBackground": "#5b595c"
}
},
"SearchMatch": {
"endBackground": "#ffd866",
"startBackground": "#ffd866"
},
"Separator": {
"separatorColor": "#363337"
},
"SidePanel": {
"background": "#221f22"
},
"Slider": {
"background": "#2d2a2e",
"buttonBorderColor": "#ffd866",
"buttonColor": "#ffd866",
"foreground": "#fcfcfa",
"tickColor": "#403e41",
"trackColor": "#403e41"
},
"SpeedSearch": {
"background": "#5b595c",
"borderColor": "#2d2a2e",
"errorForeground": "#fcfcfa",
"foreground": "#fcfcfa"
},
"Spinner": {
"background": "#2d2a2e"
},
"SplitPane": {
"background": "#2d2a2e",
"highlight": "#221f22"
},
"SplitPaneDivider": {
"draggingColor": "#403e41"
},
"StatusBar": {
"borderColor": "#363337",
"hoverBackground": "#363337"
},
"TabbedPane": {
"background": "#2d2a2e",
"contentAreaColor": "#5b595c",
"disabledForeground": "#5b595c",
"disabledUnderlineColor": "#5b595c",
"focus": "#403e41",
"focusColor": "#403e41",
"foreground": "#fcfcfa",
"hoverColor": "#363337",
"tabSelectionHeight": 2,
"underlineColor": "#ffd866"
},
"Table": {
"background": "#221f22",
"dropLineColor": "#ffd866",
"dropLineShortColor": "#ffd866",
"focusCellBackground": "#221f22",
"focusCellForeground": "#ffd866",
"foreground": "#fcfcfa",
"gridColor": "#221f22",
"lightSelectionForeground": "#ffd866",
"lightSelectionInactiveForeground": "#fcfcfa",
"lightSelectionInactiveBackground": "#363337",
"selectionBackground": "#363337",
"selectionForeground": "#ffd866",
"selectionInactiveBackground": "#363337",
"selectionInactiveForeground": "#fcfcfa",
"sortIconColor": "#fcfcfa",
"stripeColor": "#221f22"
},
"TableHeader": {
"background": "#2d2a2e",
"bottomSeparatorColor": "#363337",
"cellBorder": "4,0,4,0",
"foreground": "#fcfcfa",
"focusCellBackground": "#403e41",
"separatorColor": "#363337"
},
"textText": "#939293",
"TextArea": {
"background": "#363337",
"caretForeground": "#ffd866",
"foreground": "#fcfcfa",
"inactiveForeground": "#5b595c",
"selectionBackground": "#5b595c",
"selectionForeground": "#ffd866"
},
"TextField": {
"background": "#363337",
"caretForeground": "#ffd866",
"foreground": "#fcfcfa",
"inactiveForeground": "#5b595c",
"selectionBackground": "#5b595c",
"selectionForeground": "#ffd866"
},
"TextPane": {
"background": "#363337",
"caretForeground": "#ffd866",
"foreground": "#fcfcfa",
"inactiveForeground": "#5b595c",
"selectionBackground": "#5b595c",
"selectionForeground": "#ffd866"
},
"TitlePane": {
"background": "#221f22",
"Button.hoverBackground": "#5b595c",
"inactiveBackground": "#2d2a2e",
"infoForeground": "#939293",
"inactiveInfoForeground": "#939293"
},
"TitledBorder": {
"titleColor": "#2d2a2e"
},
"ToggleButton": {
"borderColor": "#403e41",
"buttonColor": "#fcfcfa",
"disabledText": "#5b595c",
"foreground": "#939293",
"offForeground": "#2d2a2e",
"offBackground": "#2d2a2e",
"onBackground": "#ffd866",
"onForeground": "#ffd866"
},
"ToolBar": {
"background": "#221f22",
"borderHandleColor": "#939293",
"floatingForeground": "#939293",
"foreground": "#fcfcfa"
},
"ToolTip": {
"Actions": {
"background": "#2d2a2e",
"infoForeground": "#939293"
},
"background": "#2d2a2e",
"borderColor": "#5b595c",
"foreground": "#fcfcfa",
"infoForeground": "#939293",
"shortcutForeground": "#939293"
},
"ToolWindow": {
"Button": {
"hoverBackground": "#363337",
"selectedForeground": "#ffd866",
"selectedBackground": "#363337"
},
"Header": {
"background": "#2d2a2e",
"borderColor": "#363337",
"inactiveBackground": "#2d2a2e"
},
"HeaderCloseButton": {
"background": "#2d2a2e"
},
"HeaderTab": {
"hoverBackground": "#363337",
"hoverInactiveBackground": "#363337",
"inactiveUnderlineColor": "#ffd866",
"selectedInactiveBackground": "#2d2a2e",
"underlineColor": "#ffd866",
"underlinedTabBackground": "#363337",
"underlinedTabInactiveBackground": "#363337"
}
},
"Tree": {
"background": "#221f22",
"foreground": "#939293",
"hash": "#2d2a2e",
"modifiedItemForeground": "#ffd866",
"rowHeight": 22,
"selectionBackground": "#2d2a2e",
"selectionForeground": "#ffd866",
"selectionInactiveBackground": "#2d2a2e"
},
"UiDesigner": {
"Panel": {
"background": "#2d2a2e"
},
"Preview": {
"background": "#2d2a2e"
}
},
"ValidationTooltip": {
"errorBackground": "#363337",
"errorBorderColor": "#363337",
"warningBackground": "#363337",
"warningBorderColor": "#363337"
},
"VersionControl": {
"FileHistory": {
"Commit": {
"selectedBranchBackground": "#2d2a2e"
}
},
"GitLog": {
"localBranchIconColor": "#ffd866",
"otherIconColor": "#939293",
"remoteBranchIconColor": "#fcfcfa",
"tagIconColor": "#939293"
},
"HgLog": {
"branchIconColor": "#ffd866",
"bookmarkIconColor": "#ffd866",
"closedBranchIconColor": "#5b595c",
"localTagIconColor": "#939293",
"mqTagIconColor": "#939293",
"tagIconColor": "#939293",
"tipIconColor": "#939293"
},
"Log": {
"Commit": {
"unmatchedForeground": "#939293",
"currentBranchBackground": "#363337"
}
},
"RefLabel": {
"foreground": "#ffd866",
"backgroundBase": "#5b595c"
}
},
"Viewport": {
"background": "#221f22",
"foreground": "#fcfcfa"
},
"WelcomeScreen": {
"background": "#2d2a2e",
"borderColor": "#363337",
"captionBackground": "#221f22",
"captionForeground": "#fcfcfa",
"footerBackground": "#221f22",
"footerForeground": "#fcfcfa",
"headerBackground": "#2d2a2e",
"headerForeground": "#fcfcfa",
"Projects": {
"background": "#221f22",
"selectionBackground": "#2d2a2e",
"selectionInactiveBackground": "#2d2a2e"
},
"separatorColor": "#363337"
},
"window": "#2d2a2e",
"Window": {
"border": "0,0,0,0,#363337"
},
"windowBorder": "#363337",
"windowText": "#939293"
},
"icons": {
"ColorPalette": {
"Checkbox.Background.Default.Dark": "##403e41",
"Checkbox.Background.Disabled.Dark": "##221f22",
"Checkbox.Background.Selected.Dark": "##403e41",
"Checkbox.Border.Default.Dark": "##403e41",
"Checkbox.Border.Disabled.Dark": "##221f22",
"Checkbox.Border.Selected.Dark": "##403e41",
"Checkbox.Focus.Thin.Default.Dark": "##403e41",
"Checkbox.Focus.Thin.Selected.Dark": "##221f22",
"Checkbox.Focus.Wide.Dark": "##403e41",
"Checkbox.Foreground.Disabled.Dark": "##939293",
"Checkbox.Foreground.Selected.Dark": "##fcfcfa"
}
}
}

View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2019 Antelle
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -0,0 +1,232 @@
{
"name": "Xcode-Dark",
"dark": true,
"author": "Antelle",
"editorScheme": "/Xcode-Dark.xml",
"ui": {
"*": {
"background": "#323333",
"foreground": "#DFDFE0",
"infoForeground": "#A9A9AA",
"selectionBackground": "#1D7BED",
"selectionForeground": "#F9FCFF",
"selectionInactiveBackground": "#555657",
"selectionBackgroundInactive": "#555657",
"lightSelectionBackground": "#2B4059",
"lightSelectionForeground": "#FFFFFF",
"lightSelectionInactiveBackground": "#555657",
"lightSelectionInactiveForeground": "#DFDFE0",
"disabledBackground": "#323333",
"inactiveBackground": "#313233",
"disabledForeground": "#737374",
"disabledText": "#737374",
"inactiveForeground": "#737374",
"acceleratorForeground": "#D0D0D9",
"acceleratorSelectionForeground": "#D0D0D9",
"errorForeground": "#843937",
"borderColor": "#414141",
"disabledBorderColor": "#414141",
"focusColor": "#24648D",
"focusedBorderColor": "#4C8CB5",
"separatorForeground": "#5A5B5C",
"separatorColor": "#5A5B5C",
"lineSeparatorColor": "#5A5B5C",
"modifiedItemForeground": "#1E4D86"
},
"ActionButton": {
"hoverBackground": "#5A5B5C",
"hoverBorderColor": "#5A5B5C",
"pressedBackground": "#414141",
"pressedBorderColor": "#414141"
},
"Button": {
"startBackground": "#424242",
"endBackground": "#424242",
"startBorderColor": "#525252",
"endBorderColor": "#525252",
"shadowColor": "#363636",
"default": {
"foreground": "#DDE9FA",
"startBackground": "#206DE3",
"endBackground": "#1C61CA",
"startBorderColor": "#6099EC",
"endBorderColor": "#1C61CA",
"focusedBorderColor": "#45A1F4",
"focusColor": "#245F84",
"shadowColor": "#363636"
}
},
"Borders": {
"color": "#393A3B",
"ContrastBorderColor": "#4b4c4d"
},
"ComboBox": {
"nonEditableBackground": "#4f5051",
"background": "#414141",
"ArrowButton": {
"iconColor": "#DBDBDB",
"background": "#1E67D7",
"disabledIconColor": "#6B6B6C",
"nonEditableBackground": "#4f5051"
}
},
"ComboPopup.border": "1,1,1,1,3F3F3F",
"Component": {
"errorFocusColor": "#843937",
"inactiveErrorFocusColor": "#433239",
"warningFocusColor": "#8F7B2E",
"inactiveWarningFocusColor": "#434136",
"iconColor": "#DFDFE0",
"hoverIconColor": "#FFFFFF"
},
"Counter": {
"background": "#909090",
"foreground": "#000000"
},
"DebuggerPopup.borderColor": "#414141",
"DefaultTabs": {
"borderColor": "#484949",
"background": "#2A2B2B",
"inactiveUnderlineColor": "#24648D",
"hoverBackground": "#262727",
"underlineColor": "#1D7BED",
"underlineHeight": 2,
"underlinedTabBackground": "#3D3E3F"
},
"DragAndDrop": {
"areaForeground": "#DFDFE0",
"areaBackground": "#363737",
"areaBorderColor": "#414141"
},
"EditorTabs": {
"inactiveColoredFileBackground": "#2d281d"
},
"FileColor": {
"Yellow": "#3D3B37",
"Green": "#383C38",
"Blue": "#343A3E",
"Violet": "#3C3A3D",
"Orange": "#3D3A36",
"Rose": "#3D383B"
},
"Link": {
"activeForeground": "#428EE1",
"hoverForeground": "#7094FF",
"pressedForeground": "#FC4741",
"visitedForeground": "#7094FF"
},
"Notification": {
"errorForeground": "#DFDFE0",
"errorBackground": "#843937",
"errorBorderColor": "#982B31",
"MoreButton.innerBorderColor": "#4b4c4d",
"ToolWindow": {
"informativeForeground": "#DFDFE0",
"informativeBackground": "#225DA1",
"informativeBorderColor": "#1C61CA",
"warningForeground": "#DFDFE0",
"warningBackground": "#A98938",
"warningBorderColor": "#FEC42E",
"errorForeground": "#DFDFE0",
"errorBackground": "#843937",
"errorBorderColor": "#982B31"
}
},
"ProgressBar": {
"trackColor": "#252525",
"progressColor": "#CDCCCC",
"indeterminateStartColor": "#CDCCCC",
"indeterminateEndColor": "#B4B3B3",
"failedColor": "#DF1A21",
"failedEndColor": "#982B31",
"passedColor": "#3AA87B",
"passedEndColor": "#368E68"
},
"SearchMatch": {
"startBackground": "#f5bc2d",
"endBackground": "#e9b32b"
},
"SpeedSearch": {
"errorForeground": "#DF1A21"
},
"ToggleButton": {
"onBackground": "#1D7BED"
},
"CompletionPopup": {
"matchForeground": "#FDA14F",
"selectionBackground": "#1D7BED"
},
"Popup": {
"Header": {
"activeBackground": "#4A4E52",
"inactiveBackground": "#4A4E52"
}
},
"SearchEverywhere": {
"Tab": {
"selectedForeground": "#F9FCFF",
"selectedBackground": "#555A5E"
}
},
"icons": {
"ColorPalette": {
"Actions.Grey": "#969798",
"Actions.Red": "#FC545B",
"Actions.Yellow": "#e0c24c",
"Actions.Green": "#71bf47",
"Actions.Blue": "#3062d1",
"Actions.GreyInline.Dark": "#6F6F70",
"Objects.Grey": "#6F6F70",
"Objects.RedStatus": "#FC4741",
"Objects.Red": "#FC545B",
"Objects.Pink": "#F5539E",
"Objects.Yellow": "#e0c24c",
"Objects.Green": "#71bf47",
"Objects.Blue": "#3062d1",
"Objects.Purple": "#A453A5",
"Objects.BlackText": "#000000",
"Objects.YellowDark": "#89744D",
"Objects.GreenAndroid": "#5D7554"
}
}
}
}

Some files were not shown because too many files have changed in this diff Show More