Compare commits

..

30 Commits

Author SHA1 Message Date
Karl Tauber
44a04cca2c FlatSmoothScrollingTest:
- better list/tree/etc items for easier recognizing jittery scrolling
- sliders to modify animation duration and resolution
- slider to invoke `scrollRectToVisible()`
- option to show row header for table
- use viewport.viewPosition for chart (instead of scrollbar.value)
- highlight methods in stack of tooltip (e.g. JViewport.setViewPosition())
2023-09-02 17:56:11 +02:00
Karl Tauber
b32b8db97a FlatSmoothScrollingTest: refactored line chart panel into own class for easier use in other test apps 2023-08-30 00:20:45 +02:00
Karl Tauber
c529dcb747 Smooth Scrolling:
- fixed jittery repeating-scrolling with PageUp/Down keys when reaching the top/bottom/left/right of the viewport (see FlatScrollBarUI.setValueAnimated())
- temporary change viewport scroll mode only if it is JViewport.BLIT_SCROLL_MODE
- use JViewport.SIMPLE_SCROLL_MODE when temporary disabling blitting
2023-08-27 14:31:30 +02:00
Karl Tauber
04658c2ef0 SmoothScrollingTest: fixed error reported by Error Prone 2023-08-25 17:43:58 +02:00
Karl Tauber
5cdef5409b Smooth Scrolling: fixed jittery scrolling with trackpad or Magic Mouse (if smooth scrolling is enabled) 2023-08-25 15:24:28 +02:00
Karl Tauber
6dfc204e40 SmoothScrollingTest added (from https://github.com/JFormDesigner/FlatLaf/pull/683#issuecomment-1585667066) 2023-08-25 15:23:59 +02:00
Karl Tauber
542e7d5f60 Smooth Scrolling: fixes too slow repeating block (page) scrolling (e.g. hold down PageUp key) for Tree, TextArea, TextPane and EditorPane 2023-08-24 22:38:52 +02:00
Karl Tauber
3628a03c9d introduced FlatUIAction 2023-08-24 11:54:32 +02:00
Karl Tauber
6ce2198cd6 FlatSmoothScrollingTest:
- added slider to horizontally scale chart
- improved chart legend
- record stack for points in chart and show in tooltip on hover
2023-08-23 15:53:55 +02:00
Karl Tauber
e2e3fd31e9 FlatSmoothScrollingTest:
- added small vertical line to indicate data points in chart
- added split pane to allow changing height of components
- Alt+C clears chart without moving focus to "Clear" button
- separate chart lines for smooth and non-smooth scrolling
2023-08-23 09:51:57 +02:00
Karl Tauber
cf70cfb50c ScrollBar: fixed temporary painting at wrong location during smooth scrolling when using mouse-wheel or scroll bar
(still occurs when scrolling by moving selection via keyboard)

many thanks to @Chrriis for the idea to temporary disable blitting mode on viewport
2023-08-23 09:51:57 +02:00
Karl Tauber
29f6c5fae9 FlatAnimatorTest: added test for precise scrolling with trackpad 2023-08-23 09:51:57 +02:00
Karl Tauber
419a689ca4 FlatAnimatorTest: added test for wheel scrolling (including chart) 2023-08-23 09:51:57 +02:00
Karl Tauber
865a56875f FlatSmoothScrollingTest: added "custom" scroll pane for testing smooth scrolling in case that scroll view does not implement Scrollable interface 2023-08-23 09:51:57 +02:00
Karl Tauber
3573188025 ScrollBar: support smooth scrolling via keyboard 2023-08-23 09:51:57 +02:00
Karl Tauber
1f2622819a FlatSmoothScrollingTest: support dark themes and added "Show table grid" and "Auto-resize mode" check boxes 2023-08-23 09:51:57 +02:00
Karl Tauber
305e9e602e ScrollBar: fixed jittery scrolling when in repeating mode (hold down mouse button) and smooth scrolling enabled 2023-08-23 09:51:57 +02:00
Karl Tauber
1ae31588c4 FlatSmoothScrollingTest: paint "temporary" scrollbar values in line chart using a lighter color 2023-08-23 09:51:56 +02:00
Karl Tauber
d64a8e93e1 FlatSmoothScrollingTest:
- use ChangeListener instead of AdjustmentListener because this is invoked before all other scrollbar listeners (which may run 20-30ms) and avoids a delay in the line chart
- use System.nanoTime() instead of System.currentTimeMillis() for better precision
- paint vertical lines in chart at every 200ms (was 1sec)
- print elapsed time between scrollbar events
2023-08-23 09:51:56 +02:00
Karl Tauber
e603bd81a1 FlatSmoothScrollingTest: added simple line chart that shows changes to scrollbar values 2023-08-23 09:51:56 +02:00
Karl Tauber
522ebb6fa3 FlatSmoothScrollingTest: allow enabling/disabling smooth scrolling with Alt+S without moving focus to checkbox; removed unused tree model 2023-08-23 09:51:56 +02:00
Karl Tauber
7a582c2d1f ScrollBar: fixed issue with updating thumb location (regressing since commit 2c3ef226692fa39b7e6eca3192d197c0b0753aa1) 2023-08-23 09:51:56 +02:00
Karl Tauber
762fe89867 FlatSmoothScrollingTest: added JTree, JTable, JTextArea, JTextPane and JEditorPane for testing smooth scrolling 2023-08-23 09:51:56 +02:00
Karl Tauber
1ebfe00f3c added system properties "flatlaf.animation" and "flatlaf.smoothScrolling" to disable all animations or smooth scrolling via command line (without modifying the application) 2023-08-23 09:51:56 +02:00
Karl Tauber
fdabca99b2 ScrollBar: fixed NPE when switching LaF while smooth scrolling animation is running (issue #50) 2023-08-23 09:51:56 +02:00
Karl Tauber
736305849a ScrollBar: set valueIsAdjusting property to true while smooth scrolling animation is running (issue #50) 2023-08-23 09:51:56 +02:00
Karl Tauber
889b5ea56a ScrollBar: fixed smooth scrolling issues when continuously scrolling (issue #50) 2023-08-23 09:51:56 +02:00
Karl Tauber
82514ccbfc Demo: added "Options > Smooth Scrolling" to menu (issue #50) 2023-08-23 09:51:56 +02:00
Karl Tauber
b67b701d1e ScrollPane: use smooth scrolling when rotating the mouse wheel (issue #50) 2023-08-23 09:51:56 +02:00
Karl Tauber
7f226a2742 ScrollBar: use smooth scrolling when clicking on track or on arrow button (issue #50) 2023-08-23 09:51:56 +02:00
182 changed files with 5171 additions and 12826 deletions

View File

@@ -19,24 +19,25 @@ jobs:
# test against # test against
# - Java 8 (minimum requirement) # - Java 8 (minimum requirement)
# - Java LTS versions (11, 17, ...) # - Java LTS versions (11, 17, ...)
# - latest Java version(s) # - lastest Java version(s)
java: java:
- 8 - 8
- 11 # LTS - 11 # LTS
- 17 # LTS - 17 # LTS
- 19
toolchain: [""] toolchain: [""]
include: include:
- java: 17 - java: 17
toolchain: 21 # latest toolchain: 20 # latest
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v3
- uses: gradle/wrapper-validation-action@v1 - uses: gradle/wrapper-validation-action@v1
if: matrix.java == '8' if: matrix.java == '8'
- name: Setup Java ${{ matrix.java }} - name: Setup Java ${{ matrix.java }}
uses: actions/setup-java@v4 uses: actions/setup-java@v3
with: with:
java-version: ${{ matrix.java }} java-version: ${{ matrix.java }}
distribution: temurin # Java 8, 11 and 17 are pre-installed on ubuntu-latest distribution: temurin # Java 8, 11 and 17 are pre-installed on ubuntu-latest
@@ -50,7 +51,7 @@ jobs:
run: ./gradlew build -Dtoolchain=${{ matrix.toolchain }} run: ./gradlew build -Dtoolchain=${{ matrix.toolchain }}
- name: Upload artifacts - name: Upload artifacts
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v3
if: matrix.java == '11' if: matrix.java == '11'
with: with:
name: FlatLaf-build-artifacts name: FlatLaf-build-artifacts
@@ -70,10 +71,10 @@ jobs:
github.repository == 'JFormDesigner/FlatLaf' github.repository == 'JFormDesigner/FlatLaf'
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v3
- name: Setup Java 11 - name: Setup Java 11
uses: actions/setup-java@v4 uses: actions/setup-java@v3
with: with:
java-version: 11 java-version: 11
distribution: temurin # pre-installed on ubuntu-latest distribution: temurin # pre-installed on ubuntu-latest
@@ -106,10 +107,10 @@ jobs:
github.repository == 'JFormDesigner/FlatLaf' github.repository == 'JFormDesigner/FlatLaf'
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v3
- name: Setup Java 11 - name: Setup Java 11
uses: actions/setup-java@v4 uses: actions/setup-java@v3
with: with:
java-version: 11 java-version: 11
distribution: temurin # pre-installed on ubuntu-latest distribution: temurin # pre-installed on ubuntu-latest

View File

@@ -30,10 +30,10 @@ jobs:
github.repository == 'JFormDesigner/FlatLaf' github.repository == 'JFormDesigner/FlatLaf'
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v3
- name: Setup Java 11 - name: Setup Java 11
uses: actions/setup-java@v4 uses: actions/setup-java@v3
with: with:
java-version: 11 java-version: 11
distribution: temurin # pre-installed on ubuntu-latest distribution: temurin # pre-installed on ubuntu-latest

View File

@@ -20,18 +20,17 @@ jobs:
matrix: matrix:
os: os:
- windows - windows
- macos
- ubuntu - ubuntu
runs-on: ${{ matrix.os }}-latest runs-on: ${{ matrix.os }}-latest
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v3
- uses: gradle/wrapper-validation-action@v1 - uses: gradle/wrapper-validation-action@v1
- name: Setup Java 11 - name: Setup Java 11
uses: actions/setup-java@v4 uses: actions/setup-java@v3
with: with:
java-version: 11 java-version: 11
distribution: temurin distribution: temurin
@@ -43,7 +42,7 @@ jobs:
run: ./gradlew build-natives --no-daemon run: ./gradlew build-natives --no-daemon
- name: Upload artifacts - name: Upload artifacts
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v3
with: with:
name: FlatLaf-natives-build-artifacts-${{ matrix.os }} name: FlatLaf-natives-build-artifacts-${{ matrix.os }}
path: | path: |

View File

@@ -1,136 +1,6 @@
FlatLaf Change Log FlatLaf Change Log
================== ==================
## 3.4-SNAPSHOT
#### New features and improvements
- Native libraries: System property `flatlaf.nativeLibraryPath` now supports
loading native libraries named the same as on Maven central. Improved log
messages for loading fails.
- Fonts: Updated **Inter** to
[v4.0](https://github.com/rsms/inter/releases/tag/v4.0).
#### Fixed bugs
- JIDE CommandMenuBar: Fixed `ClassCastException` when JIDE command bar displays
`JideMenu` in popup. (PR #794)
## 3.3
#### New features and improvements
- macOS (10.14+): Popups (`JPopupMenu`, `JComboBox`, `JToolTip`, etc.) now use
native macOS rounded borders. (PR #772; issue #715)
- Native libraries: Added `libflatlaf-macos-arm64.dylib` and
`libflatlaf-macos-x86_64.dylib`. See also
https://www.formdev.com/flatlaf/native-libraries/.
- ScrollPane: Support rounded border. (PR #713)
- SplitPane: Support divider hover and pressed background colors. (PR #788)
- TabbedPane: Support vertical tabs. (PR #758, issue #633)
- TabbedPane: Paint rounded tab area background for rounded cards. (issue #717)
- ToolBar: Added styling properties `separatorWidth` and `separatorColor`.
#### Fixed bugs
- Button and ToggleButton: Selected buttons did not use explicitly set
foreground color. (issue 756)
- FileChooser: Catch NPE in Java 21 when getting icon for `.exe` files that use
default Windows exe icon. (see
[JDK-8320692](https://bugs.openjdk.org/browse/JDK-8320692))
- OptionPane: Fixed styling custom panel background in `JOptionPane`. (issue
#761)
- ScrollPane: Styling ScrollPane border properties did not work if view
component is a Table.
- Table:
- Switching theme looses table grid and intercell spacing. (issues #733 and
#750)
- Fixed background of `boolean` columns when using alternating row colors.
(issue #780)
- Fixed border arc of components in complex table cell editors. (issue #786)
- TableHeader:
- No longer temporary replace header cell renderer while painting. This avoids
a `StackOverflowError` in case that custom renderer does this too. (see
[NetBeans issue #6835](https://github.com/apache/netbeans/issues/6835)) This
also improves compatibility with custom table header implementations.
- Header cell renderer background/foreground colors were not restored after
hover if renderer uses `null` for background/foreground. (PR #790)
- TabbedPane:
- Avoid unnecessary repainting whole tabbed pane content area when layouting
leading/trailing components.
- Avoid unnecessary repainting of selected tab on temporary changes.
- Fixed "endless" layouting and repainting when using nested tabbed panes (top
and bottom tab placement) and RSyntaxTextArea (with enabled line-wrapping)
as tab content. (see
[jadx issue #2030](https://github.com/skylot/jadx/issues/2030))
- Fixed broken rendering after resizing window to minimum size and then
increasing size again. (issue #767)
#### Incompatibilities
- Removed support for JetBrains custom decorations, which required
[JetBrains Runtime](https://github.com/JetBrains/JetBrainsRuntime/wiki) (JBR)
8 or 11. It did not work for JBR 17. System property
`flatlaf.useJetBrainsCustomDecorations` is now ignored. **Note**: FlatLaf
window decorations continue to work with JBR.
## 3.2.5
#### Fixed bugs
- Popup: Fixed NPE if popup invoker is `null` on Windows 10. (issue #753;
regression in 3.2.1 in fix for #626)
## 3.2.4
#### Fixed bugs
- Popup: Fixed NPE if popup invoker is `null` on Linux with Wayland and Java 21.
(issue #752; regression in 3.2.3)
## 3.2.3
#### Fixed bugs
- Popup: Popups that request focus were not shown on Linux with Wayland and Java 21.
(issue #752)
## 3.2.2
#### Fixed bugs
- Button: Fixed painting icon and text at wrong location when using HTML text,
left/right vertical alignment and running in Java 19+. (issue #746)
- CheckBox and RadioButton: Fixed cut off right side when border is removed and
horizontal alignment is set to `right`. (issue #734)
- TabbedPane: Fixed NPE when using focusable component as tab component and
switching theme. (issue #745)
## 3.2.1
#### Fixed bugs
- Fixed memory leak in
`MultiResolutionImageSupport.create(int,Dimension[],Function<Dimension,Image>)`,
which caches images created by the producer function. Used by
`FlatSVGIcon.getImage()` and `FlatSVGUtils.createWindowIconImages()`. If you
use one of these methods, it is **strongly recommended** to upgrade to this
version, because if the returned image is larger and painted very often it may
result in an out-of-memory situation. (issue #726)
- FileChooser: Fixed occasional NPE in `FlatShortcutsPanel` on Windows. (issue
#718)
- TextField: Fixed placeholder text painting, which did not respect horizontal
alignment property of `JTextField`. (issue #721)
- Popup: Fixed drop shadow if popup overlaps a heavyweight component. (Windows
10 only; issue #626)
## 3.2 ## 3.2
#### New features and improvements #### New features and improvements
@@ -253,6 +123,7 @@ FlatLaf Change Log
- Windows DLLs are now digitally signed with FormDev Software GmbH - Windows DLLs are now digitally signed with FormDev Software GmbH
certificate. certificate.
#### Fixed bugs #### Fixed bugs
- FlatLaf window decorations: - FlatLaf window decorations:

View File

@@ -62,7 +62,7 @@ build script:
artifactId: flatlaf artifactId: flatlaf
version: (see button below) version: (see button below)
Otherwise, download `flatlaf-<version>.jar` here: Otherwise download `flatlaf-<version>.jar` here:
[![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.formdev/flatlaf/badge.svg?style=flat-square&color=007ec6)](https://maven-badges.herokuapp.com/maven-central/com.formdev/flatlaf) [![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.formdev/flatlaf/badge.svg?style=flat-square&color=007ec6)](https://maven-badges.herokuapp.com/maven-central/com.formdev/flatlaf)
@@ -141,6 +141,7 @@ details and downloads.
Buzz Buzz
---- ----
- [What others say about FlatLaf on Twitter](https://twitter.com/search?f=live&q=flatlaf)
- [FlatLaf 3.1 (and 3.0) announcement on Reddit](https://www.reddit.com/r/java/comments/12xgrsu/flatlaf_31_and_30_swing_look_and_feel/) - [FlatLaf 3.1 (and 3.0) announcement on Reddit](https://www.reddit.com/r/java/comments/12xgrsu/flatlaf_31_and_30_swing_look_and_feel/)
- [FlatLaf 1.0 announcement on Reddit](https://www.reddit.com/r/java/comments/lsbcwe/flatlaf_10_swing_look_and_feel/) - [FlatLaf 1.0 announcement on Reddit](https://www.reddit.com/r/java/comments/lsbcwe/flatlaf_10_swing_look_and_feel/)
- [FlatLaf announcement on Reddit](https://www.reddit.com/r/java/comments/dl0hu3/flatlaf_flat_look_and_feel/) - [FlatLaf announcement on Reddit](https://www.reddit.com/r/java/comments/dl0hu3/flatlaf_flat_look_and_feel/)

View File

@@ -45,7 +45,7 @@ public class ReorderJarEntries
// 1st pass: copy .properties files // 1st pass: copy .properties files
copyFiles( zipOutStream, jarFile, name -> name.endsWith( ".properties" ) ); copyFiles( zipOutStream, jarFile, name -> name.endsWith( ".properties" ) );
// 2nd pass: copy other files // 2st pass: copy other files
copyFiles( zipOutStream, jarFile, name -> !name.endsWith( ".properties" ) ); copyFiles( zipOutStream, jarFile, name -> !name.endsWith( ".properties" ) );
} }

View File

@@ -26,7 +26,7 @@ tasks {
// depend on :flatlaf-core:compileJava because it generates the JNI headers // depend on :flatlaf-core:compileJava because it generates the JNI headers
dependsOn( ":flatlaf-core:compileJava" ) dependsOn( ":flatlaf-core:compileJava" )
from( project( ":flatlaf-core" ).layout.buildDirectory.dir( "generated/jni-headers" ) ) from( project( ":flatlaf-core" ).buildDir.resolve( "generated/jni-headers" ) )
into( "src/main/headers" ) into( "src/main/headers" )
include( extension.headers ) include( extension.headers )
filter<org.apache.tools.ant.filters.FixCrLfFilter>( filter<org.apache.tools.ant.filters.FixCrLfFilter>(

View File

@@ -42,7 +42,7 @@ java {
tasks { tasks {
compileJava { compileJava {
// generate JNI headers // generate JNI headers
options.headerOutputDirectory.set( layout.buildDirectory.dir( "generated/jni-headers" ) ) options.headerOutputDirectory.set( buildDir.resolve( "generated/jni-headers" ) )
} }
jar { jar {
@@ -127,11 +127,9 @@ flatlafPublish {
val natives = "src/main/resources/com/formdev/flatlaf/natives" val natives = "src/main/resources/com/formdev/flatlaf/natives"
nativeArtifacts = listOf( nativeArtifacts = listOf(
NativeArtifact( "${natives}/flatlaf-windows-x86.dll", "windows-x86", "dll" ), NativeArtifact( "${natives}/flatlaf-windows-x86.dll", "windows-x86", "dll" ),
NativeArtifact( "${natives}/flatlaf-windows-x86_64.dll", "windows-x86_64", "dll" ), NativeArtifact( "${natives}/flatlaf-windows-x86_64.dll", "windows-x86_64", "dll" ),
NativeArtifact( "${natives}/flatlaf-windows-arm64.dll", "windows-arm64", "dll" ), NativeArtifact( "${natives}/flatlaf-windows-arm64.dll", "windows-arm64", "dll" ),
NativeArtifact( "${natives}/libflatlaf-macos-arm64.dylib", "macos-arm64", "dylib" ), NativeArtifact( "${natives}/libflatlaf-linux-x86_64.so", "linux-x86_64", "so" ),
NativeArtifact( "${natives}/libflatlaf-macos-x86_64.dylib", "macos-x86_64", "dylib" ),
NativeArtifact( "${natives}/libflatlaf-linux-x86_64.so", "linux-x86_64", "so" ),
) )
} }

View File

@@ -1,5 +1,5 @@
#Signature file v4.1 #Signature file v4.1
#Version 3.3 #Version 3.2
CLSS public abstract interface com.formdev.flatlaf.FlatClientProperties CLSS public abstract interface com.formdev.flatlaf.FlatClientProperties
fld public final static java.lang.String BUTTON_TYPE = "JButton.buttonType" fld public final static java.lang.String BUTTON_TYPE = "JButton.buttonType"
@@ -23,7 +23,6 @@ fld public final static java.lang.String PLACEHOLDER_TEXT = "JTextField.placehol
fld public final static java.lang.String POPUP_BORDER_CORNER_RADIUS = "Popup.borderCornerRadius" fld public final static java.lang.String POPUP_BORDER_CORNER_RADIUS = "Popup.borderCornerRadius"
fld public final static java.lang.String POPUP_DROP_SHADOW_PAINTED = "Popup.dropShadowPainted" fld public final static java.lang.String POPUP_DROP_SHADOW_PAINTED = "Popup.dropShadowPainted"
fld public final static java.lang.String POPUP_FORCE_HEAVY_WEIGHT = "Popup.forceHeavyWeight" fld public final static java.lang.String POPUP_FORCE_HEAVY_WEIGHT = "Popup.forceHeavyWeight"
fld public final static java.lang.String POPUP_ROUNDED_BORDER_WIDTH = "Popup.roundedBorderWidth"
fld public final static java.lang.String PROGRESS_BAR_LARGE_HEIGHT = "JProgressBar.largeHeight" fld public final static java.lang.String PROGRESS_BAR_LARGE_HEIGHT = "JProgressBar.largeHeight"
fld public final static java.lang.String PROGRESS_BAR_SQUARE = "JProgressBar.square" fld public final static java.lang.String PROGRESS_BAR_SQUARE = "JProgressBar.square"
fld public final static java.lang.String SCROLL_BAR_SHOW_BUTTONS = "JScrollBar.showButtons" fld public final static java.lang.String SCROLL_BAR_SHOW_BUTTONS = "JScrollBar.showButtons"
@@ -68,11 +67,6 @@ fld public final static java.lang.String TABBED_PANE_TAB_CLOSE_TOOLTIPTEXT = "JT
fld public final static java.lang.String TABBED_PANE_TAB_HEIGHT = "JTabbedPane.tabHeight" fld public final static java.lang.String TABBED_PANE_TAB_HEIGHT = "JTabbedPane.tabHeight"
fld public final static java.lang.String TABBED_PANE_TAB_ICON_PLACEMENT = "JTabbedPane.tabIconPlacement" fld public final static java.lang.String TABBED_PANE_TAB_ICON_PLACEMENT = "JTabbedPane.tabIconPlacement"
fld public final static java.lang.String TABBED_PANE_TAB_INSETS = "JTabbedPane.tabInsets" fld public final static java.lang.String TABBED_PANE_TAB_INSETS = "JTabbedPane.tabInsets"
fld public final static java.lang.String TABBED_PANE_TAB_ROTATION = "JTabbedPane.tabRotation"
fld public final static java.lang.String TABBED_PANE_TAB_ROTATION_AUTO = "auto"
fld public final static java.lang.String TABBED_PANE_TAB_ROTATION_LEFT = "left"
fld public final static java.lang.String TABBED_PANE_TAB_ROTATION_NONE = "none"
fld public final static java.lang.String TABBED_PANE_TAB_ROTATION_RIGHT = "right"
fld public final static java.lang.String TABBED_PANE_TAB_TYPE = "JTabbedPane.tabType" fld public final static java.lang.String TABBED_PANE_TAB_TYPE = "JTabbedPane.tabType"
fld public final static java.lang.String TABBED_PANE_TAB_TYPE_CARD = "card" fld public final static java.lang.String TABBED_PANE_TAB_TYPE_CARD = "card"
fld public final static java.lang.String TABBED_PANE_TAB_TYPE_UNDERLINED = "underlined" fld public final static java.lang.String TABBED_PANE_TAB_TYPE_UNDERLINED = "underlined"
@@ -247,7 +241,7 @@ meth public void setExtraDefaults(java.util.Map<java.lang.String,java.lang.Strin
meth public void uninitialize() meth public void uninitialize()
meth public void unregisterUIDefaultsGetter(java.util.function.Function<java.lang.Object,java.lang.Object>) meth public void unregisterUIDefaultsGetter(java.util.function.Function<java.lang.Object,java.lang.Object>)
supr javax.swing.plaf.basic.BasicLookAndFeel supr javax.swing.plaf.basic.BasicLookAndFeel
hfds DESKTOPFONTHINTS,aquaLoaded,customDefaultsSources,desktopPropertyListener,desktopPropertyName,desktopPropertyName2,extraDefaults,globalExtraDefaults,mnemonicHandler,oldPopupFactory,postInitialization,preferredFontFamily,preferredLightFontFamily,preferredMonospacedFontFamily,preferredSemiboldFontFamily,subMenuUsabilityHelperInstalled,systemColorGetter,uiDefaultsGetters,updateUIPending hfds DESKTOPFONTHINTS,aquaLoaded,customDefaultsSources,desktopPropertyListener,desktopPropertyName,desktopPropertyName2,extraDefaults,getUIMethod,getUIMethodInitialized,globalExtraDefaults,mnemonicHandler,oldPopupFactory,postInitialization,preferredFontFamily,preferredLightFontFamily,preferredMonospacedFontFamily,preferredSemiboldFontFamily,subMenuUsabilityHelperInstalled,systemColorGetter,uiDefaultsGetters,updateUIPending
hcls ActiveFont,FlatUIDefaults,ImageIconUIResource hcls ActiveFont,FlatUIDefaults,ImageIconUIResource
CLSS public abstract interface static com.formdev.flatlaf.FlatLaf$DisabledIconProvider CLSS public abstract interface static com.formdev.flatlaf.FlatLaf$DisabledIconProvider
@@ -288,7 +282,6 @@ fld public final static java.lang.String UI_SCALE_ALLOW_SCALE_DOWN = "flatlaf.ui
fld public final static java.lang.String UI_SCALE_ENABLED = "flatlaf.uiScale.enabled" fld public final static java.lang.String UI_SCALE_ENABLED = "flatlaf.uiScale.enabled"
fld public final static java.lang.String UPDATE_UI_ON_SYSTEM_FONT_CHANGE = "flatlaf.updateUIOnSystemFontChange" fld public final static java.lang.String UPDATE_UI_ON_SYSTEM_FONT_CHANGE = "flatlaf.updateUIOnSystemFontChange"
fld public final static java.lang.String USE_JETBRAINS_CUSTOM_DECORATIONS = "flatlaf.useJetBrainsCustomDecorations" fld public final static java.lang.String USE_JETBRAINS_CUSTOM_DECORATIONS = "flatlaf.useJetBrainsCustomDecorations"
anno 0 java.lang.Deprecated()
fld public final static java.lang.String USE_NATIVE_LIBRARY = "flatlaf.useNativeLibrary" fld public final static java.lang.String USE_NATIVE_LIBRARY = "flatlaf.useNativeLibrary"
fld public final static java.lang.String USE_TEXT_Y_CORRECTION = "flatlaf.useTextYCorrection" fld public final static java.lang.String USE_TEXT_Y_CORRECTION = "flatlaf.useTextYCorrection"
fld public final static java.lang.String USE_UBUNTU_FONT = "flatlaf.useUbuntuFont" fld public final static java.lang.String USE_UBUNTU_FONT = "flatlaf.useUbuntuFont"

View File

@@ -33,7 +33,7 @@ public interface FlatClientProperties
//---- JButton ------------------------------------------------------------ //---- JButton ------------------------------------------------------------
/** /**
* Specifies type of button. * Specifies type of a button.
* <p> * <p>
* <strong>Components</strong> {@link javax.swing.JButton} and {@link javax.swing.JToggleButton}<br> * <strong>Components</strong> {@link javax.swing.JButton} and {@link javax.swing.JToggleButton}<br>
* <strong>Value type</strong> {@link java.lang.String}<br> * <strong>Value type</strong> {@link java.lang.String}<br>
@@ -278,13 +278,12 @@ public interface FlatClientProperties
* <p> * <p>
* Note that this is not available on all platforms since it requires special support. * Note that this is not available on all platforms since it requires special support.
* Supported platforms: * Supported platforms:
* <ul> * <p>
* <li><strong>Windows 11</strong>: Only two corner radiuses are supported * <strong>Windows 11</strong> (x86 or x86_64): Only two corner radiuses are supported
* by the OS: {@code DWMWCP_ROUND} is 8px and {@code DWMWCP_ROUNDSMALL} is 4px. * by the OS: {@code DWMWCP_ROUND} is 8px and {@code DWMWCP_ROUNDSMALL} is 4px.
* If this value is {@code 1 - 4}, then {@code DWMWCP_ROUNDSMALL} is used. * If this value is {@code 1 - 4}, then {@code DWMWCP_ROUNDSMALL} is used.
* If it is {@code >= 5}, then {@code DWMWCP_ROUND} is used. * If it is {@code >= 5}, then {@code DWMWCP_ROUND} is used.
* <li><strong>macOS</strong> (10.14 and later): Any corner radius is supported. * <p>
* </ul>
* <strong>Component</strong> {@link javax.swing.JComponent}<br> * <strong>Component</strong> {@link javax.swing.JComponent}<br>
* <strong>Value type</strong> {@link java.lang.Integer}<br> * <strong>Value type</strong> {@link java.lang.Integer}<br>
* *
@@ -292,24 +291,6 @@ public interface FlatClientProperties
*/ */
String POPUP_BORDER_CORNER_RADIUS = "Popup.borderCornerRadius"; String POPUP_BORDER_CORNER_RADIUS = "Popup.borderCornerRadius";
/**
* Specifies the popup rounded border width if the component is shown in a popup
* or if the component is the owner of another component that is shown in a popup.
* <p>
* Only used if popup uses rounded border.
* <p>
* Note that this is not available on all platforms since it requires special support.
* Supported platforms:
* <ul>
* <li><strong>macOS</strong> (10.14 and later)
* </ul>
* <strong>Component</strong> {@link javax.swing.JComponent}<br>
* <strong>Value type</strong> {@link java.lang.Integer} or {@link java.lang.Float}<br>
*
* @since 3.3
*/
String POPUP_ROUNDED_BORDER_WIDTH = "Popup.roundedBorderWidth";
/** /**
* Specifies whether a drop shadow is painted if the component is shown in a popup * Specifies whether a drop shadow is painted if the component is shown in a popup
* or if the component is the owner of another component that is shown in a popup. * or if the component is the owner of another component that is shown in a popup.
@@ -421,10 +402,10 @@ public interface FlatClientProperties
String TITLE_BAR_SHOW_TITLE = "JRootPane.titleBarShowTitle"; String TITLE_BAR_SHOW_TITLE = "JRootPane.titleBarShowTitle";
/** /**
* Specifies whether the "iconify" button should be shown in the window title bar * Specifies whether the "iconfify" button should be shown in the window title bar
* (requires enabled window decorations). Default is {@code true}. * (requires enabled window decorations). Default is {@code true}.
* <p> * <p>
* Setting this shows/hides the "iconify" button * Setting this shows/hides the "iconfify" button
* for the {@code JFrame} that contains the root pane. * for the {@code JFrame} that contains the root pane.
* <p> * <p>
* <strong>Component</strong> {@link javax.swing.JRootPane}<br> * <strong>Component</strong> {@link javax.swing.JRootPane}<br>
@@ -506,7 +487,7 @@ public interface FlatClientProperties
* On macOS, Java supports this out of the box. * On macOS, Java supports this out of the box.
* <p> * <p>
* Note that this client property must be set before the window becomes displayable. * Note that this client property must be set before the window becomes displayable.
* Otherwise, an {@link IllegalComponentStateException} is thrown. * Otherwise an {@link IllegalComponentStateException} is thrown.
* <p> * <p>
* <strong>Component</strong> {@link javax.swing.JRootPane}<br> * <strong>Component</strong> {@link javax.swing.JRootPane}<br>
* <strong>Value type</strong> {@link java.lang.String}<br> * <strong>Value type</strong> {@link java.lang.String}<br>
@@ -951,59 +932,6 @@ public interface FlatClientProperties
*/ */
String TABBED_PANE_TAB_ICON_PLACEMENT = "JTabbedPane.tabIconPlacement"; String TABBED_PANE_TAB_ICON_PLACEMENT = "JTabbedPane.tabIconPlacement";
/**
* Specifies the rotation of the tabs (title, icon, etc.).
* <p>
* <strong>Component</strong> {@link javax.swing.JTabbedPane}<br>
* <strong>Value type</strong> {@link java.lang.Integer} or {@link java.lang.String}<br>
* <strong>Allowed Values</strong>
* {@link SwingConstants#LEFT},
* {@link SwingConstants#RIGHT},
* {@link #TABBED_PANE_TAB_ROTATION_NONE}, (default)
* {@link #TABBED_PANE_TAB_ROTATION_AUTO},
* {@link #TABBED_PANE_TAB_ROTATION_LEFT} or
* {@link #TABBED_PANE_TAB_ROTATION_RIGHT}
*
* @since 3.3
*/
String TABBED_PANE_TAB_ROTATION = "JTabbedPane.tabRotation";
/**
* Tabs are not rotated.
*
* @see #TABBED_PANE_TAB_ROTATION
* @since 3.3
*/
String TABBED_PANE_TAB_ROTATION_NONE = "none";
/**
* Tabs are rotated depending on tab placement.
* <p>
* For top and bottom tab placement, the tabs are not rotated.<br>
* For left tab placement, the tabs are rotated counter-clockwise.<br>
* For right tab placement, the tabs are rotated clockwise.
*
* @see #TABBED_PANE_TAB_ROTATION
* @since 3.3
*/
String TABBED_PANE_TAB_ROTATION_AUTO = "auto";
/**
* Tabs are rotated counter-clockwise.
*
* @see #TABBED_PANE_TAB_ROTATION
* @since 3.3
*/
String TABBED_PANE_TAB_ROTATION_LEFT = "left";
/**
* Tabs are rotated clockwise.
*
* @see #TABBED_PANE_TAB_ROTATION
* @since 3.3
*/
String TABBED_PANE_TAB_ROTATION_RIGHT = "right";
/** /**
* Specifies a component that will be placed at the leading edge of the tabs area. * Specifies a component that will be placed at the leading edge of the tabs area.
* <p> * <p>

View File

@@ -71,7 +71,7 @@ class FlatInputMaps
); );
} }
// join ltr and rtl bindings to fix up/down/etc. keys in right-to-left component orientation // join ltr and rtl bindings to fix up/down/etc keys in right-to-left component orientation
Object[] bindings = (Object[]) defaults.get( "PopupMenu.selectedWindowInputMapBindings" ); Object[] bindings = (Object[]) defaults.get( "PopupMenu.selectedWindowInputMapBindings" );
Object[] rtlBindings = (Object[]) defaults.get( "PopupMenu.selectedWindowInputMapBindings.RightToLeft" ); Object[] rtlBindings = (Object[]) defaults.get( "PopupMenu.selectedWindowInputMapBindings.RightToLeft" );
if( bindings != null && rtlBindings != null ) { if( bindings != null && rtlBindings != null ) {

View File

@@ -30,6 +30,9 @@ import java.awt.image.ImageProducer;
import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener; import java.beans.PropertyChangeListener;
import java.io.File; import java.io.File;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.net.URL; import java.net.URL;
import java.util.ArrayList; import java.util.ArrayList;
@@ -75,7 +78,6 @@ import com.formdev.flatlaf.ui.FlatNativeWindowBorder;
import com.formdev.flatlaf.ui.FlatPopupFactory; import com.formdev.flatlaf.ui.FlatPopupFactory;
import com.formdev.flatlaf.ui.FlatRootPaneUI; import com.formdev.flatlaf.ui.FlatRootPaneUI;
import com.formdev.flatlaf.ui.FlatUIUtils; import com.formdev.flatlaf.ui.FlatUIUtils;
import com.formdev.flatlaf.ui.JavaCompatibility2;
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI; import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
import com.formdev.flatlaf.util.FontUtils; import com.formdev.flatlaf.util.FontUtils;
import com.formdev.flatlaf.util.GrayFilter; import com.formdev.flatlaf.util.GrayFilter;
@@ -181,11 +183,17 @@ public abstract class FlatLaf
* This depends on the operating system and on the used Java runtime. * This depends on the operating system and on the used Java runtime.
* <p> * <p>
* This method returns {@code true} on Windows 10/11 (see exception below) * This method returns {@code true} on Windows 10/11 (see exception below)
* and on Linux, otherwise returns {@code false}. * and on Linux, {@code false} otherwise.
* <p>
* Returns also {@code false} on Windows 10/11 if
* FlatLaf native window border support is available (requires Windows 10/11).
* <p> * <p>
* Returns also {@code false} on Windows 10/11 if:
* <ul>
* <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 these cases, custom decorations are enabled by the root pane. * In these cases, custom decorations are enabled by the root pane.
* Usage of {@link JFrame#setDefaultLookAndFeelDecorated(boolean)} or * Usage of {@link JFrame#setDefaultLookAndFeelDecorated(boolean)} or
* {@link JDialog#setDefaultLookAndFeelDecorated(boolean)} is not necessary. * {@link JDialog#setDefaultLookAndFeelDecorated(boolean)} is not necessary.
@@ -1287,8 +1295,8 @@ public abstract class FlatLaf
* @since 2.5 * @since 2.5
*/ */
public static Map<String, Class<?>> getStyleableInfos( JComponent c ) { public static Map<String, Class<?>> getStyleableInfos( JComponent c ) {
ComponentUI ui = JavaCompatibility2.getUI( c ); StyleableUI ui = getStyleableUI( c );
return (ui instanceof StyleableUI) ? ((StyleableUI)ui).getStyleableInfos( c ) : null; return (ui != null) ? ui.getStyleableInfos( c ) : null;
} }
/** /**
@@ -1300,10 +1308,41 @@ public abstract class FlatLaf
*/ */
@SuppressWarnings( "unchecked" ) @SuppressWarnings( "unchecked" )
public static <T> T getStyleableValue( JComponent c, String key ) { public static <T> T getStyleableValue( JComponent c, String key ) {
ComponentUI ui = JavaCompatibility2.getUI( c ); StyleableUI ui = getStyleableUI( c );
return (ui instanceof StyleableUI) ? (T) ((StyleableUI)ui).getStyleableValue( c, key ) : null; return (ui != null) ? (T) ui.getStyleableValue( c, key ) : null;
} }
private static StyleableUI getStyleableUI( JComponent c ) {
if( !getUIMethodInitialized ) {
getUIMethodInitialized = true;
if( SystemInfo.isJava_9_orLater ) {
try {
// JComponent.getUI() is available since Java 9
getUIMethod = MethodHandles.lookup().findVirtual( JComponent.class, "getUI",
MethodType.methodType( ComponentUI.class ) );
} catch( Exception ex ) {
// ignore
}
}
}
try {
Object ui;
if( getUIMethod != null )
ui = getUIMethod.invoke( c );
else
ui = c.getClass().getMethod( "getUI" ).invoke( c );
return (ui instanceof StyleableUI) ? (StyleableUI) ui : null;
} catch( Throwable ex ) {
// ignore
return null;
}
}
private static boolean getUIMethodInitialized;
private static MethodHandle getUIMethod;
/** /**
* Returns the preferred font family to be used for (nearly) all fonts; or {@code null}. * Returns the preferred font family to be used for (nearly) all fonts; or {@code null}.
* *

View File

@@ -103,10 +103,7 @@ public interface FlatSystemProperties
* <p> * <p>
* <strong>Allowed Values</strong> {@code false} and {@code true}<br> * <strong>Allowed Values</strong> {@code false} and {@code true}<br>
* <strong>Default</strong> {@code false} (since v2; was {@code true} in v1) * <strong>Default</strong> {@code false} (since v2; was {@code true} in v1)
*
* @deprecated No longer used since FlatLaf 3.3. Retained for API compatibility.
*/ */
@Deprecated
String USE_JETBRAINS_CUSTOM_DECORATIONS = "flatlaf.useJetBrainsCustomDecorations"; String USE_JETBRAINS_CUSTOM_DECORATIONS = "flatlaf.useJetBrainsCustomDecorations";
/** /**
@@ -135,6 +132,14 @@ public interface FlatSystemProperties
*/ */
String ANIMATION = "flatlaf.animation"; String ANIMATION = "flatlaf.animation";
/**
* Specifies whether smooth scrolling is enabled.
* <p>
* <strong>Allowed Values</strong> {@code false} and {@code true}<br>
* <strong>Default</strong> {@code true}
*/
String SMOOTH_SCROLLING = "flatlaf.smoothScrolling";
/** /**
* Specifies whether vertical text position is corrected when UI is scaled on HiDPI screens. * Specifies whether vertical text position is corrected when UI is scaled on HiDPI screens.
* <p> * <p>
@@ -172,33 +177,19 @@ public interface FlatSystemProperties
String USE_NATIVE_LIBRARY = "flatlaf.useNativeLibrary"; String USE_NATIVE_LIBRARY = "flatlaf.useNativeLibrary";
/** /**
* Specifies a directory in which the FlatLaf native libraries are searched for. * Specifies a directory in which the native FlatLaf libraries have been extracted.
* The path can be absolute or relative to current application working directory. * The path can be absolute or relative to current application working directory.
* This can be used to avoid extraction of the native libraries to the temporary directory at runtime. * This can be used to avoid extraction of the native libraries to the temporary directory at runtime.
* <p> * <p>
* If the value is {@code "system"} (supported since FlatLaf 2.6), * If the value is {@code "system"}, then {@link System#loadLibrary(String)} is
* then {@link System#loadLibrary(String)} is used to load the native library. * used to load the native library.
* This searches for the native library in classloader of caller * Searches for the native library in classloader of caller
* (using {@link ClassLoader#findLibrary(String)}) and in paths specified * (using {@link ClassLoader#findLibrary(String)}) and in paths specified
* in system properties {@code sun.boot.library.path} and {@code java.library.path}. * in system properties {@code sun.boot.library.path} and {@code java.library.path}.
* (supported since FlatLaf 2.6)
* <p> * <p>
* If the native library can not be loaded from the given path (or via {@link System#loadLibrary(String)}), * If the native library can not loaded from the given path (or via {@link System#loadLibrary(String)}),
* then the embedded native library is extracted to the temporary directory and loaded from there. * then the embedded native library is extracted to the temporary directory and loaded from there.
* <p>
* The file names of the native libraries must be either:
* <ul>
* <li>the same as in flatlaf.jar in package 'com/formdev/flatlaf/natives' (required for "system") or
* <li>when downloaded from Maven central then as described here:
* <a href="https://www.formdev.com/flatlaf/native-libraries/">https://www.formdev.com/flatlaf/native-libraries/</a>
* (requires FlatLaf 3.4)
* </ul>
* <p>
* <strong>Note</strong>: Since FlatLaf 3.1 it is recommended to download the
* FlatLaf native libraries from Maven central and distribute them with your
* application in the same directory as flatlaf.jar.
* Then it is <strong>not necessary</strong> to set this system property.
* See <a href="https://www.formdev.com/flatlaf/native-libraries/">https://www.formdev.com/flatlaf/native-libraries/</a>
* for details.
* *
* @since 2 * @since 2
*/ */

View File

@@ -203,7 +203,7 @@ class LinuxFontPolicy
* Gets the default font for KDE from KDE configuration files. * Gets the default font for KDE from KDE configuration files.
* *
* The Swing fonts are not updated when the user changes system font size * The Swing fonts are not updated when the user changes system font size
* (System Settings > Fonts > Force Font DPI). An application restart is necessary. * (System Settings > Fonts > Force Font DPI). A application restart is necessary.
* This is the same behavior as in native KDE applications. * This is the same behavior as in native KDE applications.
* *
* The "display scale factor" (kdeglobals: [KScreen] > ScaleFactor) is not used * The "display scale factor" (kdeglobals: [KScreen] > ScaleFactor) is not used

View File

@@ -172,7 +172,7 @@ debug*/
targetTopY = popupLocation.y; targetTopY = popupLocation.y;
targetBottomY = popupLocation.y + popupSize.height; targetBottomY = popupLocation.y + popupSize.height;
// install own event queue to suppress mouse events when mouse is moved within safe triangle // install own event queue to supress mouse events when mouse is moved within safe triangle
if( subMenuEventQueue == null ) if( subMenuEventQueue == null )
subMenuEventQueue = new SubMenuEventQueue(); subMenuEventQueue = new SubMenuEventQueue();

View File

@@ -90,7 +90,7 @@ public class FlatTabbedPaneCloseIcon
closeSize.width, closeSize.height, closeArc, closeArc ); closeSize.width, closeSize.height, closeArc, closeArc );
} }
// set color of cross // set cross color
Color fg = FlatButtonUI.buttonStateColor( c, closeForeground, null, null, closeHoverForeground, closePressedForeground ); Color fg = FlatButtonUI.buttonStateColor( c, closeForeground, null, null, closeHoverForeground, closePressedForeground );
g.setColor( FlatUIUtils.deriveColor( fg, c.getForeground() ) ); g.setColor( FlatUIUtils.deriveColor( fg, c.getForeground() ) );

View File

@@ -57,11 +57,11 @@ public class FlatTreeOpenIcon
double arc = 1.5; double arc = 1.5;
double arc2 = 0.5; double arc2 = 0.5;
path = FlatUIUtils.createPath( false, path = FlatUIUtils.createPath( false,
// bottom-left of opened part // bottom-left of opend part
2,13.5, 2,13.5,
// top-left of opened part // top-left of opend part
FlatUIUtils.ROUNDED, 4.5,7.5, arc, FlatUIUtils.ROUNDED, 4.5,7.5, arc,
// top-right of opened part // top-right of opend part
FlatUIUtils.ROUNDED, 15.5,7.5, arc2, FlatUIUtils.ROUNDED, 15.5,7.5, arc2,
// bottom-right // bottom-right

View File

@@ -71,7 +71,7 @@ public abstract class FlatWindowAbstractIcon
protected void paintBackground( Component c, Graphics2D g ) { protected void paintBackground( Component c, Graphics2D g ) {
Color background = FlatButtonUI.buttonStateColor( c, null, null, null, hoverBackground, pressedBackground ); Color background = FlatButtonUI.buttonStateColor( c, null, null, null, hoverBackground, pressedBackground );
if( background != null ) { if( background != null ) {
// disable antialiasing for background rectangle painting to avoid blurry edges when scaled (e.g. at 125% or 175%) // disable antialiasing for background rectangle painting to avoid blury edges when scaled (e.g. at 125% or 175%)
Object oldHint = g.getRenderingHint( RenderingHints.KEY_ANTIALIASING ); Object oldHint = g.getRenderingHint( RenderingHints.KEY_ANTIALIASING );
g.setRenderingHint( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF ); g.setRenderingHint( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF );

View File

@@ -28,6 +28,7 @@ import javax.swing.JComboBox;
import javax.swing.JComponent; import javax.swing.JComponent;
import javax.swing.JScrollPane; import javax.swing.JScrollPane;
import javax.swing.JSpinner; import javax.swing.JSpinner;
import javax.swing.JViewport;
import javax.swing.UIManager; import javax.swing.UIManager;
import javax.swing.plaf.basic.BasicBorders; import javax.swing.plaf.basic.BasicBorders;
import com.formdev.flatlaf.FlatClientProperties; import com.formdev.flatlaf.FlatClientProperties;
@@ -194,7 +195,8 @@ public class FlatBorder
protected boolean isEnabled( Component c ) { protected boolean isEnabled( Component c ) {
if( c instanceof JScrollPane ) { if( c instanceof JScrollPane ) {
// check whether view component is disabled // check whether view component is disabled
Component view = FlatScrollPaneUI.getView( (JScrollPane) c ); JViewport viewport = ((JScrollPane)c).getViewport();
Component view = (viewport != null) ? viewport.getView() : null;
if( view != null && !isEnabled( view ) ) if( view != null && !isEnabled( view ) )
return false; return false;
} }

View File

@@ -53,8 +53,6 @@ import javax.swing.plaf.ToolBarUI;
import javax.swing.plaf.UIResource; import javax.swing.plaf.UIResource;
import javax.swing.plaf.basic.BasicButtonListener; import javax.swing.plaf.basic.BasicButtonListener;
import javax.swing.plaf.basic.BasicButtonUI; import javax.swing.plaf.basic.BasicButtonUI;
import javax.swing.plaf.basic.BasicHTML;
import javax.swing.text.View;
import com.formdev.flatlaf.FlatClientProperties; import com.formdev.flatlaf.FlatClientProperties;
import com.formdev.flatlaf.FlatLaf; import com.formdev.flatlaf.FlatLaf;
import com.formdev.flatlaf.icons.FlatHelpButtonIcon; import com.formdev.flatlaf.icons.FlatHelpButtonIcon;
@@ -553,45 +551,9 @@ public class FlatButtonUI
} }
} }
/**
* Similar to BasicButtonUI.paint(), but does not use zero insets for HTML text,
* which is done in BasicButtonUI.layout() since Java 19.
* See https://github.com/openjdk/jdk/pull/8407
* and https://github.com/openjdk/jdk/pull/8407#issuecomment-1761583430
*/
@Override @Override
public void paint( Graphics g, JComponent c ) { public void paint( Graphics g, JComponent c ) {
g = FlatLabelUI.createGraphicsHTMLTextYCorrection( g, c ); super.paint( FlatLabelUI.createGraphicsHTMLTextYCorrection( g, c ), c );
AbstractButton b = (AbstractButton) c;
// layout
String clippedText = layout( b, b.getFontMetrics( b.getFont() ), b.getWidth(), b.getHeight() );
// not used in FlatLaf, but invoked for compatibility with BasicButtonUI.paint()
clearTextShiftOffset();
// not used in FlatLaf, but invoked for compatibility with BasicButtonUI.paint()
ButtonModel model = b.getModel();
if( model.isArmed() && model.isPressed() )
paintButtonPressed( g, b );
// paint icon
if( b.getIcon() != null )
paintIcon( g, b, iconR );
// paint text
if( clippedText != null && !clippedText.isEmpty() ) {
View view = (View) b.getClientProperty( BasicHTML.propertyKey );
if( view != null )
view.paint( g, textR ); // HTML text
else
paintText( g, b, textR, clippedText );
}
// not used in FlatLaf, but invoked for compatibility with BasicButtonUI.paint()
if( b.isFocusPainted() && b.hasFocus() )
paintFocus( g, b, viewR, textR, iconR );
} }
@Override @Override
@@ -721,15 +683,14 @@ public class FlatButtonUI
} }
protected Color getForeground( JComponent c ) { protected Color getForeground( JComponent c ) {
Color fg = c.getForeground();
boolean toolBarButton = isToolBarButton( c ) || isBorderlessButton( c ); boolean toolBarButton = isToolBarButton( c ) || isBorderlessButton( c );
// selected state // selected state
if( ((AbstractButton)c).isSelected() ) { if( ((AbstractButton)c).isSelected() ) {
return buttonStateColor( c, return buttonStateColor( c,
toolBarButton toolBarButton
? (toolbarSelectedForeground != null ? toolbarSelectedForeground : fg) ? (toolbarSelectedForeground != null ? toolbarSelectedForeground : c.getForeground())
: (isCustomForeground( fg ) ? fg : selectedForeground), : selectedForeground,
toolBarButton toolBarButton
? (toolbarDisabledSelectedForeground != null ? toolbarDisabledSelectedForeground : disabledText) ? (toolbarDisabledSelectedForeground != null ? toolbarDisabledSelectedForeground : disabledText)
: (disabledSelectedForeground != null ? disabledSelectedForeground : disabledText), : (disabledSelectedForeground != null ? disabledSelectedForeground : disabledText),
@@ -741,7 +702,7 @@ public class FlatButtonUI
// toolbar button // toolbar button
if( toolBarButton ) { if( toolBarButton ) {
return buttonStateColor( c, return buttonStateColor( c,
fg, c.getForeground(),
disabledText, disabledText,
null, null,
toolbarHoverForeground, toolbarHoverForeground,
@@ -752,7 +713,7 @@ public class FlatButtonUI
return buttonStateColor( c, return buttonStateColor( c,
getForegroundBase( c, def ), getForegroundBase( c, def ),
disabledText, disabledText,
isCustomForeground( fg ) ? null : (def ? defaultFocusedForeground : focusedForeground), isCustomForeground( c.getForeground() ) ? null : (def ? defaultFocusedForeground : focusedForeground),
def ? defaultHoverForeground : hoverForeground, def ? defaultHoverForeground : hoverForeground,
def ? defaultPressedForeground : pressedForeground ); def ? defaultPressedForeground : pressedForeground );
} }
@@ -825,67 +786,6 @@ public class FlatButtonUI
return margin instanceof UIResource && Objects.equals( margin, defaultMargin ); return margin instanceof UIResource && Objects.equals( margin, defaultMargin );
} }
@Override
public int getBaseline( JComponent c, int width, int height ) {
return getBaselineImpl( c, width, height );
}
/**
* Similar to BasicButtonUI.getBaseline(), but does not use zero insets for HTML text,
* which is done in BasicButtonUI.layout() since Java 19.
* See https://github.com/openjdk/jdk/pull/8407
* and https://github.com/openjdk/jdk/pull/8407#issuecomment-1761583430
*/
static int getBaselineImpl( JComponent c, int width, int height ) {
if( width < 0 || height < 0 )
throw new IllegalArgumentException();
AbstractButton b = (AbstractButton) c;
String text = b.getText();
if( text == null || text.isEmpty() )
return -1;
FontMetrics fm = b.getFontMetrics( b.getFont() );
layout( b, fm, width, height );
View view = (View) b.getClientProperty( BasicHTML.propertyKey );
if( view != null ) {
// HTML text
int baseline = BasicHTML.getHTMLBaseline( view, textR.width, textR.height );
return (baseline >= 0) ? textR.y + baseline : baseline;
} else
return textR.y + fm.getAscent();
}
/**
* Similar to BasicButtonUI.layout(), but does not use zero insets for HTML text,
* which is done in BasicButtonUI.layout() since Java 19.
* See https://github.com/openjdk/jdk/pull/8407
* and https://github.com/openjdk/jdk/pull/8407#issuecomment-1761583430
*/
private static String layout( AbstractButton b, FontMetrics fm, int width, int height ) {
// compute view rectangle
Insets insets = b.getInsets();
viewR.setBounds( insets.left, insets.top,
width - insets.left - insets.right,
height - insets.top - insets.bottom );
// reset rectangles
textR.setBounds( 0, 0, 0, 0 );
iconR.setBounds( 0, 0, 0, 0 );
String text = b.getText();
return SwingUtilities.layoutCompoundLabel( b, fm, text, b.getIcon(),
b.getVerticalAlignment(), b.getHorizontalAlignment(),
b.getVerticalTextPosition(), b.getHorizontalTextPosition(),
viewR, iconR, textR,
(text != null) ? b.getIconTextGap() : 0 );
}
private static Rectangle viewR = new Rectangle();
private static Rectangle textR = new Rectangle();
private static Rectangle iconR = new Rectangle();
//---- class FlatButtonListener ------------------------------------------- //---- class FlatButtonListener -------------------------------------------
protected class FlatButtonListener protected class FlatButtonListener

View File

@@ -926,7 +926,7 @@ public class FlatComboBoxUI
protected void configurePopup() { protected void configurePopup() {
super.configurePopup(); super.configurePopup();
// make opaque to avoid that background shines through border (e.g. at 150% scaling) // make opaque to avoid that background shines thru border (e.g. at 150% scaling)
setOpaque( true ); setOpaque( true );
// set popup border // set popup border
@@ -944,7 +944,7 @@ public class FlatComboBoxUI
if( popupBackground != null ) if( popupBackground != null )
list.setBackground( popupBackground ); list.setBackground( popupBackground );
// set popup background because it may shine through when scaled (e.g. at 150%) // set popup background because it may shine thru when scaled (e.g. at 150%)
// use non-UIResource to avoid that it is overwritten when making // use non-UIResource to avoid that it is overwritten when making
// popup visible (see JPopupMenu.setInvoker()) in theme editor preview // popup visible (see JPopupMenu.setInvoker()) in theme editor preview
setBackground( FlatUIUtils.nonUIResource( list.getBackground() ) ); setBackground( FlatUIUtils.nonUIResource( list.getBackground() ) );
@@ -1090,7 +1090,7 @@ public class FlatComboBoxUI
} }
// using synchronized to avoid problems with code that modifies combo box // using synchronized to avoid problems with code that modifies combo box
// (model, selection, etc.) not on AWT thread (which should be not done) // (model, selection, etc) not on AWT thread (which should be not done)
synchronized void install( Component c, int focusWidth ) { synchronized void install( Component c, int focusWidth ) {
if( !(c instanceof JComponent) ) if( !(c instanceof JComponent) )
return; return;
@@ -1242,7 +1242,7 @@ public class FlatComboBoxUI
* Key selection manager that delegates to the default manager. * Key selection manager that delegates to the default manager.
* Shows the popup if Space key is pressed and "typed characters" buffer is empty. * Shows the popup if Space key is pressed and "typed characters" buffer is empty.
* If items contain spaces (e.g. "a b") it is still possible to select them * If items contain spaces (e.g. "a b") it is still possible to select them
* by pressing keys 'a', 'Space' and 'b'. * by pressing keys a, Space and b.
*/ */
private class FlatKeySelectionManager private class FlatKeySelectionManager
implements JComboBox.KeySelectionManager, UIResource implements JComboBox.KeySelectionManager, UIResource

View File

@@ -31,6 +31,7 @@ import javax.swing.UIManager;
import javax.swing.plaf.ComponentUI; import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.basic.BasicEditorPaneUI; import javax.swing.plaf.basic.BasicEditorPaneUI;
import javax.swing.text.Caret; import javax.swing.text.Caret;
import javax.swing.text.DefaultEditorKit;
import javax.swing.text.JTextComponent; import javax.swing.text.JTextComponent;
import com.formdev.flatlaf.FlatClientProperties; import com.formdev.flatlaf.FlatClientProperties;
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable; import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
@@ -145,6 +146,21 @@ public class FlatEditorPaneUI
focusListener = null; focusListener = null;
} }
@Override
protected void installKeyboardActions() {
super.installKeyboardActions();
installKeyboardActions( getComponent() );
}
static void installKeyboardActions( JTextComponent c ) {
FlatScrollPaneUI.installSmoothScrollingDelegateActions( c, false,
/* page-down */ DefaultEditorKit.pageDownAction, // PAGE_DOWN
/* page-up */ DefaultEditorKit.pageUpAction, // PAGE_UP
/* DefaultEditorKit.selectionPageDownAction */ "selection-page-down", // shift PAGE_DOWN
/* DefaultEditorKit.selectionPageUpAction */ "selection-page-up" // shift PAGE_UP
);
}
@Override @Override
protected Caret createCaret() { protected Caret createCaret() {
return new FlatCaret( null, false ); return new FlatCaret( null, false );
@@ -159,6 +175,11 @@ public class FlatEditorPaneUI
super.propertyChange( e ); super.propertyChange( e );
propertyChange( getComponent(), e, this::installStyle ); propertyChange( getComponent(), e, this::installStyle );
// BasicEditorPaneUI.propertyChange() re-applied actions from editor kit,
// which removed our delegate actions
if( "editorKit".equals( propertyName ) )
installKeyboardActions( getComponent() );
} }
static void propertyChange( JTextComponent c, PropertyChangeEvent e, Runnable installStyle ) { static void propertyChange( JTextComponent c, PropertyChangeEvent e, Runnable installStyle ) {

View File

@@ -29,7 +29,6 @@ import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener; import java.beans.PropertyChangeListener;
import java.io.File; import java.io.File;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.function.Function; import java.util.function.Function;
import javax.swing.AbstractButton; import javax.swing.AbstractButton;
import javax.swing.Box; import javax.swing.Box;
@@ -370,11 +369,7 @@ public class FlatFileChooserUI
// get system icon // get system icon
if( f != null ) { if( f != null ) {
try { icon = getFileChooser().getFileSystemView().getSystemIcon( f );
icon = getFileChooser().getFileSystemView().getSystemIcon( f );
} catch( NullPointerException ex ) {
// Java 21 may throw a NPE for exe files that use default Windows exe icon
}
if( icon != null ) { if( icon != null ) {
if( icon instanceof ImageIcon ) if( icon instanceof ImageIcon )
@@ -413,7 +408,7 @@ public class FlatFileChooserUI
protected final File[] files; protected final File[] files;
protected final JToggleButton[] buttons; protected final JToggleButton[] buttons;
protected final ButtonGroup buttonGroup = new ButtonGroup(); protected final ButtonGroup buttonGroup;
@SuppressWarnings( "unchecked" ) @SuppressWarnings( "unchecked" )
public FlatShortcutsPanel( JFileChooser fc ) { public FlatShortcutsPanel( JFileChooser fc ) {
@@ -432,22 +427,19 @@ public class FlatFileChooserUI
File[] files = getChooserShortcutPanelFiles( fsv ); File[] files = getChooserShortcutPanelFiles( fsv );
if( filesFunction != null ) if( filesFunction != null )
files = filesFunction.apply( files ); files = filesFunction.apply( files );
this.files = files;
// create toolbar buttons // create toolbar buttons
ArrayList<File> filesList = new ArrayList<>(); buttons = new JToggleButton[files.length];
ArrayList<JToggleButton> buttonsList = new ArrayList<>(); buttonGroup = new ButtonGroup();
for( File file : files ) { for( int i = 0; i < files.length; i++ ) {
if( file == null )
continue;
// wrap drive path // wrap drive path
if( fsv.isFileSystemRoot( file ) ) if( fsv.isFileSystemRoot( files[i] ) )
file = fsv.createFileObject( file.getAbsolutePath() ); files[i] = fsv.createFileObject( files[i].getAbsolutePath() );
File file = files[i];
String name = getDisplayName( fsv, file ); String name = getDisplayName( fsv, file );
Icon icon = getIcon( fsv, file ); Icon icon = getIcon( fsv, file );
if( name == null )
continue;
// remove path from name // remove path from name
int lastSepIndex = name.lastIndexOf( File.separatorChar ); int lastSepIndex = name.lastIndexOf( File.separatorChar );
@@ -462,21 +454,15 @@ public class FlatFileChooserUI
// create button // create button
JToggleButton button = createButton( name, icon ); JToggleButton button = createButton( name, icon );
File f = file;
button.addActionListener( e -> { button.addActionListener( e -> {
fc.setCurrentDirectory( f ); fc.setCurrentDirectory( file );
} ); } );
add( button ); add( button );
buttonGroup.add( button ); buttonGroup.add( button );
buttons[i] = button;
filesList.add( file );
buttonsList.add( button );
} }
this.files = filesList.toArray( new File[filesList.size()] );
this.buttons = buttonsList.toArray( new JToggleButton[buttonsList.size()] );
directoryChanged( fc.getCurrentDirectory() ); directoryChanged( fc.getCurrentDirectory() );
} }
@@ -544,36 +530,29 @@ public class FlatFileChooserUI
if( doNotUseSystemIcons() ) if( doNotUseSystemIcons() )
return new FlatFileViewDirectoryIcon(); return new FlatFileViewDirectoryIcon();
// Java 17+ supports getting larger system icons
try { try {
// Java 17+ supports getting larger system icons if( SystemInfo.isJava_17_orLater ) {
try { Method m = fsv.getClass().getMethod( "getSystemIcon", File.class, int.class, int.class );
if( SystemInfo.isJava_17_orLater ) { return (Icon) m.invoke( fsv, file, iconSize.width, iconSize.height );
Method m = fsv.getClass().getMethod( "getSystemIcon", File.class, int.class, int.class ); } else if( iconSize.width > 16 || iconSize.height > 16 ) {
return (Icon) m.invoke( fsv, file, iconSize.width, iconSize.height ); Class<?> cls = Class.forName( "sun.awt.shell.ShellFolder" );
} else if( iconSize.width > 16 || iconSize.height > 16 ) { if( cls.isInstance( file ) ) {
Class<?> cls = Class.forName( "sun.awt.shell.ShellFolder" ); Method m = file.getClass().getMethod( "getIcon", boolean.class );
if( cls.isInstance( file ) ) { m.setAccessible( true );
Method m = file.getClass().getMethod( "getIcon", boolean.class ); Image image = (Image) m.invoke( file, true );
m.setAccessible( true ); if( image != null )
Image image = (Image) m.invoke( file, true ); return new ImageIcon( image );
if( image != null )
return new ImageIcon( image );
}
} }
} catch( Exception ex ) {
// do not log InaccessibleObjectException because access
// may be denied via VM option '--illegal-access=deny' (default in Java 16)
// (not catching InaccessibleObjectException here because it is new in Java 9, but FlatLaf also runs on Java 8)
if( !"java.lang.reflect.InaccessibleObjectException".equals( ex.getClass().getName() ) )
LoggingFacade.INSTANCE.logSevere( null, ex );
} }
} catch( IllegalAccessException ex ) {
// get system icon in default size 16x16 // do not log because access may be denied via VM option '--illegal-access=deny'
return fsv.getSystemIcon( file ); } catch( Exception ex ) {
} catch( NullPointerException ex ) { LoggingFacade.INSTANCE.logSevere( null, ex );
// Java 21 may throw a NPE for exe files that use default Windows exe icon
return new FlatFileViewDirectoryIcon();
} }
// get system icon in default size 16x16
return fsv.getSystemIcon( file );
} }
protected void directoryChanged( File file ) { protected void directoryChanged( File file ) {

View File

@@ -22,7 +22,6 @@ import java.awt.Component;
import java.awt.Graphics; import java.awt.Graphics;
import java.awt.Graphics2D; import java.awt.Graphics2D;
import java.awt.Insets; import java.awt.Insets;
import javax.swing.JComponent;
/** /**
* Line border for various components. * Line border for various components.
@@ -67,9 +66,6 @@ public class FlatLineBorder
@Override @Override
public void paintBorder( Component c, Graphics g, int x, int y, int width, int height ) { public void paintBorder( Component c, Graphics g, int x, int y, int width, int height ) {
if( c instanceof JComponent && ((JComponent)c).getClientProperty( FlatPopupFactory.KEY_POPUP_USES_NATIVE_BORDER ) != null )
return;
Graphics2D g2 = (Graphics2D) g.create(); Graphics2D g2 = (Graphics2D) g.create();
try { try {
FlatUIUtils.setRenderingHints( g2 ); FlatUIUtils.setRenderingHints( g2 );

View File

@@ -411,7 +411,7 @@ public class FlatListUI
int leftIndex = locationToIndex( list, new Point( r.x - 1, r.y ) ); int leftIndex = locationToIndex( list, new Point( r.x - 1, r.y ) );
int rightIndex = locationToIndex( list, new Point( r.x + r.width, r.y ) ); int rightIndex = locationToIndex( list, new Point( r.x + r.width, r.y ) );
// special handling for the case that last column contains fewer cells than the other columns // special handling for the case that last column contains less cells than the other columns
boolean ltr = list.getComponentOrientation().isLeftToRight(); boolean ltr = list.getComponentOrientation().isLeftToRight();
if( !ltr && leftIndex >= 0 && leftIndex != row && leftIndex == locationToIndex( list, new Point( r.x - 1, r.y - 1 ) ) ) if( !ltr && leftIndex >= 0 && leftIndex != row && leftIndex == locationToIndex( list, new Point( r.x - 1, r.y - 1 ) ) )
leftIndex = -1; leftIndex = -1;

View File

@@ -27,7 +27,6 @@ import java.awt.event.ActionEvent;
import java.beans.PropertyChangeListener; import java.beans.PropertyChangeListener;
import java.util.Map; import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import javax.swing.AbstractAction;
import javax.swing.ActionMap; import javax.swing.ActionMap;
import javax.swing.BoxLayout; import javax.swing.BoxLayout;
import javax.swing.JComponent; import javax.swing.JComponent;
@@ -149,7 +148,7 @@ public class FlatMenuBarUI
map = new ActionMapUIResource(); map = new ActionMapUIResource();
SwingUtilities.replaceUIActionMap( menuBar, map ); SwingUtilities.replaceUIActionMap( menuBar, map );
} }
map.put( "takeFocus", new TakeFocus() ); map.put( "takeFocus", new TakeFocus( "takeFocus" ) );
} }
/** @since 2 */ /** @since 2 */
@@ -373,8 +372,12 @@ public class FlatMenuBarUI
* On other platforms, the popup of the first menu is shown. * On other platforms, the popup of the first menu is shown.
*/ */
private static class TakeFocus private static class TakeFocus
extends AbstractAction extends FlatUIAction
{ {
public TakeFocus( String name ) {
super( name );
}
@Override @Override
public void actionPerformed( ActionEvent e ) { public void actionPerformed( ActionEvent e ) {
JMenuBar menuBar = (JMenuBar) e.getSource(); JMenuBar menuBar = (JMenuBar) e.getSource();

View File

@@ -17,7 +17,6 @@
package com.formdev.flatlaf.ui; package com.formdev.flatlaf.ui;
import java.awt.Color; import java.awt.Color;
import java.awt.Container;
import java.awt.Dimension; import java.awt.Dimension;
import java.awt.Font; import java.awt.Font;
import java.awt.Graphics; import java.awt.Graphics;
@@ -272,7 +271,7 @@ public class FlatMenuUI
if( !isHover() ) if( !isHover() )
selectionBackground = getStyleFromMenuBarUI( ui -> ui.selectionBackground, menuBarSelectionBackground, selectionBackground ); selectionBackground = getStyleFromMenuBarUI( ui -> ui.selectionBackground, menuBarSelectionBackground, selectionBackground );
Container menuBar = menuItem.getParent(); JMenuBar menuBar = (JMenuBar) menuItem.getParent();
JRootPane rootPane = SwingUtilities.getRootPane( menuBar ); JRootPane rootPane = SwingUtilities.getRootPane( menuBar );
if( rootPane != null && rootPane.getParent() instanceof Window && if( rootPane != null && rootPane.getParent() instanceof Window &&
rootPane.getJMenuBar() == menuBar && rootPane.getJMenuBar() == menuBar &&
@@ -322,17 +321,12 @@ public class FlatMenuUI
} }
private <T> T getStyleFromMenuBarUI( Function<FlatMenuBarUI, T> f, T defaultValue ) { private <T> T getStyleFromMenuBarUI( Function<FlatMenuBarUI, T> f, T defaultValue ) {
Container menuItemParent = menuItem.getParent(); MenuBarUI ui = ((JMenuBar)menuItem.getParent()).getUI();
if( menuItemParent instanceof JMenuBar ) { if( !(ui instanceof FlatMenuBarUI) )
MenuBarUI ui = ((JMenuBar) menuItemParent).getUI(); return defaultValue;
if( ui instanceof FlatMenuBarUI ) {
T value = f.apply( (FlatMenuBarUI) ui ); T value = f.apply( (FlatMenuBarUI) ui );
if( value != null ) { return (value != null) ? value : defaultValue;
return value;
}
}
}
return defaultValue;
} }
} }
} }

View File

@@ -78,15 +78,9 @@ class FlatNativeLibrary
// //
// To avoid this, flatlaf.dll is not linked to jawt.dll, // To avoid this, flatlaf.dll is not linked to jawt.dll,
// which avoids loading jawt.dll when flatlaf.dll is loaded. // which avoids loading jawt.dll when flatlaf.dll is loaded.
// Instead, flatlaf.dll dynamically loads jawt.dll when first used, // Instead flatlaf.dll dynamically loads jawt.dll when first used,
// which is guaranteed after AWT initialization. // which is guaranteed after AWT initialization.
} else if( SystemInfo.isMacOS_10_14_Mojave_orLater && (SystemInfo.isAARCH64 || SystemInfo.isX86_64) ) {
// macOS: requires macOS 10.14 or later (arm64 or x86_64)
classifier = SystemInfo.isAARCH64 ? "macos-arm64" : "macos-x86_64";
ext = "dylib";
} else if( SystemInfo.isLinux && SystemInfo.isX86_64 ) { } else if( SystemInfo.isLinux && SystemInfo.isX86_64 ) {
// Linux: requires x86_64 // Linux: requires x86_64
@@ -117,32 +111,13 @@ class FlatNativeLibrary
if( library.isLoaded() ) if( library.isLoaded() )
return library; return library;
LoggingFacade.INSTANCE.logSevere( "Did not find library '" + System.mapLibraryName( libraryName ) LoggingFacade.INSTANCE.logSevere( "Did not find library " + libraryName + " in java.library.path, using extracted library instead", null );
+ "' in java.library.path '" + System.getProperty( "java.library.path" )
+ "', using extracted library instead", null );
} else { } else {
// try standard library naming scheme
// (same as in flatlaf.jar in package 'com/formdev/flatlaf/natives')
File libraryFile = new File( libraryPath, System.mapLibraryName( libraryName ) ); File libraryFile = new File( libraryPath, System.mapLibraryName( libraryName ) );
if( libraryFile.exists() ) if( libraryFile.exists() )
return new NativeLibrary( libraryFile, true ); return new NativeLibrary( libraryFile, true );
// try Maven naming scheme LoggingFacade.INSTANCE.logSevere( "Did not find external library " + libraryFile + ", using extracted library instead", null );
// (see https://www.formdev.com/flatlaf/native-libraries/)
String libraryName2 = null;
File jarFile = getJarFile();
if( jarFile != null ) {
libraryName2 = buildLibraryName( jarFile, classifier, ext );
File libraryFile2 = new File( libraryPath, libraryName2 );
if( libraryFile2.exists() )
return new NativeLibrary( libraryFile2, true );
}
LoggingFacade.INSTANCE.logSevere( "Did not find library '"
+ libraryFile.getName()
+ (libraryName2 != null ? ("' or '" + libraryName2) : "")
+ "' in '" + libraryFile.getParentFile().getAbsolutePath()
+ "', using extracted library instead", null );
} }
} }
@@ -170,33 +145,6 @@ class FlatNativeLibrary
* flatlaf-3.1-linux-x86_64.so * flatlaf-3.1-linux-x86_64.so
*/ */
private static File findLibraryBesideJar( String classifier, String ext ) { private static File findLibraryBesideJar( String classifier, String ext ) {
// get location of FlatLaf jar (or fat/uber application jar)
File jarFile = getJarFile();
if( jarFile == null )
return null;
// build library file
String libraryName = buildLibraryName( jarFile, classifier, ext );
File parent = jarFile.getParentFile();
// check whether native library exists in same directory as jar
File libraryFile = new File( parent, libraryName );
if( libraryFile.isFile() )
return libraryFile;
// if jar is in "lib" directory, then also check whether native library exists
// in "../bin" directory
if( parent.getName().equalsIgnoreCase( "lib" ) ) {
libraryFile = new File( parent.getParentFile(), "bin/" + libraryName );
if( libraryFile.isFile() )
return libraryFile;
}
// native library not found
return null;
}
private static File getJarFile() {
try { try {
// get location of FlatLaf jar // get location of FlatLaf jar
CodeSource codeSource = FlatNativeLibrary.class.getProtectionDomain().getCodeSource(); CodeSource codeSource = FlatNativeLibrary.class.getProtectionDomain().getCodeSource();
@@ -214,19 +162,31 @@ class FlatNativeLibrary
if( !jarFile.isFile() ) if( !jarFile.isFile() )
return null; return null;
return jarFile; // build library file
String jarName = jarFile.getName();
String jarBasename = jarName.substring( 0, jarName.lastIndexOf( '.' ) );
File parent = jarFile.getParentFile();
String libraryName = jarBasename
+ (jarBasename.contains( "flatlaf" ) ? "" : "-flatlaf")
+ '-' + classifier + '.' + ext;
// check whether native library exists in same directory as jar
File libraryFile = new File( parent, libraryName );
if( libraryFile.isFile() )
return libraryFile;
// if jar is in "lib" directory, then also check whether library exists
// in "../bin" directory
if( parent.getName().equalsIgnoreCase( "lib" ) ) {
libraryFile = new File( parent.getParentFile(), "bin/" + libraryName );
if( libraryFile.isFile() )
return libraryFile;
}
} catch( Exception ex ) { } catch( Exception ex ) {
LoggingFacade.INSTANCE.logSevere( ex.getMessage(), ex ); LoggingFacade.INSTANCE.logSevere( ex.getMessage(), ex );
return null;
} }
}
private static String buildLibraryName( File jarFile, String classifier, String ext ) { return null;
String jarName = jarFile.getName();
String jarBasename = jarName.substring( 0, jarName.lastIndexOf( '.' ) );
return jarBasename
+ (jarBasename.contains( "flatlaf" ) ? "" : "-flatlaf")
+ '-' + classifier + '.' + ext;
} }
private static void loadJAWT() { private static void loadJAWT() {

View File

@@ -35,12 +35,6 @@ import com.formdev.flatlaf.util.SystemInfo;
*/ */
class FlatNativeLinuxLibrary class FlatNativeLinuxLibrary
{ {
/**
* Checks whether native library is loaded/available.
* <p>
* <b>Note</b>: It is required to invoke this method before invoking any other
* method of this class. Otherwise, the native library may not be loaded.
*/
static boolean isLoaded() { static boolean isLoaded() {
return SystemInfo.isLinux && FlatNativeLibrary.isLoaded(); return SystemInfo.isLinux && FlatNativeLibrary.isLoaded();
} }

View File

@@ -1,56 +0,0 @@
/*
* Copyright 2023 FormDev Software GmbH
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.formdev.flatlaf.ui;
import java.awt.Window;
/**
* Native methods for macOS.
* <p>
* <b>Note</b>: This is private API. Do not use!
*
* <h2>Methods that use windows as parameter</h2>
*
* For all methods that accept a {@link java.awt.Window} as parameter,
* the underlying macOS window must be already created,
* otherwise the method fails. You can use following to ensure this:
* <pre>{@code
* if( !window.isDisplayable() )
* window.addNotify();
* }</pre>
* or invoke the method after packing the window. E.g.
* <pre>{@code
* window.pack();
* }</pre>
*
* @author Karl Tauber
* @since 3.3
*/
public class FlatNativeMacLibrary
{
/**
* Checks whether native library is loaded/available.
* <p>
* <b>Note</b>: It is required to invoke this method before invoking any other
* method of this class. Otherwise, the native library may not be loaded.
*/
public static boolean isLoaded() {
return FlatNativeLibrary.isLoaded();
}
public native static boolean setWindowRoundedBorder( Window window, float radius, float borderWidth, int borderColor );
}

View File

@@ -17,26 +17,20 @@
package com.formdev.flatlaf.ui; package com.formdev.flatlaf.ui;
import java.awt.Color; import java.awt.Color;
import java.awt.Component;
import java.awt.Container; import java.awt.Container;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle; import java.awt.Rectangle;
import java.awt.Toolkit;
import java.awt.Window; import java.awt.Window;
import java.beans.PropertyChangeListener; import java.beans.PropertyChangeListener;
import java.util.List; import java.util.List;
import javax.swing.JDialog; import javax.swing.JDialog;
import javax.swing.JFrame; import javax.swing.JFrame;
import javax.swing.JRootPane; import javax.swing.JRootPane;
import javax.swing.SwingUtilities;
import javax.swing.UIManager; import javax.swing.UIManager;
import javax.swing.event.ChangeListener; import javax.swing.event.ChangeListener;
import javax.swing.plaf.BorderUIResource;
import com.formdev.flatlaf.FlatClientProperties; import com.formdev.flatlaf.FlatClientProperties;
import com.formdev.flatlaf.FlatLaf; import com.formdev.flatlaf.FlatLaf;
import com.formdev.flatlaf.FlatSystemProperties; import com.formdev.flatlaf.FlatSystemProperties;
import com.formdev.flatlaf.util.HiDPIUtils; import com.formdev.flatlaf.ui.JBRCustomDecorations.JBRWindowTopBorder;
import com.formdev.flatlaf.util.SystemInfo; import com.formdev.flatlaf.util.SystemInfo;
/** /**
@@ -60,15 +54,27 @@ public class FlatNativeWindowBorder
!SystemInfo.isWinPE && !SystemInfo.isWinPE &&
FlatSystemProperties.getBoolean( FlatSystemProperties.USE_WINDOW_DECORATIONS, true ); FlatSystemProperties.getBoolean( FlatSystemProperties.USE_WINDOW_DECORATIONS, true );
// check this field before using class JBRCustomDecorations to avoid unnecessary loading of that class
private static final boolean canUseJBRCustomDecorations =
canUseWindowDecorations &&
SystemInfo.isJetBrainsJVM_11_orLater &&
FlatSystemProperties.getBoolean( FlatSystemProperties.USE_JETBRAINS_CUSTOM_DECORATIONS, false );
private static Boolean supported; private static Boolean supported;
private static Provider nativeProvider; private static Provider nativeProvider;
public static boolean isSupported() { public static boolean isSupported() {
if( canUseJBRCustomDecorations )
return JBRCustomDecorations.isSupported();
initialize(); initialize();
return supported; return supported;
} }
static Object install( JRootPane rootPane ) { static Object install( JRootPane rootPane ) {
if( canUseJBRCustomDecorations )
return JBRCustomDecorations.install( rootPane );
if( !isSupported() ) if( !isSupported() )
return null; return null;
@@ -157,6 +163,11 @@ public class FlatNativeWindowBorder
} }
static void uninstall( JRootPane rootPane, Object data ) { static void uninstall( JRootPane rootPane, Object data ) {
if( canUseJBRCustomDecorations ) {
JBRCustomDecorations.uninstall( rootPane, data );
return;
}
if( !isSupported() ) if( !isSupported() )
return; return;
@@ -204,6 +215,9 @@ public class FlatNativeWindowBorder
} }
public static boolean hasCustomDecoration( Window window ) { public static boolean hasCustomDecoration( Window window ) {
if( canUseJBRCustomDecorations )
return JBRCustomDecorations.hasCustomDecoration( window );
if( !isSupported() ) if( !isSupported() )
return false; return false;
@@ -211,6 +225,11 @@ public class FlatNativeWindowBorder
} }
public static void setHasCustomDecoration( Window window, boolean hasCustomDecoration ) { public static void setHasCustomDecoration( Window window, boolean hasCustomDecoration ) {
if( canUseJBRCustomDecorations ) {
JBRCustomDecorations.setHasCustomDecoration( window, hasCustomDecoration );
return;
}
if( !isSupported() ) if( !isSupported() )
return; return;
@@ -221,6 +240,11 @@ public class FlatNativeWindowBorder
List<Rectangle> hitTestSpots, Rectangle appIconBounds, Rectangle minimizeButtonBounds, List<Rectangle> hitTestSpots, Rectangle appIconBounds, Rectangle minimizeButtonBounds,
Rectangle maximizeButtonBounds, Rectangle closeButtonBounds ) Rectangle maximizeButtonBounds, Rectangle closeButtonBounds )
{ {
if( canUseJBRCustomDecorations ) {
JBRCustomDecorations.setTitleBarHeightAndHitTestSpots( window, titleBarHeight, hitTestSpots );
return;
}
if( !isSupported() ) if( !isSupported() )
return; return;
@@ -229,7 +253,7 @@ public class FlatNativeWindowBorder
} }
static boolean showWindow( Window window, int cmd ) { static boolean showWindow( Window window, int cmd ) {
if( !isSupported() ) if( canUseJBRCustomDecorations || !isSupported() )
return false; return false;
return nativeProvider.showWindow( window, cmd ); return nativeProvider.showWindow( window, cmd );
@@ -296,36 +320,20 @@ public class FlatNativeWindowBorder
* No longer needed since Windows 11. * No longer needed since Windows 11.
*/ */
static class WindowTopBorder static class WindowTopBorder
extends BorderUIResource.EmptyBorderUIResource extends JBRCustomDecorations.JBRWindowTopBorder
{ {
private static WindowTopBorder instance; private static WindowTopBorder instance;
private final Color activeLightColor = new Color( 0x707070 ); static JBRWindowTopBorder getInstance() {
private final Color activeDarkColor = new Color( 0x2D2E2F ); if( canUseJBRCustomDecorations )
private final Color inactiveLightColor = new Color( 0xaaaaaa ); return JBRWindowTopBorder.getInstance();
private final Color inactiveDarkColor = new Color( 0x494A4B );
private boolean colorizationAffectsBorders;
private Color activeColor;
static WindowTopBorder getInstance() {
if( instance == null ) if( instance == null )
instance = new WindowTopBorder(); instance = new WindowTopBorder();
return instance; return instance;
} }
WindowTopBorder() { @Override
super( 1, 0, 0, 0 );
update();
installListeners();
}
void update() {
colorizationAffectsBorders = isColorizationColorAffectsBorders();
activeColor = calculateActiveBorderColor();
}
void installListeners() { void installListeners() {
nativeProvider.addChangeListener( e -> { nativeProvider.addChangeListener( e -> {
update(); update();
@@ -338,69 +346,19 @@ public class FlatNativeWindowBorder
} ); } );
} }
@Override
boolean isColorizationColorAffectsBorders() { boolean isColorizationColorAffectsBorders() {
return nativeProvider.isColorizationColorAffectsBorders(); return nativeProvider.isColorizationColorAffectsBorders();
} }
@Override
Color getColorizationColor() { Color getColorizationColor() {
return nativeProvider.getColorizationColor(); return nativeProvider.getColorizationColor();
} }
@Override
int getColorizationColorBalance() { int getColorizationColorBalance() {
return nativeProvider.getColorizationColorBalance(); return nativeProvider.getColorizationColorBalance();
} }
private Color calculateActiveBorderColor() {
if( !colorizationAffectsBorders )
return null;
Color colorizationColor = getColorizationColor();
if( colorizationColor != null ) {
int colorizationColorBalance = getColorizationColorBalance();
if( colorizationColorBalance < 0 || colorizationColorBalance > 100 )
colorizationColorBalance = 100;
if( colorizationColorBalance == 0 )
return new Color( 0xD9D9D9 );
if( colorizationColorBalance == 100 )
return colorizationColor;
float alpha = colorizationColorBalance / 100.0f;
float remainder = 1 - alpha;
int r = Math.round( colorizationColor.getRed() * alpha + 0xD9 * remainder );
int g = Math.round( colorizationColor.getGreen() * alpha + 0xD9 * remainder );
int b = Math.round( colorizationColor.getBlue() * alpha + 0xD9 * remainder );
// avoid potential IllegalArgumentException in Color constructor
r = Math.min( Math.max( r, 0 ), 255 );
g = Math.min( Math.max( g, 0 ), 255 );
b = Math.min( Math.max( b, 0 ), 255 );
return new Color( r, g, b );
}
Color activeBorderColor = (Color) Toolkit.getDefaultToolkit().getDesktopProperty( "win.frame.activeBorderColor" );
return (activeBorderColor != null) ? activeBorderColor : UIManager.getColor( "MenuBar.borderColor" );
}
@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();
boolean dark = FlatLaf.isLafDark();
g.setColor( active
? (activeColor != null ? activeColor : (dark ? activeDarkColor : activeLightColor))
: (dark ? inactiveDarkColor : inactiveLightColor) );
HiDPIUtils.paintAtScale1x( (Graphics2D) g, x, y, width, height, this::paintImpl );
}
private void paintImpl( Graphics2D g, int x, int y, int width, int height, double scaleFactor ) {
g.fillRect( x, y, width, 1 );
}
void repaintBorder( Component c ) {
c.repaint( 0, 0, c.getWidth(), 1 );
}
} }
} }

View File

@@ -16,7 +16,6 @@
package com.formdev.flatlaf.ui; package com.formdev.flatlaf.ui;
import java.awt.Color;
import java.awt.Window; import java.awt.Window;
import com.formdev.flatlaf.util.SystemInfo; import com.formdev.flatlaf.util.SystemInfo;
@@ -32,12 +31,6 @@ public class FlatNativeWindowsLibrary
{ {
private static long osBuildNumber = Long.MIN_VALUE; private static long osBuildNumber = Long.MIN_VALUE;
/**
* Checks whether native library is loaded/available.
* <p>
* <b>Note</b>: It is required to invoke this method before invoking any other
* method of this class. Otherwise, the native library may not be loaded.
*/
public static boolean isLoaded() { public static boolean isLoaded() {
return SystemInfo.isWindows && FlatNativeLibrary.isLoaded(); return SystemInfo.isWindows && FlatNativeLibrary.isLoaded();
} }
@@ -100,60 +93,15 @@ public class FlatNativeWindowsLibrary
public native static boolean setWindowCornerPreference( long hwnd, int cornerPreference ); public native static boolean setWindowCornerPreference( long hwnd, int cornerPreference );
/** /**
* DWMWINDOWATTRIBUTE * Sets the color of the window border.
* see https://learn.microsoft.com/en-us/windows/win32/api/dwmapi/ne-dwmapi-dwmwindowattribute * The red/green/blue values must be in range {@code 0 - 255}.
* * If red is {@code -1}, then the system default border color is used (useful to reset the border color).
* @since 3.3 * If red is {@code -2}, then no border is painted.
*/ * <p>
public static final int * Invokes Win32 API method {@code DwmSetWindowAttribute(DWMWA_BORDER_COLOR)}.
DWMWA_USE_IMMERSIVE_DARK_MODE = 20,
DWMWA_BORDER_COLOR = 34,
DWMWA_CAPTION_COLOR = 35,
DWMWA_TEXT_COLOR = 36;
/**
* Invokes Win32 API method {@code DwmSetWindowAttribute()} with a {@code BOOL} attribute value.
* See https://learn.microsoft.com/en-us/windows/win32/api/dwmapi/nf-dwmapi-dwmsetwindowattribute
*
* @since 3.3
*/
public native static boolean dwmSetWindowAttributeBOOL( long hwnd, int attribute, boolean value );
/**
* Invokes Win32 API method {@code DwmSetWindowAttribute()} with a {@code DWORD} attribute value.
* See https://learn.microsoft.com/en-us/windows/win32/api/dwmapi/nf-dwmapi-dwmsetwindowattribute
*
* @since 3.3
*/
public native static boolean dwmSetWindowAttributeDWORD( long hwnd, int attribute, int value );
/** @since 3.3 */
public static final int
// use this constant to reset any window part colors to the system default behavior
DWMWA_COLOR_DEFAULT = 0xFFFFFFFF,
// use this constant to specify that a window part should not be rendered
DWMWA_COLOR_NONE = 0xFFFFFFFE;
/** @since 3.3 */
public static final Color COLOR_NONE = new Color( 0, true );
/**
* Invokes Win32 API method {@code DwmSetWindowAttribute()} with a {@code COLORREF} attribute value.
* See https://learn.microsoft.com/en-us/windows/win32/api/dwmapi/nf-dwmapi-dwmsetwindowattribute * See https://learn.microsoft.com/en-us/windows/win32/api/dwmapi/nf-dwmapi-dwmsetwindowattribute
* <p> * <p>
* Supported since Windows 11 Build 22000. * Supported since Windows 11 Build 22000.
*
* @since 3.3
*/ */
public static boolean dwmSetWindowAttributeCOLORREF( long hwnd, int attribute, Color color ) { public native static boolean setWindowBorderColor( long hwnd, int red, int green, int blue );
// convert color to Windows RGB value
int rgb = (color == COLOR_NONE)
? DWMWA_COLOR_NONE
: (color != null
? (color.getRed() | (color.getGreen() << 8) | (color.getBlue() << 16))
: DWMWA_COLOR_DEFAULT);
// DwmSetWindowAttribute() expects COLORREF as attribute value, which is defined as DWORD
return dwmSetWindowAttributeDWORD( hwnd, attribute, rgb );
}
} }

View File

@@ -30,7 +30,9 @@ import javax.swing.JPanel;
import javax.swing.JRootPane; import javax.swing.JRootPane;
import javax.swing.SwingUtilities; import javax.swing.SwingUtilities;
import javax.swing.UIManager; import javax.swing.UIManager;
import javax.swing.border.Border;
import javax.swing.plaf.ComponentUI; import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.UIResource;
import javax.swing.plaf.basic.BasicHTML; import javax.swing.plaf.basic.BasicHTML;
import javax.swing.plaf.basic.BasicOptionPaneUI; import javax.swing.plaf.basic.BasicOptionPaneUI;
import com.formdev.flatlaf.FlatClientProperties; import com.formdev.flatlaf.FlatClientProperties;
@@ -113,6 +115,13 @@ public class FlatOptionPaneUI
sameSizeButtons = FlatUIUtils.getUIBoolean( "OptionPane.sameSizeButtons", true ); sameSizeButtons = FlatUIUtils.getUIBoolean( "OptionPane.sameSizeButtons", true );
} }
@Override
protected void installComponents() {
super.installComponents();
updateChildPanels( optionPane );
}
@Override @Override
protected PropertyChangeListener createPropertyChangeListener() { protected PropertyChangeListener createPropertyChangeListener() {
PropertyChangeListener superListener = super.createPropertyChangeListener(); PropertyChangeListener superListener = super.createPropertyChangeListener();
@@ -146,13 +155,6 @@ public class FlatOptionPaneUI
protected Container createMessageArea() { protected Container createMessageArea() {
Container messageArea = super.createMessageArea(); Container messageArea = super.createMessageArea();
// use non-UIResource OptionPane.messageAreaBorder to avoid that it is replaced when switching LaF
// and make panel non-opaque for OptionPane.background
updateAreaPanel( messageArea );
// make known sub-panels non-opaque for OptionPane.background
updateKnownChildPanels( messageArea );
// set icon-message gap // set icon-message gap
if( iconMessageGap > 0 ) { if( iconMessageGap > 0 ) {
Component iconMessageSeparator = SwingUtils.getComponentByName( messageArea, "OptionPane.separator" ); Component iconMessageSeparator = SwingUtils.getComponentByName( messageArea, "OptionPane.separator" );
@@ -167,10 +169,6 @@ public class FlatOptionPaneUI
protected Container createButtonArea() { protected Container createButtonArea() {
Container buttonArea = super.createButtonArea(); Container buttonArea = super.createButtonArea();
// use non-UIResource OptionPane.buttonAreaBorder to avoid that it is replaced when switching LaF
// and make panel non-opaque for OptionPane.background
updateAreaPanel( buttonArea );
// scale button padding and subtract focusWidth // scale button padding and subtract focusWidth
if( buttonArea.getLayout() instanceof ButtonAreaLayout ) { if( buttonArea.getLayout() instanceof ButtonAreaLayout ) {
ButtonAreaLayout layout = (ButtonAreaLayout) buttonArea.getLayout(); ButtonAreaLayout layout = (ButtonAreaLayout) buttonArea.getLayout();
@@ -220,33 +218,22 @@ public class FlatOptionPaneUI
super.addMessageComponents( container, cons, msg, maxll, internallyCreated ); super.addMessageComponents( container, cons, msg, maxll, internallyCreated );
} }
private void updateAreaPanel( Container area ) { private void updateChildPanels( Container c ) {
if( !(area instanceof JPanel) )
return;
// use non-UIResource border to avoid that it is replaced when switching LaF
// and make panel non-opaque for OptionPane.background
JPanel panel = (JPanel) area;
panel.setBorder( FlatUIUtils.nonUIResource( panel.getBorder() ) );
panel.setOpaque( false );
}
private void updateKnownChildPanels( Container c ) {
for( Component child : c.getComponents() ) { for( Component child : c.getComponents() ) {
if( child instanceof JPanel && child.getName() != null ) { if( child.getClass() == JPanel.class ) {
switch( child.getName() ) { JPanel panel = (JPanel)child;
case "OptionPane.realBody":
case "OptionPane.body": // make sub-panel non-opaque for OptionPane.background
case "OptionPane.separator": panel.setOpaque( false );
case "OptionPane.break":
// make known sub-panels non-opaque for OptionPane.background // use non-UIResource borders to avoid that they are replaced when switching LaF
((JPanel)child).setOpaque( false ); Border border = panel.getBorder();
break; if( border instanceof UIResource )
} panel.setBorder( FlatUIUtils.nonUIResource( border ) );
} }
if( child instanceof Container ) if( child instanceof Container )
updateKnownChildPanels( (Container) child ); updateChildPanels( (Container) child );
} }
} }

View File

@@ -36,7 +36,6 @@ import java.awt.Window;
import java.awt.event.ComponentEvent; import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener; import java.awt.event.ComponentListener;
import java.awt.event.MouseEvent; import java.awt.event.MouseEvent;
import java.awt.event.WindowFocusListener;
import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType; import java.lang.invoke.MethodType;
@@ -71,8 +70,6 @@ import com.formdev.flatlaf.util.UIScale;
public class FlatPopupFactory public class FlatPopupFactory
extends PopupFactory extends PopupFactory
{ {
static final String KEY_POPUP_USES_NATIVE_BORDER = "FlatLaf.internal.FlatPopupFactory.popupUsesNativeBorder";
private MethodHandle java8getPopupMethod; private MethodHandle java8getPopupMethod;
private MethodHandle java9getPopupMethod; private MethodHandle java9getPopupMethod;
@@ -86,35 +83,26 @@ public class FlatPopupFactory
y = pt.y; y = pt.y;
} }
fixLinuxWaylandJava21focusIssue( owner );
boolean forceHeavyWeight = isOptionEnabled( owner, contents, FlatClientProperties.POPUP_FORCE_HEAVY_WEIGHT, "Popup.forceHeavyWeight" ); boolean forceHeavyWeight = isOptionEnabled( owner, contents, FlatClientProperties.POPUP_FORCE_HEAVY_WEIGHT, "Popup.forceHeavyWeight" );
if( !isOptionEnabled( owner, contents, FlatClientProperties.POPUP_DROP_SHADOW_PAINTED, "Popup.dropShadowPainted" ) || SystemInfo.isProjector || SystemInfo.isWebswing ) if( !isOptionEnabled( owner, contents, FlatClientProperties.POPUP_DROP_SHADOW_PAINTED, "Popup.dropShadowPainted" ) || SystemInfo.isProjector || SystemInfo.isWebswing )
return new NonFlashingPopup( getPopupForScreenOfOwner( owner, contents, x, y, forceHeavyWeight ), contents ); return new NonFlashingPopup( getPopupForScreenOfOwner( owner, contents, x, y, forceHeavyWeight ), contents );
// macOS and Linux adds drop shadow to heavy weight popups // macOS and Linux adds drop shadow to heavy weight popups
if( SystemInfo.isMacOS || SystemInfo.isLinux ) { if( SystemInfo.isMacOS || SystemInfo.isLinux )
NonFlashingPopup popup = new NonFlashingPopup( getPopupForScreenOfOwner( owner, contents, x, y, true ), contents ); return new NonFlashingPopup( getPopupForScreenOfOwner( owner, contents, x, y, true ), contents );
if( popup.popupWindow != null && SystemInfo.isMacOS && FlatNativeMacLibrary.isLoaded() )
setupRoundedBorder( popup.popupWindow, owner, contents );
return popup;
}
// Windows 11 with FlatLaf native library can use rounded corners and shows drop shadow for heavy weight popups // Windows 11 with FlatLaf native library can use rounded corners and shows drop shadow for heavy weight popups
int borderCornerRadius;
if( isWindows11BorderSupported() && if( isWindows11BorderSupported() &&
getBorderCornerRadius( owner, contents ) > 0 ) (borderCornerRadius = getBorderCornerRadius( owner, contents )) > 0 )
{ {
NonFlashingPopup popup = new NonFlashingPopup( getPopupForScreenOfOwner( owner, contents, x, y, true ), contents ); NonFlashingPopup popup = new NonFlashingPopup( getPopupForScreenOfOwner( owner, contents, x, y, true ), contents );
if( popup.popupWindow != null ) if( popup.popupWindow != null )
setupRoundedBorder( popup.popupWindow, owner, contents ); setupWindows11Border( popup.popupWindow, contents, borderCornerRadius );
return popup; return popup;
} }
// check whether popup overlaps a heavy weight component
if( !forceHeavyWeight && overlapsHeavyWeightComponent( owner, contents, x, y ) )
forceHeavyWeight = true;
// create drop shadow popup // create drop shadow popup
return new DropShadowPopup( getPopupForScreenOfOwner( owner, contents, x, y, forceHeavyWeight ), owner, contents ); return new DropShadowPopup( getPopupForScreenOfOwner( owner, contents, x, y, forceHeavyWeight ), owner, contents );
} }
@@ -167,6 +155,67 @@ public class FlatPopupFactory
} }
} }
/**
* Shows the given popup and, if necessary, fixes the location of a heavy weight popup window.
* <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 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
*/
private static void showPopupAndFixLocation( Popup popup, Window popupWindow ) {
if( popupWindow != null ) {
// remember location of heavy weight popup window
int x = popupWindow.getX();
int y = popupWindow.getY();
popup.show();
// restore popup window location if it has changed
// (probably scaled when screens use different scale factors)
if( popupWindow.getX() != x || popupWindow.getY() != y )
popupWindow.setLocation( x, y );
} else
popup.show();
}
private boolean isOptionEnabled( Component owner, Component contents, String clientKey, String uiKey ) {
Object value = getOption( owner, contents, clientKey, uiKey );
return (value instanceof Boolean) ? (Boolean) value : false;
}
private int getBorderCornerRadius( Component owner, Component contents ) {
String uiKey =
(contents instanceof BasicComboPopup) ? "ComboBox.borderCornerRadius" :
(contents instanceof JPopupMenu) ? "PopupMenu.borderCornerRadius" :
(contents instanceof JToolTip) ? "ToolTip.borderCornerRadius" :
"Popup.borderCornerRadius";
Object value = getOption( owner, contents, FlatClientProperties.POPUP_BORDER_CORNER_RADIUS, uiKey );
return (value instanceof Integer) ? (Integer) value : 0;
}
/**
* Get option from:
* <ol>
* <li>client property {@code clientKey} of {@code owner}
* <li>client property {@code clientKey} of {@code contents}
* <li>UI property {@code uiKey}
* </ol>
*/
private Object getOption( Component owner, Component contents, String clientKey, String uiKey ) {
for( Component c : new Component[] { owner, contents } ) {
if( c instanceof JComponent ) {
Object value = ((JComponent)c).getClientProperty( clientKey );
if( value != null )
return value;
}
}
return UIManager.get( uiKey );
}
/** /**
* There is no API in Java 8 to force creation of heavy weight popups, * There is no API in Java 8 to force creation of heavy weight popups,
* but it is possible with reflection. Java 9 provides a new method. * but it is possible with reflection. Java 9 provides a new method.
@@ -202,33 +251,6 @@ public class FlatPopupFactory
} }
} }
private static boolean isOptionEnabled( Component owner, Component contents, String clientKey, String uiKey ) {
Object value = getOption( owner, contents, clientKey, uiKey );
return (value instanceof Boolean) ? (Boolean) value : false;
}
/**
* Get option from:
* <ol>
* <li>client property {@code clientKey} of {@code owner}
* <li>client property {@code clientKey} of {@code contents}
* <li>UI property {@code uiKey}
* </ol>
*/
private static Object getOption( Component owner, Component contents, String clientKey, String uiKey ) {
for( Component c : new Component[] { owner, contents } ) {
if( c instanceof JComponent ) {
Object value = ((JComponent)c).getClientProperty( clientKey );
if( value != null )
return value;
}
}
return UIManager.get( uiKey );
}
//---- tooltips -----------------------------------------------------------
/** /**
* Usually ToolTipManager places a tooltip at (mouseLocation.x, mouseLocation.y + 20). * Usually ToolTipManager places a tooltip at (mouseLocation.x, mouseLocation.y + 20).
* In case that the tooltip would be partly outside of the screen, * In case that the tooltip would be partly outside of the screen,
@@ -313,58 +335,48 @@ public class FlatPopupFactory
((JComponent)owner).getToolTipLocation( me ) != null; ((JComponent)owner).getToolTipLocation( me ) != null;
} }
//---- native rounded border ----------------------------------------------
private static boolean isWindows11BorderSupported() { private static boolean isWindows11BorderSupported() {
return SystemInfo.isWindows_11_orLater && FlatNativeWindowsLibrary.isLoaded(); return SystemInfo.isWindows_11_orLater && FlatNativeWindowsLibrary.isLoaded();
} }
private static void setupRoundedBorder( Window popupWindow, Component owner, Component contents ) { private static void setupWindows11Border( Window popupWindow, Component contents, int borderCornerRadius ) {
// make sure that the native window is created // make sure that the Windows 11 window is created
if( !popupWindow.isDisplayable() ) if( !popupWindow.isDisplayable() )
popupWindow.addNotify(); popupWindow.addNotify();
int borderCornerRadius = getBorderCornerRadius( owner, contents ); // get window handle
float borderWidth = getRoundedBorderWidth( owner, contents ); long hwnd = FlatNativeWindowsLibrary.getHWND( popupWindow );
// get Swing border color // set corner preference
Color borderColor = null; // use system default color int cornerPreference = (borderCornerRadius <= 4)
? FlatNativeWindowsLibrary.DWMWCP_ROUNDSMALL // 4px
: FlatNativeWindowsLibrary.DWMWCP_ROUND; // 8px
FlatNativeWindowsLibrary.setWindowCornerPreference( hwnd, cornerPreference );
// set border color
int red = -1; // use system default color
int green = 0;
int blue = 0;
if( contents instanceof JComponent ) { if( contents instanceof JComponent ) {
Border border = ((JComponent)contents).getBorder(); Border border = ((JComponent)contents).getBorder();
border = FlatUIUtils.unwrapNonUIResourceBorder( border ); border = FlatUIUtils.unwrapNonUIResourceBorder( border );
// get color from border of contents (e.g. JPopupMenu or JToolTip) // get color from border of contents (e.g. JPopupMenu or JToolTip)
Color borderColor = null;
if( border instanceof FlatLineBorder ) if( border instanceof FlatLineBorder )
borderColor = ((FlatLineBorder)border).getLineColor(); borderColor = ((FlatLineBorder)border).getLineColor();
else if( border instanceof LineBorder ) else if( border instanceof LineBorder )
borderColor = ((LineBorder)border).getLineColor(); borderColor = ((LineBorder)border).getLineColor();
else if( border instanceof EmptyBorder ) else if( border instanceof EmptyBorder )
borderColor = FlatNativeWindowsLibrary.COLOR_NONE; // do not paint border red = -2; // do not paint border
// avoid that FlatLineBorder paints the Swing border if( borderColor != null ) {
((JComponent)contents).putClientProperty( KEY_POPUP_USES_NATIVE_BORDER, true ); red = borderColor.getRed();
} green = borderColor.getGreen();
blue = borderColor.getBlue();
if( SystemInfo.isWindows ) { }
// get native window handle
long hwnd = FlatNativeWindowsLibrary.getHWND( popupWindow );
// set corner preference
int cornerPreference = (borderCornerRadius <= 4)
? FlatNativeWindowsLibrary.DWMWCP_ROUNDSMALL // 4px
: FlatNativeWindowsLibrary.DWMWCP_ROUND; // 8px
FlatNativeWindowsLibrary.setWindowCornerPreference( hwnd, cornerPreference );
// set border color
FlatNativeWindowsLibrary.dwmSetWindowAttributeCOLORREF( hwnd, FlatNativeWindowsLibrary.DWMWA_BORDER_COLOR, borderColor );
} else if( SystemInfo.isMacOS ) {
if( borderColor == null || borderColor == FlatNativeWindowsLibrary.COLOR_NONE )
borderWidth = 0;
// set corner radius, border width and color
FlatNativeMacLibrary.setWindowRoundedBorder( popupWindow, borderCornerRadius,
borderWidth, (borderColor != null) ? borderColor.getRGB() : 0 );
} }
FlatNativeWindowsLibrary.setWindowBorderColor( hwnd, red, green, blue );
} }
private static void resetWindows11Border( Window popupWindow ) { private static void resetWindows11Border( Window popupWindow ) {
@@ -377,117 +389,6 @@ public class FlatPopupFactory
FlatNativeWindowsLibrary.setWindowCornerPreference( hwnd, FlatNativeWindowsLibrary.DWMWCP_DONOTROUND ); FlatNativeWindowsLibrary.setWindowCornerPreference( hwnd, FlatNativeWindowsLibrary.DWMWCP_DONOTROUND );
} }
private static int getBorderCornerRadius( Component owner, Component contents ) {
String uiKey =
(contents instanceof BasicComboPopup) ? "ComboBox.borderCornerRadius" :
(contents instanceof JPopupMenu) ? "PopupMenu.borderCornerRadius" :
(contents instanceof JToolTip) ? "ToolTip.borderCornerRadius" :
"Popup.borderCornerRadius";
Object value = getOption( owner, contents, FlatClientProperties.POPUP_BORDER_CORNER_RADIUS, uiKey );
return (value instanceof Integer) ? (Integer) value : 0;
}
private static float getRoundedBorderWidth( Component owner, Component contents ) {
String uiKey =
(contents instanceof BasicComboPopup) ? "ComboBox.roundedBorderWidth" :
(contents instanceof JPopupMenu) ? "PopupMenu.roundedBorderWidth" :
(contents instanceof JToolTip) ? "ToolTip.roundedBorderWidth" :
"Popup.roundedBorderWidth";
Object value = getOption( owner, contents, FlatClientProperties.POPUP_ROUNDED_BORDER_WIDTH, uiKey );
return (value instanceof Number) ? ((Number)value).floatValue() : 0;
}
//---- fixes --------------------------------------------------------------
private static boolean overlapsHeavyWeightComponent( Component owner, Component contents, int x, int y ) {
if( owner == null )
return false;
Window window = SwingUtilities.getWindowAncestor( owner );
if( window == null )
return false;
Rectangle r = new Rectangle( new Point( x, y ), contents.getPreferredSize() );
return overlapsHeavyWeightComponent( window, r );
}
private static boolean overlapsHeavyWeightComponent( Component parent, Rectangle r ) {
if( !parent.isVisible() || !r.intersects( parent.getBounds() ) )
return false;
if( !parent.isLightweight() && !(parent instanceof Window) )
return true;
if( parent instanceof Container ) {
Rectangle r2 = new Rectangle( r.x - parent.getX(), r.y - parent.getY(), r.width, r.height );
for( Component c : ((Container)parent).getComponents() ) {
if( overlapsHeavyWeightComponent( c, r2 ) )
return true;
}
}
return false;
}
/**
* On Linux with Wayland, since Java 21, Swing adds a window focus listener to popup owner/invoker window,
* which hides the popup as soon as the owner/invoker window looses focus.
* This works fine for light-weight popups.
* It also works for heavy-weight popups if they do not request focus.
* Because FlatLaf always uses heavy-weight popups, all popups that request focus
* are broken since Java 21.
*
* This method removes the problematic window focus listener.
*
* https://bugs.openjdk.org/browse/JDK-8280993
* https://github.com/openjdk/jdk/pull/13830
*/
private static void fixLinuxWaylandJava21focusIssue( Component owner ) {
// only necessary on Linux when running in Java 21+
if( owner == null || !SystemInfo.isLinux || SystemInfo.javaVersion < SystemInfo.toVersion( 21, 0, 0, 0 ) )
return;
// get window
Window window = SwingUtilities.getWindowAncestor( owner );
if( window == null )
return;
// remove window focus listener, which was added from class sun.awt.UNIXToolkit since Java 21
for( WindowFocusListener l : window.getWindowFocusListeners() ) {
if( "sun.awt.UNIXToolkit$1".equals( l.getClass().getName() ) ) {
window.removeWindowFocusListener( l );
break;
}
}
}
/**
* Shows the given popup and, if necessary, fixes the location of a heavy weight popup window.
* <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 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
*/
private static void showPopupAndFixLocation( Popup popup, Window popupWindow ) {
if( popupWindow != null ) {
// remember location of heavy weight popup window
int x = popupWindow.getX();
int y = popupWindow.getY();
popup.show();
// restore popup window location if it has changed
// (probably scaled when screens use different scale factors)
if( popupWindow.getX() != x || popupWindow.getY() != y )
popupWindow.setLocation( x, y );
} else
popup.show();
}
//---- class NonFlashingPopup --------------------------------------------- //---- class NonFlashingPopup ---------------------------------------------
private static class NonFlashingPopup private static class NonFlashingPopup
@@ -541,9 +442,6 @@ public class FlatPopupFactory
@Override @Override
public void hide() { public void hide() {
if( contents instanceof JComponent )
((JComponent)contents).putClientProperty( KEY_POPUP_USES_NATIVE_BORDER, null );
if( delegate != null ) { if( delegate != null ) {
delegate.hide(); delegate.hide();
delegate = null; delegate = null;

View File

@@ -35,7 +35,6 @@ import javax.swing.CellRendererPane;
import javax.swing.Icon; import javax.swing.Icon;
import javax.swing.JComponent; import javax.swing.JComponent;
import javax.swing.LookAndFeel; import javax.swing.LookAndFeel;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities; import javax.swing.SwingUtilities;
import javax.swing.UIManager; import javax.swing.UIManager;
import javax.swing.plaf.ComponentUI; import javax.swing.plaf.ComponentUI;
@@ -263,7 +262,7 @@ public class FlatRadioButtonUI
@Override @Override
public void paint( Graphics g, JComponent c ) { public void paint( Graphics g, JComponent c ) {
// fill background even if not opaque and if: // fill background even if not opaque if
// - contentAreaFilled is true and // - contentAreaFilled is true and
// - if background color is different to default background color // - if background color is different to default background color
// (this paints selection if using the component as cell renderer) // (this paints selection if using the component as cell renderer)
@@ -279,27 +278,20 @@ public class FlatRadioButtonUI
int focusWidth = getIconFocusWidth( c ); int focusWidth = getIconFocusWidth( c );
if( focusWidth > 0 ) { if( focusWidth > 0 ) {
boolean ltr = c.getComponentOrientation().isLeftToRight(); boolean ltr = c.getComponentOrientation().isLeftToRight();
int halign = ((AbstractButton)c).getHorizontalAlignment();
if( halign == SwingConstants.LEADING )
halign = ltr ? SwingConstants.LEFT : SwingConstants.RIGHT;
else if( halign == SwingConstants.TRAILING )
halign = ltr ? SwingConstants.RIGHT : SwingConstants.LEFT;
Insets insets = c.getInsets( tempInsets ); Insets insets = c.getInsets( tempInsets );
if( (focusWidth > insets.left || focusWidth > insets.right) && int leftOrRightInset = ltr ? insets.left : insets.right;
(halign == SwingConstants.LEFT || halign == SwingConstants.RIGHT) ) if( focusWidth > leftOrRightInset ) {
{
// The left (or right) inset is smaller than the focus width, which may be // The left (or right) inset is smaller than the focus width, which may be
// the case if insets were explicitly reduced (e.g. with an EmptyBorder). // the case if insets were explicitly reduced (e.g. with an EmptyBorder).
// In this case the width has been increased in getPreferredSize() and // In this case the width has been increased in getPreferredSize() and
// here it is necessary to fix icon and text painting location. // here it is necessary to fix icon and text painting location.
int offset = (halign == SwingConstants.LEFT) int offset = focusWidth - leftOrRightInset;
? Math.max( focusWidth - insets.left, 0 ) if( !ltr )
: -Math.max( focusWidth - insets.right, 0 ); offset = -offset;
// move the graphics origin to the left (or right) // move the graphics origin to the left (or right)
g.translate( offset, 0 ); g.translate( offset, 0 );
super.paint( FlatLabelUI.createGraphicsHTMLTextYCorrection( g, c ), c ); super.paint( g, c );
g.translate( -offset, 0 ); g.translate( -offset, 0 );
return; return;
} }
@@ -336,11 +328,6 @@ public class FlatRadioButtonUI
: 0; : 0;
} }
@Override
public int getBaseline( JComponent c, int width, int height ) {
return FlatButtonUI.getBaselineImpl( c, width, height );
}
//---- class FlatRadioButtonListener -------------------------------------- //---- class FlatRadioButtonListener --------------------------------------
/** @since 2 */ /** @since 2 */

View File

@@ -50,6 +50,7 @@ import javax.swing.plaf.RootPaneUI;
import javax.swing.plaf.UIResource; import javax.swing.plaf.UIResource;
import javax.swing.plaf.basic.BasicRootPaneUI; import javax.swing.plaf.basic.BasicRootPaneUI;
import com.formdev.flatlaf.FlatClientProperties; import com.formdev.flatlaf.FlatClientProperties;
import com.formdev.flatlaf.FlatLaf;
import com.formdev.flatlaf.util.HiDPIUtils; import com.formdev.flatlaf.util.HiDPIUtils;
import com.formdev.flatlaf.util.SystemInfo; import com.formdev.flatlaf.util.SystemInfo;
import com.formdev.flatlaf.util.UIScale; import com.formdev.flatlaf.util.UIScale;
@@ -155,6 +156,10 @@ public class FlatRootPaneUI
if( background == null || background instanceof UIResource ) if( background == null || background instanceof UIResource )
parent.setBackground( UIManager.getColor( "control" ) ); parent.setBackground( UIManager.getColor( "control" ) );
} }
// enable dark window appearance on macOS when running in JetBrains Runtime
if( SystemInfo.isJetBrainsJVM && SystemInfo.isMacOS_10_14_Mojave_orLater )
c.putClientProperty( "jetbrains.awt.windowDarkAppearance", FlatLaf.isLafDark() );
} }
@Override @Override
@@ -494,7 +499,7 @@ public class FlatRootPaneUI
@Override @Override
public void invalidateLayout( Container parent ) { public void invalidateLayout( Container parent ) {
if( titlePane != null ) if( titlePane != null )
titlePane.menuBarInvalidate(); titlePane.menuBarChanged();
} }
@Override @Override

View File

@@ -22,6 +22,7 @@ import java.awt.Dimension;
import java.awt.Graphics; import java.awt.Graphics;
import java.awt.Insets; import java.awt.Insets;
import java.awt.Rectangle; import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.MouseAdapter; import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent; import java.awt.event.MouseEvent;
import java.beans.PropertyChangeListener; import java.beans.PropertyChangeListener;
@@ -39,10 +40,13 @@ import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.basic.BasicScrollBarUI; import javax.swing.plaf.basic.BasicScrollBarUI;
import com.formdev.flatlaf.FlatClientProperties; import com.formdev.flatlaf.FlatClientProperties;
import com.formdev.flatlaf.FlatLaf; import com.formdev.flatlaf.FlatLaf;
import com.formdev.flatlaf.FlatSystemProperties;
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable; import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableField; import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableField;
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableLookupProvider; import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableLookupProvider;
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI; import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
import com.formdev.flatlaf.util.Animator;
import com.formdev.flatlaf.util.CubicBezierEasing;
import com.formdev.flatlaf.util.LoggingFacade; import com.formdev.flatlaf.util.LoggingFacade;
import com.formdev.flatlaf.util.SystemInfo; import com.formdev.flatlaf.util.SystemInfo;
import com.formdev.flatlaf.util.UIScale; import com.formdev.flatlaf.util.UIScale;
@@ -203,6 +207,16 @@ public class FlatScrollBarUI
oldStyleValues = null; oldStyleValues = null;
} }
@Override
protected TrackListener createTrackListener() {
return new FlatTrackListener();
}
@Override
protected ScrollListener createScrollListener() {
return new FlatScrollListener();
}
@Override @Override
protected PropertyChangeListener createPropertyChangeListener() { protected PropertyChangeListener createPropertyChangeListener() {
PropertyChangeListener superListener = super.createPropertyChangeListener(); PropertyChangeListener superListener = super.createPropertyChangeListener();
@@ -245,7 +259,7 @@ public class FlatScrollBarUI
// because scroll bars do not receive mouse exited event. // because scroll bars do not receive mouse exited event.
// The scroll pane, including its scroll bars, is not part // The scroll pane, including its scroll bars, is not part
// of the component hierarchy and does not receive mouse events // of the component hierarchy and does not receive mouse events
// directly. Instead, LWComponentPeer receives mouse events // directly. Instead LWComponentPeer receives mouse events
// and delegates them to peers, but entered/exited events // and delegates them to peers, but entered/exited events
// are sent only for the whole scroll pane. // are sent only for the whole scroll pane.
// Exited event is only sent when mouse leaves scroll pane. // Exited event is only sent when mouse leaves scroll pane.
@@ -431,6 +445,197 @@ public class FlatScrollBarUI
return allowsAbsolutePositioning; return allowsAbsolutePositioning;
} }
@Override
protected void scrollByBlock( int direction ) {
runAndSetValueAnimated( () -> {
super.scrollByBlock( direction );
} );
}
@Override
protected void scrollByUnit( int direction ) {
runAndSetValueAnimated( () -> {
super.scrollByUnit( direction );
} );
}
/**
* Runs the given runnable, which should modify the scroll bar value,
* and then animate scroll bar value from old value to new value.
*/
public void runAndSetValueAnimated( Runnable r ) {
if( inRunAndSetValueAnimated || !isSmoothScrollingEnabled() ) {
r.run();
return;
}
inRunAndSetValueAnimated = true;
if( animator != null )
animator.cancel();
if( useValueIsAdjusting )
scrollbar.setValueIsAdjusting( true );
// remember current scrollbar value so that we can start scroll animation from there
int oldValue = scrollbar.getValue();
// run given runnable, which computes and sets the new scrollbar value
FlatScrollPaneUI.runWithoutBlitting( scrollbar.getParent(), () ->{
// if invoked while animation is running, calculation of new value
// should start at the previous target value
if( targetValue != Integer.MIN_VALUE )
scrollbar.setValue( targetValue );
r.run();
} );
// do not use animation if started dragging thumb
if( isDragging ) {
// do not clear valueIsAdjusting here
inRunAndSetValueAnimated = false;
return;
}
int newValue = scrollbar.getValue();
if( newValue != oldValue ) {
// start scroll animation if value has changed
setValueAnimated( oldValue, newValue );
} else {
// clear valueIsAdjusting if value has not changed
if( useValueIsAdjusting )
scrollbar.setValueIsAdjusting( false );
}
inRunAndSetValueAnimated = false;
}
private boolean inRunAndSetValueAnimated;
private Animator animator;
private int startValue = Integer.MIN_VALUE;
private int targetValue = Integer.MIN_VALUE;
private boolean useValueIsAdjusting = true;
int getTargetValue() {
return targetValue;
}
public void setValueAnimated( int initialValue, int value ) {
if( useValueIsAdjusting )
scrollbar.setValueIsAdjusting( true );
// (always) set scrollbar value to initial value
scrollbar.setValue( initialValue );
// do some check if animation already running
if( animator != null && animator.isRunning() && targetValue != Integer.MIN_VALUE ) {
// Ignore requests if animation still running and scroll direction is the same
// and new value is within currently running animation.
// Without this check, repeating-scrolling via keyboard would become
// very slow when reaching the top/bottom/left/right of the viewport,
// because it would start a new 200ms animation to scroll a few pixels.
if( value == targetValue ||
(value > startValue && value < targetValue) || // scroll down/right
(value < startValue && value > targetValue) ) // scroll up/left
return;
}
startValue = initialValue;
targetValue = value;
// create animator
if( animator == null ) {
int duration = FlatUIUtils.getUIInt( "ScrollPane.smoothScrolling.duration", 200 );
int resolution = FlatUIUtils.getUIInt( "ScrollPane.smoothScrolling.resolution", 10 );
Object interpolator = UIManager.get( "ScrollPane.smoothScrolling.interpolator" );
animator = new Animator( duration, fraction -> {
if( scrollbar == null || !scrollbar.isShowing() ) {
animator.stop();
return;
}
// re-enable valueIsAdjusting if disabled while animation is running
// (e.g. in mouse released listener)
if( useValueIsAdjusting && !scrollbar.getValueIsAdjusting() )
scrollbar.setValueIsAdjusting( true );
scrollbar.setValue( startValue + Math.round( (targetValue - startValue) * fraction ) );
}, () -> {
startValue = targetValue = Integer.MIN_VALUE;
if( useValueIsAdjusting && scrollbar != null )
scrollbar.setValueIsAdjusting( false );
});
animator.setResolution( resolution );
animator.setInterpolator( (interpolator instanceof Animator.Interpolator)
? (Animator.Interpolator) interpolator
: new CubicBezierEasing( 0.5f, 0.5f, 0.5f, 1 ) );
}
// restart animator
animator.cancel();
animator.start();
}
protected boolean isSmoothScrollingEnabled() {
if( !Animator.useAnimation() || !FlatSystemProperties.getBoolean( FlatSystemProperties.SMOOTH_SCROLLING, true ) )
return false;
// if scroll bar is child of scroll pane, check only client property of scroll pane
Container parent = scrollbar.getParent();
JComponent c = (parent instanceof JScrollPane) ? (JScrollPane) parent : scrollbar;
Object smoothScrolling = c.getClientProperty( FlatClientProperties.SCROLL_PANE_SMOOTH_SCROLLING );
if( smoothScrolling instanceof Boolean )
return (Boolean) smoothScrolling;
// Note: Getting UI value "ScrollPane.smoothScrolling" here to allow
// applications to turn smooth scrolling on or off at any time
// (e.g. in application options dialog).
return UIManager.getBoolean( "ScrollPane.smoothScrolling" );
}
//---- class FlatTrackListener --------------------------------------------
protected class FlatTrackListener
extends TrackListener
{
@Override
public void mousePressed( MouseEvent e ) {
// Do not use valueIsAdjusting here (in runAndSetValueAnimated())
// for smooth scrolling because super.mousePressed() enables this itself
// and super.mouseRelease() disables it later.
// If we would disable valueIsAdjusting here (in runAndSetValueAnimated())
// and move the thumb with the mouse, then the thumb location is not updated
// if later scrolled with a key (e.g. HOME key).
useValueIsAdjusting = false;
runAndSetValueAnimated( () -> {
super.mousePressed( e );
} );
}
@Override
public void mouseReleased( MouseEvent e ) {
super.mouseReleased( e );
useValueIsAdjusting = true;
}
}
//---- class FlatScrollListener -------------------------------------------
protected class FlatScrollListener
extends ScrollListener
{
@Override
public void actionPerformed( ActionEvent e ) {
runAndSetValueAnimated( () -> {
super.actionPerformed( e );
} );
}
}
//---- class ScrollBarHoverListener --------------------------------------- //---- class ScrollBarHoverListener ---------------------------------------
// using static field to disabling hover for other scroll bars // using static field to disabling hover for other scroll bars

View File

@@ -1,118 +0,0 @@
/*
* Copyright 2023 FormDev Software GmbH
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.formdev.flatlaf.ui;
import java.awt.Component;
import java.awt.Insets;
import javax.swing.JList;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTree;
import javax.swing.UIManager;
import javax.swing.text.JTextComponent;
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
import com.formdev.flatlaf.util.UIScale;
/**
* Border for {@link javax.swing.JScrollPane}.
*
* @uiDefault ScrollPane.arc int
* @uiDefault ScrollPane.List.arc int
* @uiDefault ScrollPane.Table.arc int
* @uiDefault ScrollPane.TextComponent.arc int
* @uiDefault ScrollPane.Tree.arc int
* @author Karl Tauber
* @since 3.3
*/
public class FlatScrollPaneBorder
extends FlatBorder
{
@Styleable protected int arc = UIManager.getInt( "ScrollPane.arc" );
private boolean isArcStyled;
private final int listArc = FlatUIUtils.getUIInt( "ScrollPane.List.arc", -1 );
private final int tableArc = FlatUIUtils.getUIInt( "ScrollPane.Table.arc", -1 );
private final int textComponentArc = FlatUIUtils.getUIInt( "ScrollPane.TextComponent.arc", -1 );
private final int treeArc = FlatUIUtils.getUIInt( "ScrollPane.Tree.arc", -1 );
@Override
public Object applyStyleProperty( String key, Object value ) {
Object oldValue = super.applyStyleProperty( key, value );
if( "arc".equals( key ) )
isArcStyled = true;
return oldValue;
}
@Override
public Insets getBorderInsets( Component c, Insets insets ) {
insets = super.getBorderInsets( c, insets );
// if view is rounded, increase left and right insets to avoid that the viewport
// is painted over the rounded border on the corners
int padding = getLeftRightPadding( c );
if( padding > 0 ) {
insets.left += padding;
insets.right += padding;
}
return insets;
}
@Override
protected int getArc( Component c ) {
if( isCellEditor( c ) )
return 0;
if( isArcStyled )
return arc;
if( c instanceof JScrollPane ) {
Component view = FlatScrollPaneUI.getView( (JScrollPane) c );
if( listArc >= 0 && view instanceof JList )
return listArc;
if( tableArc >= 0 && view instanceof JTable )
return tableArc;
if( textComponentArc >= 0&& view instanceof JTextComponent )
return textComponentArc;
if( treeArc >= 0 && view instanceof JTree )
return treeArc;
}
return arc;
}
/**
* Returns the scaled left/right padding used when arc is larger than zero.
* <p>
* This is the distance from the inside of the left border to the left side of the view component.
* On the right side, this is the distance between the right side of the view component and
* the vertical scrollbar. Or the inside of the right border if the scrollbar is hidden.
*/
public int getLeftRightPadding( Component c ) {
// Subtract lineWidth from radius because radius is given for the outside
// of the painted line, but insets from super already include lineWidth.
// Reduce padding by 10% to make padding slightly smaller because it is not recognizable
// when the view is minimally painted over the beginning of the border curve.
int arc = getArc( c );
return (arc > 0)
? Math.max( Math.round( UIScale.scale( ((arc / 2f) - getLineWidth( c )) * 0.9f ) ), 0 )
: 0;
}
}

View File

@@ -19,11 +19,10 @@ package com.formdev.flatlaf.ui;
import java.awt.Component; import java.awt.Component;
import java.awt.Container; import java.awt.Container;
import java.awt.Graphics; import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Insets; import java.awt.Insets;
import java.awt.KeyboardFocusManager; import java.awt.KeyboardFocusManager;
import java.awt.LayoutManager;
import java.awt.Rectangle; import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ContainerEvent; import java.awt.event.ContainerEvent;
import java.awt.event.ContainerListener; import java.awt.event.ContainerListener;
import java.awt.event.FocusEvent; import java.awt.event.FocusEvent;
@@ -34,6 +33,8 @@ import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener; import java.beans.PropertyChangeListener;
import java.util.Map; import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import javax.swing.Action;
import javax.swing.ActionMap;
import javax.swing.BorderFactory; import javax.swing.BorderFactory;
import javax.swing.JButton; import javax.swing.JButton;
import javax.swing.JComponent; import javax.swing.JComponent;
@@ -44,19 +45,18 @@ import javax.swing.JTree;
import javax.swing.JViewport; import javax.swing.JViewport;
import javax.swing.LookAndFeel; import javax.swing.LookAndFeel;
import javax.swing.ScrollPaneConstants; import javax.swing.ScrollPaneConstants;
import javax.swing.ScrollPaneLayout;
import javax.swing.Scrollable; import javax.swing.Scrollable;
import javax.swing.SwingConstants; import javax.swing.SwingConstants;
import javax.swing.SwingUtilities; import javax.swing.SwingUtilities;
import javax.swing.UIManager; import javax.swing.UIManager;
import javax.swing.border.Border;
import javax.swing.plaf.ComponentUI; import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.basic.BasicScrollPaneUI; import javax.swing.plaf.basic.BasicScrollPaneUI;
import com.formdev.flatlaf.FlatClientProperties; import com.formdev.flatlaf.FlatClientProperties;
import com.formdev.flatlaf.FlatSystemProperties;
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable; import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI; import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
import com.formdev.flatlaf.util.Animator;
import com.formdev.flatlaf.util.LoggingFacade; import com.formdev.flatlaf.util.LoggingFacade;
import com.formdev.flatlaf.util.UIScale;
/** /**
* Provides the Flat LaF UI delegate for {@link javax.swing.JScrollPane}. * Provides the Flat LaF UI delegate for {@link javax.swing.JScrollPane}.
@@ -103,13 +103,7 @@ public class FlatScrollPaneUI
super.installUI( c ); super.installUI( c );
int focusWidth = UIManager.getInt( "Component.focusWidth" ); int focusWidth = UIManager.getInt( "Component.focusWidth" );
int arc = UIManager.getInt( "ScrollPane.arc" ); LookAndFeel.installProperty( c, "opaque", focusWidth == 0 );
LookAndFeel.installProperty( c, "opaque", focusWidth == 0 && arc == 0 );
// install layout manager
LayoutManager layout = c.getLayout();
if( layout != null && layout.getClass() == ScrollPaneLayout.UIResource.class )
c.setLayout( createScrollPaneLayout() );
installStyle(); installStyle();
@@ -120,10 +114,6 @@ public class FlatScrollPaneUI
public void uninstallUI( JComponent c ) { public void uninstallUI( JComponent c ) {
MigLayoutVisualPadding.uninstall( scrollpane ); MigLayoutVisualPadding.uninstall( scrollpane );
// uninstall layout manager
if( c.getLayout() instanceof FlatScrollPaneLayout )
c.setLayout( new ScrollPaneLayout.UIResource() );
super.uninstallUI( c ); super.uninstallUI( c );
oldStyleValues = null; oldStyleValues = null;
@@ -146,30 +136,38 @@ public class FlatScrollPaneUI
handler = null; handler = null;
} }
/**
* @since 3.3
*/
protected FlatScrollPaneLayout createScrollPaneLayout() {
return new FlatScrollPaneLayout();
}
@Override @Override
protected MouseWheelListener createMouseWheelListener() { protected MouseWheelListener createMouseWheelListener() {
MouseWheelListener superListener = super.createMouseWheelListener(); MouseWheelListener superListener = super.createMouseWheelListener();
return e -> { return e -> {
if( isSmoothScrollingEnabled() && if( isSmoothScrollingEnabled() &&
scrollpane.isWheelScrollingEnabled() && scrollpane.isWheelScrollingEnabled() )
e.getScrollType() == MouseWheelEvent.WHEEL_UNIT_SCROLL &&
e.getPreciseWheelRotation() != 0 &&
e.getPreciseWheelRotation() != e.getWheelRotation() )
{ {
mouseWheelMovedSmooth( e ); if( e.getScrollType() == MouseWheelEvent.WHEEL_UNIT_SCROLL &&
isPreciseWheelEvent( e ) )
{
// precise scrolling
mouseWheelMovedPrecise( e );
} else {
// smooth scrolling
JScrollBar scrollBar = findScrollBarToScroll( e );
if( scrollBar != null && scrollBar.getUI() instanceof FlatScrollBarUI ) {
FlatScrollBarUI ui = (FlatScrollBarUI) scrollBar.getUI();
ui.runAndSetValueAnimated( () -> {
superListener.mouseWheelMoved( e );
} );
} else
superListener.mouseWheelMoved( e );
}
} else } else
superListener.mouseWheelMoved( e ); superListener.mouseWheelMoved( e );
}; };
} }
protected boolean isSmoothScrollingEnabled() { protected boolean isSmoothScrollingEnabled() {
if( !Animator.useAnimation() || !FlatSystemProperties.getBoolean( FlatSystemProperties.SMOOTH_SCROLLING, true ) )
return false;
Object smoothScrolling = scrollpane.getClientProperty( FlatClientProperties.SCROLL_PANE_SMOOTH_SCROLLING ); Object smoothScrolling = scrollpane.getClientProperty( FlatClientProperties.SCROLL_PANE_SMOOTH_SCROLLING );
if( smoothScrolling instanceof Boolean ) if( smoothScrolling instanceof Boolean )
return (Boolean) smoothScrolling; return (Boolean) smoothScrolling;
@@ -180,19 +178,40 @@ public class FlatScrollPaneUI
return UIManager.getBoolean( "ScrollPane.smoothScrolling" ); return UIManager.getBoolean( "ScrollPane.smoothScrolling" );
} }
private void mouseWheelMovedSmooth( MouseWheelEvent e ) { private long lastPreciseWheelWhen;
private boolean isPreciseWheelEvent( MouseWheelEvent e ) {
double preciseWheelRotation = e.getPreciseWheelRotation();
if( preciseWheelRotation != 0 && preciseWheelRotation != e.getWheelRotation() ) {
// precise wheel event
lastPreciseWheelWhen = e.getWhen();
return true;
}
// If a non-precise wheel event occurs shortly after a precise wheel event,
// then it is probably still a precise wheel but the precise value
// is by chance an integer value (e.g. 1.0 or 2.0).
// Not handling this special case, would start an animation for smooth scrolling,
// which would be interrupted soon when the next precise wheel event occurs.
// This would result in jittery scrolling. E.g. on a MacBook using Trackpad or Magic Mouse.
if( e.getWhen() - lastPreciseWheelWhen < 1000 )
return true;
// non-precise wheel event
lastPreciseWheelWhen = 0;
return false;
}
private void mouseWheelMovedPrecise( MouseWheelEvent e ) {
// return if there is no viewport // return if there is no viewport
JViewport viewport = scrollpane.getViewport(); JViewport viewport = scrollpane.getViewport();
if( viewport == null ) if( viewport == null )
return; return;
// find scrollbar to scroll // find scrollbar to scroll
JScrollBar scrollbar = scrollpane.getVerticalScrollBar(); JScrollBar scrollbar = findScrollBarToScroll( e );
if( scrollbar == null || !scrollbar.isVisible() || e.isShiftDown() ) { if( scrollbar == null )
scrollbar = scrollpane.getHorizontalScrollBar(); return;
if( scrollbar == null || !scrollbar.isVisible() )
return;
}
// consume event // consume event
e.consume(); e.consume();
@@ -285,6 +304,16 @@ public class FlatScrollPaneUI
*/ */
} }
private JScrollBar findScrollBarToScroll( MouseWheelEvent e ) {
JScrollBar scrollBar = scrollpane.getVerticalScrollBar();
if( scrollBar == null || !scrollBar.isVisible() || e.isShiftDown() ) {
scrollBar = scrollpane.getHorizontalScrollBar();
if( scrollBar == null || !scrollBar.isVisible() )
return null;
}
return scrollBar;
}
@Override @Override
protected PropertyChangeListener createPropertyChangeListener() { protected PropertyChangeListener createPropertyChangeListener() {
PropertyChangeListener superListener = super.createPropertyChangeListener(); PropertyChangeListener superListener = super.createPropertyChangeListener();
@@ -313,7 +342,8 @@ public class FlatScrollPaneUI
Object corner = e.getNewValue(); Object corner = e.getNewValue();
if( corner instanceof JButton && if( corner instanceof JButton &&
((JButton)corner).getBorder() instanceof FlatButtonBorder && ((JButton)corner).getBorder() instanceof FlatButtonBorder &&
getView( scrollpane ) instanceof JTable ) scrollpane.getViewport() != null &&
scrollpane.getViewport().getView() instanceof JTable )
{ {
((JButton)corner).setBorder( BorderFactory.createEmptyBorder() ); ((JButton)corner).setBorder( BorderFactory.createEmptyBorder() );
((JButton)corner).setFocusable( false ); ((JButton)corner).setFocusable( false );
@@ -330,18 +360,6 @@ public class FlatScrollPaneUI
scrollpane.revalidate(); scrollpane.revalidate();
scrollpane.repaint(); scrollpane.repaint();
break; break;
case "border":
Object newBorder = e.getNewValue();
if( newBorder != null && newBorder == UIManager.getBorder( "Table.scrollPaneBorder" ) ) {
// JTable.configureEnclosingScrollPaneUI() replaces the scrollpane border
// with another one --> re-apply style on new border
borderShared = null;
installStyle();
scrollpane.revalidate();
scrollpane.repaint();
}
break;
} }
}; };
} }
@@ -368,10 +386,9 @@ public class FlatScrollPaneUI
/** @since 2 */ /** @since 2 */
protected Object applyStyleProperty( String key, Object value ) { protected Object applyStyleProperty( String key, Object value ) {
if( key.equals( "focusWidth" ) || key.equals( "arc" ) ) { if( key.equals( "focusWidth" ) ) {
int focusWidth = (value instanceof Integer) ? (int) value : UIManager.getInt( "Component.focusWidth" ); int focusWidth = (value instanceof Integer) ? (int) value : UIManager.getInt( "Component.focusWidth" );
int arc = (value instanceof Integer) ? (int) value : UIManager.getInt( "ScrollPane.arc" ); LookAndFeel.installProperty( scrollpane, "opaque", focusWidth == 0 );
LookAndFeel.installProperty( scrollpane, "opaque", focusWidth == 0 && arc == 0 );
} }
if( borderShared == null ) if( borderShared == null )
@@ -437,46 +454,13 @@ public class FlatScrollPaneUI
c.getHeight() - insets.top - insets.bottom ); c.getHeight() - insets.top - insets.bottom );
} }
// if view is rounded, paint rounded background with view background color
// to ensure that free areas at left and right have same color as view
Component view;
float arc = getBorderArc( scrollpane );
if( arc > 0 && (view = getView( scrollpane )) != null ) {
float focusWidth = FlatUIUtils.getBorderFocusWidth( c );
g.setColor( view.getBackground() );
Object[] oldRenderingHints = FlatUIUtils.setRenderingHints( g );
FlatUIUtils.paintComponentBackground( (Graphics2D) g, 0, 0, c.getWidth(), c.getHeight(), focusWidth, arc );
FlatUIUtils.resetRenderingHints( g, oldRenderingHints );
}
paint( g, c ); paint( g, c );
} }
@Override
public void paint( Graphics g, JComponent c ) {
Border viewportBorder = scrollpane.getViewportBorder();
if( viewportBorder != null ) {
Rectangle r = scrollpane.getViewportBorderBounds();
int padding = getBorderLeftRightPadding( scrollpane );
JScrollBar vsb = scrollpane.getVerticalScrollBar();
if( padding > 0 &&
vsb != null && vsb.isVisible() &&
scrollpane.getLayout() instanceof FlatScrollPaneLayout &&
((FlatScrollPaneLayout)scrollpane.getLayout()).canIncreaseViewportWidth( scrollpane ) )
{
boolean ltr = scrollpane.getComponentOrientation().isLeftToRight();
int extraWidth = Math.min( padding, vsb.getWidth() );
viewportBorder.paintBorder( scrollpane, g, r.x - (ltr ? 0 : extraWidth), r.y, r.width + extraWidth, r.height );
} else
viewportBorder.paintBorder( scrollpane, g, r.x, r.y, r.width, r.height );
}
}
/** @since 1.3 */ /** @since 1.3 */
public static boolean isPermanentFocusOwner( JScrollPane scrollPane ) { public static boolean isPermanentFocusOwner( JScrollPane scrollPane ) {
Component view = getView( scrollPane ); JViewport viewport = scrollPane.getViewport();
Component view = (viewport != null) ? viewport.getView() : null;
if( view == null ) if( view == null )
return false; return false;
@@ -496,23 +480,117 @@ public class FlatScrollPaneUI
return false; return false;
} }
static Component getView( JScrollPane scrollPane ) { @Override
JViewport viewport = scrollPane.getViewport(); protected void syncScrollPaneWithViewport() {
return (viewport != null) ? viewport.getView() : null; // if the viewport has been scrolled by using JComponent.scrollRectToVisible()
// (e.g. by moving selection), then it is necessary to update the scroll bar values
if( isSmoothScrollingEnabled() ) {
runAndSyncScrollBarValueAnimated( scrollpane.getVerticalScrollBar(), 0, false, () -> {
runAndSyncScrollBarValueAnimated( scrollpane.getHorizontalScrollBar(), 1, false, () -> {
super.syncScrollPaneWithViewport();
} );
} );
} else
super.syncScrollPaneWithViewport();
} }
private static float getBorderArc( JScrollPane scrollPane ) { /**
Border border = scrollPane.getBorder(); * Runs the given runnable, if smooth scrolling is enabled, with disabled
return (border instanceof FlatScrollPaneBorder) * viewport blitting mode and with scroll bar value set to "target" value.
? UIScale.scale( (float) ((FlatScrollPaneBorder)border).getArc( scrollPane ) ) * This is necessary when calculating new view position during animation.
: 0; * Otherwise calculation would use wrong view position and (repeating) scrolling
* would be much slower than without smooth scrolling.
*/
private void runWithScrollBarsTargetValues( boolean blittingOnly, Runnable r ) {
if( isSmoothScrollingEnabled() ) {
runWithoutBlitting( scrollpane, () -> {
if( blittingOnly )
r.run();
else {
runAndSyncScrollBarValueAnimated( scrollpane.getVerticalScrollBar(), 0, true, () -> {
runAndSyncScrollBarValueAnimated( scrollpane.getHorizontalScrollBar(), 1, true, r );
} );
}
} );
} else
r.run();
} }
private static int getBorderLeftRightPadding( JScrollPane scrollPane ) { private void runAndSyncScrollBarValueAnimated( JScrollBar sb, int i, boolean useTargetValue, Runnable r ) {
Border border = scrollPane.getBorder(); if( inRunAndSyncValueAnimated[i] || sb == null || !(sb.getUI() instanceof FlatScrollBarUI) ) {
return (border instanceof FlatScrollPaneBorder) r.run();
? ((FlatScrollPaneBorder)border).getLeftRightPadding( scrollPane ) return;
: 0; }
inRunAndSyncValueAnimated[i] = true;
int oldValue = sb.getValue();
int oldVisibleAmount = sb.getVisibleAmount();
int oldMinimum = sb.getMinimum();
int oldMaximum = sb.getMaximum();
FlatScrollBarUI ui = (FlatScrollBarUI) sb.getUI();
if( useTargetValue && ui.getTargetValue() != Integer.MIN_VALUE )
sb.setValue( ui.getTargetValue() );
r.run();
int newValue = sb.getValue();
if( newValue != oldValue &&
sb.getVisibleAmount() == oldVisibleAmount &&
sb.getMinimum() == oldMinimum &&
sb.getMaximum() == oldMaximum &&
sb.getUI() instanceof FlatScrollBarUI )
{
ui.setValueAnimated( oldValue, newValue );
}
inRunAndSyncValueAnimated[i] = false;
}
private final boolean[] inRunAndSyncValueAnimated = new boolean[2];
/**
* Runs the given runnable with disabled viewport blitting mode.
* If blitting mode is enabled, the viewport immediately repaints parts of the
* view if the view position is changed via JViewport.setViewPosition().
* This causes scrolling artifacts if smooth scrolling is enabled and the view position
* is "temporary" changed to its new target position, changed back to its old position
* and again moved animated to the target position.
*/
static void runWithoutBlitting( Container scrollPane, Runnable r ) {
// prevent the viewport to immediately repaint using blitting
JViewport viewport = (scrollPane instanceof JScrollPane) ? ((JScrollPane)scrollPane).getViewport() : null;
boolean isBlitScrollMode = (viewport != null) ? viewport.getScrollMode() == JViewport.BLIT_SCROLL_MODE : false;
if( isBlitScrollMode )
viewport.setScrollMode( JViewport.SIMPLE_SCROLL_MODE );
try {
r.run();
} finally {
if( isBlitScrollMode )
viewport.setScrollMode( JViewport.BLIT_SCROLL_MODE );
}
}
public static void installSmoothScrollingDelegateActions( JComponent c, boolean blittingOnly, String... actionKeys ) {
// get shared action map, used for all components of same type
ActionMap map = SwingUtilities.getUIActionMap( c );
if( map == null )
return;
// install actions, but only if not already installed
for( String actionKey : actionKeys )
installSmoothScrollingDelegateAction( map, blittingOnly, actionKey );
}
private static void installSmoothScrollingDelegateAction( ActionMap map, boolean blittingOnly, String actionKey ) {
Action oldAction = map.get( actionKey );
if( oldAction == null || oldAction instanceof SmoothScrollingDelegateAction )
return; // not found or already installed
map.put( actionKey, new SmoothScrollingDelegateAction( oldAction, blittingOnly ) );
} }
//---- class Handler ------------------------------------------------------ //---- class Handler ------------------------------------------------------
@@ -537,71 +615,43 @@ public class FlatScrollPaneUI
@Override @Override
public void focusGained( FocusEvent e ) { public void focusGained( FocusEvent e ) {
// necessary to update focus border // necessary to update focus border
if( scrollpane.getBorder() instanceof FlatBorder ) scrollpane.repaint();
scrollpane.repaint();
} }
@Override @Override
public void focusLost( FocusEvent e ) { public void focusLost( FocusEvent e ) {
// necessary to update focus border // necessary to update focus border
if( scrollpane.getBorder() instanceof FlatBorder ) scrollpane.repaint();
scrollpane.repaint();
} }
} }
//---- class FlatScrollPaneLayout ----------------------------------------- //---- class SmoothScrollingDelegateAction --------------------------------
/** /**
* @since 3.3 * Used to run component actions with disabled blitting mode and
* with scroll bar target values.
*/ */
protected static class FlatScrollPaneLayout private static class SmoothScrollingDelegateAction
extends ScrollPaneLayout.UIResource extends FlatUIAction
{ {
private final boolean blittingOnly;
private SmoothScrollingDelegateAction( Action delegate, boolean blittingOnly ) {
super( delegate );
this.blittingOnly = blittingOnly;
}
@Override @Override
public void layoutContainer( Container parent ) { public void actionPerformed( ActionEvent e ) {
super.layoutContainer( parent ); Object source = e.getSource();
JScrollPane scrollPane = (source instanceof Component)
JScrollPane scrollPane = (JScrollPane) parent; ? (JScrollPane) SwingUtilities.getAncestorOfClass( JScrollPane.class, (Component) source )
int padding = getBorderLeftRightPadding( scrollPane ); : null;
if( padding > 0 && vsb != null && vsb.isVisible() ) { if( scrollPane != null && scrollPane.getUI() instanceof FlatScrollPaneUI ) {
// move vertical scrollbar to trailing edge ((FlatScrollPaneUI)scrollPane.getUI()).runWithScrollBarsTargetValues( blittingOnly,
Insets insets = scrollPane.getInsets(); () -> delegate.actionPerformed( e ) );
Rectangle r = vsb.getBounds(); } else
int y = Math.max( r.y, insets.top + padding ); delegate.actionPerformed( e );
int y2 = Math.min( r.y + r.height, scrollPane.getHeight() - insets.bottom - padding );
boolean ltr = scrollPane.getComponentOrientation().isLeftToRight();
vsb.setBounds( r.x + (ltr ? padding : -padding), y, r.width, y2 - y );
// increase width of viewport, column header and horizontal scrollbar
if( canIncreaseViewportWidth( scrollPane ) ) {
int extraWidth = Math.min( padding, vsb.getWidth() );
resizeViewport( viewport, extraWidth, ltr );
resizeViewport( colHead, extraWidth, ltr );
resizeViewport( hsb, extraWidth, ltr );
}
}
}
boolean canIncreaseViewportWidth( JScrollPane scrollPane ) {
return scrollPane.getComponentOrientation().isLeftToRight()
? !isCornerVisible( upperRight ) && !isCornerVisible( lowerRight )
: !isCornerVisible( upperLeft ) && !isCornerVisible( lowerLeft );
}
private static boolean isCornerVisible( Component corner ) {
return corner != null &&
corner.getWidth() > 0 &&
corner.getHeight() > 0 &&
corner.isVisible();
}
private static void resizeViewport( Component c, int extraWidth, boolean ltr ) {
if( c == null )
return;
Rectangle vr = c.getBounds();
c.setBounds( vr.x - (ltr ? 0 : extraWidth), vr.y, vr.width + extraWidth, vr.height );
} }
} }
} }

View File

@@ -16,9 +16,7 @@
package com.formdev.flatlaf.ui; package com.formdev.flatlaf.ui;
import java.awt.Canvas;
import java.awt.Color; import java.awt.Color;
import java.awt.Component;
import java.awt.Container; import java.awt.Container;
import java.awt.Cursor; import java.awt.Cursor;
import java.awt.Graphics; import java.awt.Graphics;
@@ -69,8 +67,6 @@ import com.formdev.flatlaf.util.UIScale;
* <!-- FlatSplitPaneUI --> * <!-- FlatSplitPaneUI -->
* *
* @uiDefault Component.arrowType String chevron (default) or triangle * @uiDefault Component.arrowType String chevron (default) or triangle
* @uiDefault SplitPaneDivider.hoverColor Color optional
* @uiDefault SplitPaneDivider.pressedColor Color optional
* @uiDefault SplitPaneDivider.oneTouchArrowColor Color * @uiDefault SplitPaneDivider.oneTouchArrowColor Color
* @uiDefault SplitPaneDivider.oneTouchHoverArrowColor Color * @uiDefault SplitPaneDivider.oneTouchHoverArrowColor Color
* @uiDefault SplitPaneDivider.oneTouchPressedArrowColor Color * @uiDefault SplitPaneDivider.oneTouchPressedArrowColor Color
@@ -87,7 +83,6 @@ public class FlatSplitPaneUI
implements StyleableUI implements StyleableUI
{ {
@Styleable protected String arrowType; @Styleable protected String arrowType;
/** @since 3.3 */ @Styleable protected Color draggingColor;
@Styleable protected Color oneTouchArrowColor; @Styleable protected Color oneTouchArrowColor;
@Styleable protected Color oneTouchHoverArrowColor; @Styleable protected Color oneTouchHoverArrowColor;
@Styleable protected Color oneTouchPressedArrowColor; @Styleable protected Color oneTouchPressedArrowColor;
@@ -109,8 +104,6 @@ public class FlatSplitPaneUI
protected void installDefaults() { protected void installDefaults() {
arrowType = UIManager.getString( "Component.arrowType" ); arrowType = UIManager.getString( "Component.arrowType" );
draggingColor = UIManager.getColor( "SplitPaneDivider.draggingColor" );
// get one-touch colors before invoking super.installDefaults() because they are // get one-touch colors before invoking super.installDefaults() because they are
// used in there on LaF switching // used in there on LaF switching
oneTouchArrowColor = UIManager.getColor( "SplitPaneDivider.oneTouchArrowColor" ); oneTouchArrowColor = UIManager.getColor( "SplitPaneDivider.oneTouchArrowColor" );
@@ -124,8 +117,6 @@ public class FlatSplitPaneUI
protected void uninstallDefaults() { protected void uninstallDefaults() {
super.uninstallDefaults(); super.uninstallDefaults();
draggingColor = null;
oneTouchArrowColor = null; oneTouchArrowColor = null;
oneTouchHoverArrowColor = null; oneTouchHoverArrowColor = null;
oneTouchPressedArrowColor = null; oneTouchPressedArrowColor = null;
@@ -192,49 +183,12 @@ public class FlatSplitPaneUI
return FlatStylingSupport.getAnnotatedStyleableValue( this, key ); return FlatStylingSupport.getAnnotatedStyleableValue( this, key );
} }
@Override
protected Component createDefaultNonContinuousLayoutDivider() {
// only used for non-continuous layout if left or right component is heavy weight
return new Canvas() {
@Override
public void paint( Graphics g ) {
if( !isContinuousLayout() && getLastDragLocation() != -1 )
paintDragDivider( g, 0 );
}
};
}
@Override
public void finishedPaintingChildren( JSplitPane sp, Graphics g ) {
if( sp == splitPane && getLastDragLocation() != -1 && !isContinuousLayout() && !draggingHW )
paintDragDivider( g, getLastDragLocation() );
}
private void paintDragDivider( Graphics g, int dividerLocation ) {
// divider bounds
boolean horizontal = (getOrientation() == JSplitPane.HORIZONTAL_SPLIT);
int x = horizontal ? dividerLocation : 0;
int y = !horizontal ? dividerLocation : 0;
int width = horizontal ? dividerSize : splitPane.getWidth();
int height = !horizontal ? dividerSize : splitPane.getHeight();
// paint background
g.setColor( FlatUIUtils.deriveColor( draggingColor, splitPane.getBackground() ) );
g.fillRect( x, y, width, height );
// paint divider style (e.g. grip)
if( divider instanceof FlatSplitPaneDivider )
((FlatSplitPaneDivider)divider).paintStyle( g, x, y, width, height );
}
//---- class FlatSplitPaneDivider ----------------------------------------- //---- class FlatSplitPaneDivider -----------------------------------------
protected class FlatSplitPaneDivider protected class FlatSplitPaneDivider
extends BasicSplitPaneDivider extends BasicSplitPaneDivider
{ {
@Styleable protected String style = UIManager.getString( "SplitPaneDivider.style" ); @Styleable protected String style = UIManager.getString( "SplitPaneDivider.style" );
/** @since 3.3 */ @Styleable protected Color hoverColor = UIManager.getColor( "SplitPaneDivider.hoverColor" );
/** @since 3.3 */ @Styleable protected Color pressedColor = UIManager.getColor( "SplitPaneDivider.pressedColor" );
@Styleable protected Color gripColor = UIManager.getColor( "SplitPaneDivider.gripColor" ); @Styleable protected Color gripColor = UIManager.getColor( "SplitPaneDivider.gripColor" );
@Styleable protected int gripDotCount = FlatUIUtils.getUIInt( "SplitPaneDivider.gripDotCount", 3 ); @Styleable protected int gripDotCount = FlatUIUtils.getUIInt( "SplitPaneDivider.gripDotCount", 3 );
@Styleable protected int gripDotSize = FlatUIUtils.getUIInt( "SplitPaneDivider.gripDotSize", 3 ); @Styleable protected int gripDotSize = FlatUIUtils.getUIInt( "SplitPaneDivider.gripDotSize", 3 );
@@ -297,31 +251,15 @@ public class FlatSplitPaneUI
@Override @Override
public void paint( Graphics g ) { public void paint( Graphics g ) {
// paint hover or pressed background
Color hoverOrPressedColor = (isContinuousLayout() && dragger != null)
? pressedColor
: (isMouseOver() && dragger == null
? hoverColor
: null);
if( hoverOrPressedColor != null ) {
g.setColor( FlatUIUtils.deriveColor( hoverOrPressedColor, splitPane.getBackground() ) );
g.fillRect( 0, 0, getWidth(), getHeight() );
}
super.paint( g ); super.paint( g );
paintStyle( g, 0, 0, getWidth(), getHeight() );
}
/** @since 3.3 */
protected void paintStyle( Graphics g, int x, int y, int width, int height ) {
if( "plain".equals( style ) ) if( "plain".equals( style ) )
return; return;
Object[] oldRenderingHints = FlatUIUtils.setRenderingHints( g ); Object[] oldRenderingHints = FlatUIUtils.setRenderingHints( g );
g.setColor( gripColor ); g.setColor( gripColor );
paintGrip( g, x, y, width, height ); paintGrip( g, 0, 0, getWidth(), getHeight() );
FlatUIUtils.resetRenderingHints( g, oldRenderingHints ); FlatUIUtils.resetRenderingHints( g, oldRenderingHints );
} }
@@ -348,29 +286,6 @@ public class FlatSplitPaneUI
: location == (splitPane.getWidth() - getWidth() - insets.right); : location == (splitPane.getWidth() - getWidth() - insets.right);
} }
@Override
protected void setMouseOver( boolean mouseOver ) {
super.setMouseOver( mouseOver );
repaintIfNecessary();
}
@Override
protected void prepareForDragging() {
super.prepareForDragging();
repaintIfNecessary();
}
@Override
protected void finishDraggingTo( int location ) {
super.finishDraggingTo( location );
repaintIfNecessary();
}
private void repaintIfNecessary() {
if( hoverColor != null || pressedColor != null )
repaint();
}
//---- class FlatOneTouchButton --------------------------------------- //---- class FlatOneTouchButton ---------------------------------------
protected class FlatOneTouchButton protected class FlatOneTouchButton

View File

@@ -18,7 +18,6 @@ package com.formdev.flatlaf.ui;
import java.awt.Color; import java.awt.Color;
import java.awt.Component; import java.awt.Component;
import java.awt.Container;
import java.awt.Cursor; import java.awt.Cursor;
import java.awt.Dimension; import java.awt.Dimension;
import java.awt.Graphics; import java.awt.Graphics;
@@ -29,15 +28,16 @@ import java.awt.event.MouseEvent;
import java.awt.geom.Rectangle2D; import java.awt.geom.Rectangle2D;
import java.beans.PropertyChangeListener; import java.beans.PropertyChangeListener;
import java.util.Map; import java.util.Map;
import javax.swing.CellRendererPane;
import javax.swing.Icon; import javax.swing.Icon;
import javax.swing.JComponent; import javax.swing.JComponent;
import javax.swing.JLabel; import javax.swing.JLabel;
import javax.swing.JTable; import javax.swing.JTable;
import javax.swing.SwingConstants; import javax.swing.SwingConstants;
import javax.swing.UIManager; import javax.swing.UIManager;
import javax.swing.border.Border;
import javax.swing.event.MouseInputListener; import javax.swing.event.MouseInputListener;
import javax.swing.plaf.ComponentUI; import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.UIResource;
import javax.swing.plaf.basic.BasicTableHeaderUI; import javax.swing.plaf.basic.BasicTableHeaderUI;
import javax.swing.table.JTableHeader; import javax.swing.table.JTableHeader;
import javax.swing.table.TableCellRenderer; import javax.swing.table.TableCellRenderer;
@@ -114,11 +114,6 @@ public class FlatTableHeaderUI
public void installUI( JComponent c ) { public void installUI( JComponent c ) {
super.installUI( c ); super.installUI( c );
// replace cell renderer pane
header.remove( rendererPane );
rendererPane = new FlatTableHeaderCellRendererPane();
header.add( rendererPane );
installStyle(); installStyle();
} }
@@ -270,8 +265,16 @@ public class FlatTableHeaderUI
} }
} }
// temporary use own default renderer
FlatTableCellHeaderRenderer tempRenderer = new FlatTableCellHeaderRenderer( header.getDefaultRenderer() );
header.setDefaultRenderer( tempRenderer );
// paint header // paint header
super.paint( g, c ); super.paint( g, c );
// restore default renderer
tempRenderer.reset();
header.setDefaultRenderer( tempRenderer.delegate );
} }
private boolean isSystemDefaultRenderer( Object headerRenderer ) { private boolean isSystemDefaultRenderer( Object headerRenderer ) {
@@ -329,129 +332,119 @@ public class FlatTableHeaderUI
return false; return false;
} }
//---- class FlatTableHeaderCellRendererPane ------------------------------ //---- class FlatTableCellHeaderRenderer ----------------------------------
/** /**
* Cell renderer pane that is used to paint hover and pressed background/foreground * A delegating header renderer that is only used to paint hover and pressed
* and to paint sort arrows at top, bottom or left position. * background/foreground and to paint sort arrows at top, bottom or left position.
*/ */
private class FlatTableHeaderCellRendererPane private class FlatTableCellHeaderRenderer
extends CellRendererPane implements TableCellRenderer, Border, UIResource
{ {
private final Icon ascendingSortIcon; private final TableCellRenderer delegate;
private final Icon descendingSortIcon;
public FlatTableHeaderCellRendererPane() { private JLabel l;
ascendingSortIcon = UIManager.getIcon( "Table.ascendingSortIcon" ); private Color oldBackground;
descendingSortIcon = UIManager.getIcon( "Table.descendingSortIcon" ); private Color oldForeground;
private Boolean oldOpaque;
private int oldHorizontalTextPosition = -1;
private Border origBorder;
private Icon sortIcon;
FlatTableCellHeaderRenderer( TableCellRenderer delegate ) {
this.delegate = delegate;
} }
@Override @Override
public void paintComponent( Graphics g, Component c, Container p, int x, int y, int w, int h, boolean shouldValidate ) { public Component getTableCellRendererComponent( JTable table, Object value, boolean isSelected,
if( !(c instanceof JLabel) ) { boolean hasFocus, int row, int column )
super.paintComponent( g, c, p, x, y, w, h, shouldValidate ); {
return; Component c = delegate.getTableCellRendererComponent( table, value, isSelected, hasFocus, row, column );
} if( !(c instanceof JLabel) )
return c;
JLabel l = (JLabel) c; l = (JLabel) c;
Color oldBackground = null;
Color oldForeground = null;
boolean oldOpaque = false;
Icon oldIcon = null;
int oldHorizontalTextPosition = -1;
// hover and pressed background/foreground // hover and pressed background/foreground
TableColumn draggedColumn = header.getDraggedColumn(); TableColumn draggedColumn = header.getDraggedColumn();
Color background = null; Color background = null;
Color foreground = null; Color foreground = null;
if( draggedColumn != null && if( draggedColumn != null && header.getTable().convertColumnIndexToView( draggedColumn.getModelIndex() ) == column ) {
header.getTable().convertColumnIndexToView( draggedColumn.getModelIndex() )
== getColumn( x - header.getDraggedDistance(), w ) )
{
background = pressedBackground; background = pressedBackground;
foreground = pressedForeground; foreground = pressedForeground;
} else if( getRolloverColumn() >= 0 && getRolloverColumn() == getColumn( x, w ) ) { } else if( getRolloverColumn() == column ) {
background = hoverBackground; background = hoverBackground;
foreground = hoverForeground; foreground = hoverForeground;
} }
if( background != null ) { if( background != null ) {
oldBackground = l.getBackground(); if( oldBackground == null )
oldOpaque = l.isOpaque(); oldBackground = l.getBackground();
if( oldOpaque == null )
oldOpaque = l.isOpaque();
l.setBackground( FlatUIUtils.deriveColor( background, header.getBackground() ) ); l.setBackground( FlatUIUtils.deriveColor( background, header.getBackground() ) );
l.setOpaque( true ); l.setOpaque( true );
} }
if( foreground != null ) { if( foreground != null ) {
oldForeground = l.getForeground(); if( oldForeground == null )
oldForeground = l.getForeground();
l.setForeground( FlatUIUtils.deriveColor( foreground, header.getForeground() ) ); l.setForeground( FlatUIUtils.deriveColor( foreground, header.getForeground() ) );
} }
// sort icon position // sort icon
Icon icon = l.getIcon(); if( sortIconPosition == SwingConstants.LEFT ) {
boolean isSortIcon = (icon != null && (icon == ascendingSortIcon || icon == descendingSortIcon)); // left
if( isSortIcon ) { if( oldHorizontalTextPosition < 0 )
if( sortIconPosition == SwingConstants.LEFT ) {
// left
oldHorizontalTextPosition = l.getHorizontalTextPosition(); oldHorizontalTextPosition = l.getHorizontalTextPosition();
l.setHorizontalTextPosition( SwingConstants.RIGHT ); l.setHorizontalTextPosition( SwingConstants.RIGHT );
} else if( sortIconPosition == SwingConstants.TOP || sortIconPosition == SwingConstants.BOTTOM ) { } else if( sortIconPosition == SwingConstants.TOP || sortIconPosition == SwingConstants.BOTTOM ) {
// top or bottom // top or bottom
oldIcon = icon; sortIcon = l.getIcon();
l.setIcon( null ); origBorder = l.getBorder();
} l.setIcon( null );
l.setBorder( this );
} }
// paint renderer component return l;
super.paintComponent( g, c, p, x, y, w, h, shouldValidate ); }
// paint top or bottom sort icon void reset() {
if( isSortIcon && (sortIconPosition == SwingConstants.TOP || sortIconPosition == SwingConstants.BOTTOM) ) { if( l == null )
int xi = x + ((w - icon.getIconWidth()) / 2); return;
int yi = (sortIconPosition == SwingConstants.TOP)
? y + UIScale.scale( 1 )
: y + height - icon.getIconHeight()
- 1 // for gap
- (int) (1 * UIScale.getUserScaleFactor()); // for bottom border
icon.paintIcon( c, g, xi, yi );
}
// restore modified renderer component properties if( oldBackground != null )
if( background != null ) {
l.setBackground( oldBackground ); l.setBackground( oldBackground );
l.setOpaque( oldOpaque ); if( oldForeground != null )
}
if( foreground != null )
l.setForeground( oldForeground ); l.setForeground( oldForeground );
if( oldIcon != null ) if( oldOpaque != null )
l.setIcon( oldIcon ); l.setOpaque( oldOpaque );
if( oldHorizontalTextPosition >= 0 ) if( oldHorizontalTextPosition >= 0 )
l.setHorizontalTextPosition( oldHorizontalTextPosition ); l.setHorizontalTextPosition( oldHorizontalTextPosition );
} }
/** @Override
* Get column index for given coordinates. public void paintBorder( Component c, Graphics g, int x, int y, int width, int height ) {
*/ if( origBorder != null )
private int getColumn( int x, int width ) { origBorder.paintBorder( c, g, x, y, width, height );
TableColumnModel columnModel = header.getColumnModel();
int columnCount = columnModel.getColumnCount();
boolean ltr = header.getComponentOrientation().isLeftToRight();
int cx = ltr ? 0 : getWidthInRightToLef();
for( int i = 0; i < columnCount; i++ ) { if( sortIcon != null ) {
int cw = columnModel.getColumn( i ).getWidth(); int xi = x + ((width - sortIcon.getIconWidth()) / 2);
if( x == cx - (ltr ? 0 : cw) && width == cw ) int yi = (sortIconPosition == SwingConstants.TOP)
return i; ? y + UIScale.scale( 1 )
: y + height - sortIcon.getIconHeight()
cx += ltr ? cw : -cw; - 1 // for gap
- (int) (1 * UIScale.getUserScaleFactor()); // for bottom border
sortIcon.paintIcon( c, g, xi, yi );
} }
return -1;
} }
// similar to JTableHeader.getWidthInRightToLeft() @Override
private int getWidthInRightToLef() { public Insets getBorderInsets( Component c ) {
JTable table = header.getTable(); return (origBorder != null) ? origBorder.getBorderInsets( c ) : new Insets( 0, 0, 0, 0 );
return (table != null && table.getAutoResizeMode() != JTable.AUTO_RESIZE_OFF) }
? table.getWidth()
: header.getWidth(); @Override
public boolean isBorderOpaque() {
return (origBorder != null) ? origBorder.isBorderOpaque() : false;
} }
} }

View File

@@ -17,7 +17,6 @@
package com.formdev.flatlaf.ui; package com.formdev.flatlaf.ui;
import java.awt.Color; import java.awt.Color;
import java.awt.Component;
import java.awt.Container; import java.awt.Container;
import java.awt.Dimension; import java.awt.Dimension;
import java.awt.EventQueue; import java.awt.EventQueue;
@@ -27,25 +26,18 @@ import java.awt.Insets;
import java.awt.event.FocusEvent; import java.awt.event.FocusEvent;
import java.awt.event.FocusListener; import java.awt.event.FocusListener;
import java.awt.geom.Rectangle2D; import java.awt.geom.Rectangle2D;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener; import java.beans.PropertyChangeListener;
import java.util.Map; import java.util.Map;
import javax.swing.JComponent; import javax.swing.JComponent;
import javax.swing.JScrollPane; import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JViewport; import javax.swing.JViewport;
import javax.swing.LookAndFeel; import javax.swing.LookAndFeel;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities; import javax.swing.SwingUtilities;
import javax.swing.UIManager; import javax.swing.UIManager;
import javax.swing.plaf.ComponentUI; import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.UIResource;
import javax.swing.plaf.basic.BasicTableUI; import javax.swing.plaf.basic.BasicTableUI;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.JTableHeader; import javax.swing.table.JTableHeader;
import javax.swing.table.TableCellRenderer;
import com.formdev.flatlaf.FlatClientProperties; import com.formdev.flatlaf.FlatClientProperties;
import com.formdev.flatlaf.icons.FlatCheckBoxIcon;
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable; import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI; import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
import com.formdev.flatlaf.util.Graphics2DProxy; import com.formdev.flatlaf.util.Graphics2DProxy;
@@ -124,7 +116,6 @@ public class FlatTableUI
private boolean oldShowHorizontalLines; private boolean oldShowHorizontalLines;
private boolean oldShowVerticalLines; private boolean oldShowVerticalLines;
private Dimension oldIntercellSpacing; private Dimension oldIntercellSpacing;
private TableCellRenderer oldBooleanRenderer;
private PropertyChangeListener propertyChangeListener; private PropertyChangeListener propertyChangeListener;
private Map<String, Object> oldStyleValues; private Map<String, Object> oldStyleValues;
@@ -160,35 +151,19 @@ public class FlatTableUI
if( rowHeight > 0 ) if( rowHeight > 0 )
LookAndFeel.installProperty( table, "rowHeight", UIScale.scale( rowHeight ) ); LookAndFeel.installProperty( table, "rowHeight", UIScale.scale( rowHeight ) );
FlatTablePropertyWatcher watcher = FlatTablePropertyWatcher.get( table ); if( !showHorizontalLines ) {
if( watcher != null )
watcher.enabled = false;
if( !showHorizontalLines && (watcher == null || !watcher.showHorizontalLinesChanged) ) {
oldShowHorizontalLines = table.getShowHorizontalLines(); oldShowHorizontalLines = table.getShowHorizontalLines();
table.setShowHorizontalLines( false ); table.setShowHorizontalLines( false );
} }
if( !showVerticalLines && (watcher == null || !watcher.showVerticalLinesChanged) ) { if( !showVerticalLines ) {
oldShowVerticalLines = table.getShowVerticalLines(); oldShowVerticalLines = table.getShowVerticalLines();
table.setShowVerticalLines( false ); table.setShowVerticalLines( false );
} }
if( intercellSpacing != null && (watcher == null || !watcher.intercellSpacingChanged) ) { if( intercellSpacing != null ) {
oldIntercellSpacing = table.getIntercellSpacing(); oldIntercellSpacing = table.getIntercellSpacing();
table.setIntercellSpacing( intercellSpacing ); table.setIntercellSpacing( intercellSpacing );
} }
if( watcher != null )
watcher.enabled = true;
else
table.addPropertyChangeListener( new FlatTablePropertyWatcher() );
// install boolean renderer
oldBooleanRenderer = table.getDefaultRenderer( Boolean.class );
if( oldBooleanRenderer instanceof UIResource )
table.setDefaultRenderer( Boolean.class, new FlatBooleanRenderer() );
else
oldBooleanRenderer = null;
} }
@Override @Override
@@ -202,36 +177,15 @@ public class FlatTableUI
oldStyleValues = null; oldStyleValues = null;
FlatTablePropertyWatcher watcher = FlatTablePropertyWatcher.get( table );
if( watcher != null )
watcher.enabled = false;
// restore old show horizontal/vertical lines (if not modified) // restore old show horizontal/vertical lines (if not modified)
if( !showHorizontalLines && oldShowHorizontalLines && !table.getShowHorizontalLines() && if( !showHorizontalLines && oldShowHorizontalLines && !table.getShowHorizontalLines() )
(watcher == null || !watcher.showHorizontalLinesChanged) ) table.setShowHorizontalLines( true );
table.setShowHorizontalLines( true ); if( !showVerticalLines && oldShowVerticalLines && !table.getShowVerticalLines() )
if( !showVerticalLines && oldShowVerticalLines && !table.getShowVerticalLines() && table.setShowVerticalLines( true );
(watcher == null || !watcher.showVerticalLinesChanged) )
table.setShowVerticalLines( true );
// restore old intercell spacing (if not modified) // restore old intercell spacing (if not modified)
if( intercellSpacing != null && table.getIntercellSpacing().equals( intercellSpacing ) && if( intercellSpacing != null && table.getIntercellSpacing().equals( intercellSpacing ) )
(watcher == null || !watcher.intercellSpacingChanged) ) table.setIntercellSpacing( oldIntercellSpacing );
table.setIntercellSpacing( oldIntercellSpacing );
if( watcher != null )
watcher.enabled = true;
// uninstall boolean renderer
if( table.getDefaultRenderer( Boolean.class ) instanceof FlatBooleanRenderer ) {
if( oldBooleanRenderer instanceof Component ) {
// because the old renderer component was not attached to any component hierarchy,
// its UI was not yet updated, and it is necessary to do it here
SwingUtilities.updateComponentTreeUI( (Component) oldBooleanRenderer );
}
table.setDefaultRenderer( Boolean.class, oldBooleanRenderer );
}
oldBooleanRenderer = null;
} }
@Override @Override
@@ -513,70 +467,4 @@ public class FlatTableUI
} }
} }
} }
//---- class FlatTablePropertyWatcher -------------------------------------
/**
* Listener that watches for change of some table properties from application code.
* This information is used in {@link FlatTableUI#installDefaults()} and
* {@link FlatTableUI#uninstallDefaults()} to decide whether FlatLaf modifies those properties.
* If they are modified in application code, FlatLaf no longer changes them.
*
* The listener is added once for each table, but never removed.
* So switching Laf/theme reuses existing listener.
*/
private static class FlatTablePropertyWatcher
implements PropertyChangeListener
{
boolean enabled = true;
boolean showHorizontalLinesChanged;
boolean showVerticalLinesChanged;
boolean intercellSpacingChanged;
static FlatTablePropertyWatcher get( JTable table ) {
for( PropertyChangeListener l : table.getPropertyChangeListeners() ) {
if( l instanceof FlatTablePropertyWatcher )
return (FlatTablePropertyWatcher) l;
}
return null;
}
//---- interface PropertyChangeListener ----
@Override
public void propertyChange( PropertyChangeEvent e ) {
if( !enabled )
return;
switch( e.getPropertyName() ) {
case "showHorizontalLines": showHorizontalLinesChanged = true; break;
case "showVerticalLines": showVerticalLinesChanged = true; break;
case "rowMargin": intercellSpacingChanged = true; break;
}
}
}
//---- class FlatBooleanRenderer ------------------------------------------
private static class FlatBooleanRenderer
extends DefaultTableCellRenderer
implements UIResource
{
private boolean selected;
FlatBooleanRenderer() {
setHorizontalAlignment( SwingConstants.CENTER );
setIcon( new FlatCheckBoxIcon() {
@Override
protected boolean isSelected( Component c ) {
return selected;
}
} );
}
@Override
protected void setValue( Object value ) {
selected = (value != null && (Boolean) value);
}
}
} }

View File

@@ -141,6 +141,12 @@ public class FlatTextAreaUI
focusListener = null; focusListener = null;
} }
@Override
protected void installKeyboardActions() {
super.installKeyboardActions();
FlatEditorPaneUI.installKeyboardActions( getComponent() );
}
@Override @Override
protected Caret createCaret() { protected Caret createCaret() {
return new FlatCaret( null, false ); return new FlatCaret( null, false );

View File

@@ -45,7 +45,6 @@ import javax.swing.JTextField;
import javax.swing.JToggleButton; import javax.swing.JToggleButton;
import javax.swing.JToolBar; import javax.swing.JToolBar;
import javax.swing.LookAndFeel; import javax.swing.LookAndFeel;
import javax.swing.SwingConstants;
import javax.swing.UIManager; import javax.swing.UIManager;
import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener; import javax.swing.event.DocumentListener;
@@ -481,21 +480,9 @@ debug*/
// compute placeholder location // compute placeholder location
Rectangle r = getVisibleEditorRect(); Rectangle r = getVisibleEditorRect();
FontMetrics fm = c.getFontMetrics( c.getFont() ); FontMetrics fm = c.getFontMetrics( c.getFont() );
int x = r.x;
int y = r.y + fm.getAscent() + ((r.height - fm.getHeight()) / 2);
// apply horizontal alignment to x location
String clippedPlaceholder = JavaCompatibility.getClippedString( c, fm, placeholder, r.width ); String clippedPlaceholder = JavaCompatibility.getClippedString( c, fm, placeholder, r.width );
int stringWidth = fm.stringWidth( clippedPlaceholder ); int x = r.x + (isLeftToRight() ? 0 : r.width - fm.stringWidth( clippedPlaceholder ));
int halign = (c instanceof JTextField) ? ((JTextField)c).getHorizontalAlignment() : SwingConstants.LEADING; int y = r.y + fm.getAscent() + ((r.height - fm.getHeight()) / 2);
if( halign == SwingConstants.LEADING )
halign = isLeftToRight() ? SwingConstants.LEFT : SwingConstants.RIGHT;
else if( halign == SwingConstants.TRAILING )
halign = isLeftToRight() ? SwingConstants.RIGHT : SwingConstants.LEFT;
if( halign == SwingConstants.RIGHT )
x += r.width - stringWidth;
else if( halign == SwingConstants.CENTER )
x = Math.max( 0, x + (r.width / 2) - (stringWidth / 2) );
// paint placeholder // paint placeholder
g.setColor( placeholderForeground ); g.setColor( placeholderForeground );

View File

@@ -142,6 +142,12 @@ public class FlatTextPaneUI
focusListener = null; focusListener = null;
} }
@Override
protected void installKeyboardActions() {
super.installKeyboardActions();
FlatEditorPaneUI.installKeyboardActions( getComponent() );
}
@Override @Override
protected Caret createCaret() { protected Caret createCaret() {
return new FlatCaret( null, false ); return new FlatCaret( null, false );
@@ -156,6 +162,11 @@ public class FlatTextPaneUI
super.propertyChange( e ); super.propertyChange( e );
FlatEditorPaneUI.propertyChange( getComponent(), e, this::installStyle ); FlatEditorPaneUI.propertyChange( getComponent(), e, this::installStyle );
// BasicEditorPaneUI.propertyChange() re-applied actions from editor kit,
// which removed our delegate actions
if( "editorKit".equals( propertyName ) )
FlatEditorPaneUI.installKeyboardActions( getComponent() );
} }
/** @since 2 */ /** @since 2 */

View File

@@ -608,10 +608,6 @@ public class FlatTitlePane
doLayout(); doLayout();
} }
void menuBarInvalidate() {
menuBarPlaceholder.invalidate();
}
@Override @Override
public void paint( Graphics g ) { public void paint( Graphics g ) {
super.paint( g ); super.paint( g );
@@ -839,6 +835,10 @@ public class FlatTitlePane
window.dispatchEvent( new WindowEvent( window, WindowEvent.WINDOW_CLOSING ) ); window.dispatchEvent( new WindowEvent( window, WindowEvent.WINDOW_CLOSING ) );
} }
private boolean hasJBRCustomDecoration() {
return window != null && JBRCustomDecorations.hasCustomDecoration( window );
}
/** /**
* Returns whether windows uses native window border and has custom decorations enabled. * Returns whether windows uses native window border and has custom decorations enabled.
*/ */
@@ -896,7 +896,10 @@ public class FlatTitlePane
iconBounds.width += iconInsets.right; iconBounds.width += iconInsets.right;
} }
appIconBounds = iconBounds; if( hasJBRCustomDecoration() )
hitTestSpots.add( iconBounds );
else
appIconBounds = iconBounds;
} else if( showIconBesideTitle && titleLabel.getIcon() != null && titleLabel.getUI() instanceof FlatTitleLabelUI ) { } else if( showIconBesideTitle && titleLabel.getIcon() != null && titleLabel.getUI() instanceof FlatTitleLabelUI ) {
FlatTitleLabelUI ui = (FlatTitleLabelUI) titleLabel.getUI(); FlatTitleLabelUI ui = (FlatTitleLabelUI) titleLabel.getUI();
@@ -924,7 +927,10 @@ public class FlatTitlePane
iconR.width += 2; iconR.width += 2;
iconR.height += 2; iconR.height += 2;
appIconBounds = iconR; if( hasJBRCustomDecoration() )
hitTestSpots.add( iconR );
else
appIconBounds = iconR;
} }
} }
@@ -1264,7 +1270,7 @@ debug*/
public void mouseClicked( MouseEvent e ) { public void mouseClicked( MouseEvent e ) {
// on Linux, when using native library, the mouse clicked event // on Linux, when using native library, the mouse clicked event
// is usually not sent and maximize/restore is done in mouse pressed event // is usually not sent and maximize/restore is done in mouse pressed event
// this check is here for the case that a mouse clicked event comes through for some reason // this check is here for the case that a mouse clicked event comes thru for some reason
if( linuxNativeMove && SystemInfo.isLinux && FlatNativeLinuxLibrary.isWMUtilsSupported( window ) ) { if( linuxNativeMove && SystemInfo.isLinux && FlatNativeLinuxLibrary.isWMUtilsSupported( window ) ) {
// see comment in mousePressed() // see comment in mousePressed()
if( lastSingleClickWhen != 0 && (e.getWhen() - lastSingleClickWhen) <= getMultiClickInterval() ) { if( lastSingleClickWhen != 0 && (e.getWhen() - lastSingleClickWhen) <= getMultiClickInterval() ) {

View File

@@ -18,7 +18,6 @@ package com.formdev.flatlaf.ui;
import static com.formdev.flatlaf.util.UIScale.scale; import static com.formdev.flatlaf.util.UIScale.scale;
import java.awt.Color; import java.awt.Color;
import java.awt.Container;
import java.awt.Dimension; import java.awt.Dimension;
import java.awt.Graphics; import java.awt.Graphics;
import java.awt.Graphics2D; import java.awt.Graphics2D;
@@ -174,12 +173,6 @@ public class FlatToolBarSeparatorUI
if( size != null ) if( size != null )
return scale( size ); return scale( size );
// get separator width
int separatorWidth = this.separatorWidth;
FlatToolBarUI toolBarUI = getToolBarUI( c );
if( toolBarUI != null && toolBarUI.separatorWidth != null )
separatorWidth = toolBarUI.separatorWidth;
// make sure that gap on left and right side of line have same size // make sure that gap on left and right side of line have same size
int sepWidth = (scale( (separatorWidth - LINE_WIDTH) / 2 ) * 2) + scale( LINE_WIDTH ); int sepWidth = (scale( (separatorWidth - LINE_WIDTH) / 2 ) * 2) + scale( LINE_WIDTH );
@@ -203,12 +196,6 @@ public class FlatToolBarSeparatorUI
float lineWidth = scale( 1f ); float lineWidth = scale( 1f );
float offset = scale( 2f ); float offset = scale( 2f );
// get separator color
Color separatorColor = this.separatorColor;
FlatToolBarUI toolBarUI = getToolBarUI( c );
if( toolBarUI != null && toolBarUI.separatorColor != null )
separatorColor = toolBarUI.separatorColor;
Object[] oldRenderingHints = FlatUIUtils.setRenderingHints( g ); Object[] oldRenderingHints = FlatUIUtils.setRenderingHints( g );
g.setColor( separatorColor ); g.setColor( separatorColor );
@@ -223,11 +210,4 @@ public class FlatToolBarSeparatorUI
private boolean isVertical( JComponent c ) { private boolean isVertical( JComponent c ) {
return ((JToolBar.Separator)c).getOrientation() == SwingConstants.VERTICAL; return ((JToolBar.Separator)c).getOrientation() == SwingConstants.VERTICAL;
} }
private FlatToolBarUI getToolBarUI( JComponent c ) {
Container parent = c.getParent();
return (parent instanceof JToolBar && ((JToolBar)parent).getUI() instanceof FlatToolBarUI)
? (FlatToolBarUI) ((JToolBar)parent).getUI()
: null;
}
} }

View File

@@ -93,10 +93,6 @@ public class FlatToolBarUI
@Styleable protected Insets borderMargins; @Styleable protected Insets borderMargins;
@Styleable protected Color gripColor; @Styleable protected Color gripColor;
// for FlatToolBarSeparatorUI
/** @since 3.3 */ @Styleable protected Integer separatorWidth;
/** @since 3.3 */ @Styleable protected Color separatorColor;
private FocusTraversalPolicy focusTraversalPolicy; private FocusTraversalPolicy focusTraversalPolicy;
private Boolean oldFloatable; private Boolean oldFloatable;
private Map<String, Object> oldStyleValues; private Map<String, Object> oldStyleValues;

View File

@@ -238,6 +238,22 @@ public class FlatTreeUI
oldStyleValues = null; oldStyleValues = null;
} }
@Override
protected void installKeyboardActions() {
super.installKeyboardActions();
FlatScrollPaneUI.installSmoothScrollingDelegateActions( tree, false,
"scrollDownChangeSelection", // PAGE_DOWN
"scrollUpChangeSelection", // PAGE_UP
"scrollDownChangeLead", // ctrl PAGE_DOWN
"scrollUpChangeLead", // ctrl PAGE_UP
"scrollDownExtendSelection", // shift PAGE_DOWN, shift ctrl PAGE_DOWN
"scrollUpExtendSelection", // shift PAGE_UP, shift ctrl PAGE_UP
"selectNextChangeLead", // ctrl DOWN
"selectPreviousChangeLead" // ctrl UP
);
}
@Override @Override
protected void updateRenderer() { protected void updateRenderer() {
super.updateRenderer(); super.updateRenderer();

View File

@@ -0,0 +1,62 @@
/*
* Copyright 2023 FormDev Software GmbH
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.formdev.flatlaf.ui;
import java.beans.PropertyChangeListener;
import javax.swing.Action;
/**
* Base class for UI actions used in ActionMap.
* (similar to class sun.swing.UIAction)
*
* @author Karl Tauber
* @since 3.3
*/
public abstract class FlatUIAction
implements Action
{
protected final String name;
protected final Action delegate;
protected FlatUIAction( String name ) {
this.name = name;
this.delegate = null;
}
protected FlatUIAction( Action delegate ) {
this.name = null;
this.delegate = delegate;
}
@Override
public Object getValue( String key ) {
if( key == NAME && delegate == null )
return name;
return (delegate != null) ? delegate.getValue( key ) : null;
}
@Override
public boolean isEnabled() {
return (delegate != null) ? delegate.isEnabled() : true;
}
// do nothing in following methods because this class is immutable
@Override public void putValue( String key, Object value ) {}
@Override public void setEnabled( boolean b ) {}
@Override public void addPropertyChangeListener( PropertyChangeListener listener ) {}
@Override public void removePropertyChangeListener( PropertyChangeListener listener ) {}
}

View File

@@ -299,10 +299,15 @@ public class FlatUIUtils
if( c == null ) if( c == null )
return false; return false;
// check whether used as table cell editor // check whether used in cell editor (check 3 levels up)
Container parent = c.getParent(); Component c2 = c;
if( parent instanceof JTable && ((JTable)parent).getEditorComponent() == c ) for( int i = 0; i <= 2 && c2 != null; i++ ) {
return true; Container parent = c2.getParent();
if( parent instanceof JTable && ((JTable)parent).getEditorComponent() == c2 )
return true;
c2 = parent;
}
// check whether used as cell editor // check whether used as cell editor
// Table.editor is set in JTable.GenericEditor constructor // Table.editor is set in JTable.GenericEditor constructor
@@ -729,7 +734,7 @@ public class FlatUIUtils
} }
/** /**
* Creates a (rounded) rectangle used to paint components (border, background, etc.). * Creates a (rounded) rectangle used to paint components (border, background, etc).
* The given arc diameter is limited to min(width,height). * The given arc diameter is limited to min(width,height).
*/ */
public static Shape createComponentRectangle( float x, float y, float w, float h, float arc ) { public static Shape createComponentRectangle( float x, float y, float w, float h, float arc ) {

View File

@@ -18,6 +18,7 @@ package com.formdev.flatlaf.ui;
import java.awt.Component; import java.awt.Component;
import java.awt.Graphics; import java.awt.Graphics;
import java.lang.reflect.Method;
import javax.swing.JComponent; import javax.swing.JComponent;
import javax.swing.JViewport; import javax.swing.JViewport;
import javax.swing.plaf.ComponentUI; import javax.swing.plaf.ComponentUI;
@@ -47,9 +48,14 @@ public class FlatViewportUI
Component view = ((JViewport)c).getView(); Component view = ((JViewport)c).getView();
if( view instanceof JComponent ) { if( view instanceof JComponent ) {
ComponentUI ui = JavaCompatibility2.getUI( (JComponent) view ); try {
if( ui instanceof ViewportPainter ) Method m = view.getClass().getMethod( "getUI" );
((ViewportPainter)ui).paintViewport( g, (JComponent) view, (JViewport) c ); Object ui = m.invoke( view );
if( ui instanceof ViewportPainter )
((ViewportPainter)ui).paintViewport( g, (JComponent) view, (JViewport) c );
} catch( Exception ex ) {
// ignore
}
} }
} }

View File

@@ -0,0 +1,306 @@
/*
* Copyright 2020 FormDev Software GmbH
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.formdev.flatlaf.ui;
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.Toolkit;
import java.awt.Window;
import java.awt.event.HierarchyEvent;
import java.awt.event.HierarchyListener;
import java.beans.PropertyChangeListener;
import java.lang.reflect.Method;
import java.util.Collections;
import java.util.List;
import javax.swing.JRootPane;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.plaf.BorderUIResource;
import com.formdev.flatlaf.FlatLaf;
import com.formdev.flatlaf.util.LoggingFacade;
import com.formdev.flatlaf.util.HiDPIUtils;
import com.formdev.flatlaf.util.SystemInfo;
/**
* Support for custom window decorations provided by JetBrains Runtime (based on OpenJDK).
* Requires that the application runs on Windows 10 in a JetBrains Runtime 11 or later.
* <ul>
* <li><a href="https://confluence.jetbrains.com/display/JBR/JetBrains+Runtime">https://confluence.jetbrains.com/display/JBR/JetBrains+Runtime</a></li>
* <li><a href="https://github.com/JetBrains/JetBrainsRuntime">https://github.com/JetBrains/JetBrainsRuntime</a></li>
* </ul>
*
* @author Karl Tauber
*/
public class JBRCustomDecorations
{
private static Boolean supported;
private static Method Window_hasCustomDecoration;
private static Method Window_setHasCustomDecoration;
private static Method WWindowPeer_setCustomDecorationTitleBarHeight;
private static Method WWindowPeer_setCustomDecorationHitTestSpots;
private static Method AWTAccessor_getComponentAccessor;
private static Method AWTAccessor_ComponentAccessor_getPeer;
public static boolean isSupported() {
initialize();
return supported;
}
static Object install( JRootPane rootPane ) {
if( !isSupported() )
return null;
// check whether root pane already has a parent, which is the case when switching LaF
Container parent = rootPane.getParent();
if( parent != null ) {
if( parent instanceof Window )
FlatNativeWindowBorder.install( (Window) parent );
return null;
}
// Use hierarchy listener to wait until the root pane is added to a window.
// Enabling JBR decorations must be done very early, probably before
// window becomes displayable (window.isDisplayable()). Tried also using
// "ancestor" property change event on root pane, but this is invoked too late.
HierarchyListener addListener = new HierarchyListener() {
@Override
public void hierarchyChanged( HierarchyEvent e ) {
if( e.getChanged() != rootPane || (e.getChangeFlags() & HierarchyEvent.PARENT_CHANGED) == 0 )
return;
Container parent = e.getChangedParent();
if( parent instanceof Window )
FlatNativeWindowBorder.install( (Window) parent );
// remove listener since it is actually not possible to uninstall JBR decorations
// use invokeLater to remove listener to avoid that listener
// is removed while listener queue is processed
EventQueue.invokeLater( () -> {
rootPane.removeHierarchyListener( this );
} );
}
};
rootPane.addHierarchyListener( addListener );
return addListener;
}
static void uninstall( JRootPane rootPane, Object data ) {
// remove listener (if not yet done)
if( data instanceof HierarchyListener )
rootPane.removeHierarchyListener( (HierarchyListener) data );
// since it is actually not possible to uninstall JBR decorations,
// simply reduce titleBarHeight so that it is still possible to resize window
// and remove hitTestSpots
Container parent = rootPane.getParent();
if( parent instanceof Window )
setHasCustomDecoration( (Window) parent, false );
}
static boolean hasCustomDecoration( Window window ) {
if( !isSupported() )
return false;
try {
return (Boolean) Window_hasCustomDecoration.invoke( window );
} catch( Exception ex ) {
LoggingFacade.INSTANCE.logSevere( null, ex );
return false;
}
}
static void setHasCustomDecoration( Window window, boolean hasCustomDecoration ) {
if( !isSupported() )
return;
try {
if( hasCustomDecoration )
Window_setHasCustomDecoration.invoke( window );
else
setTitleBarHeightAndHitTestSpots( window, 4, Collections.emptyList() );
} catch( Exception ex ) {
LoggingFacade.INSTANCE.logSevere( null, ex );
}
}
static void setTitleBarHeightAndHitTestSpots( Window window, int titleBarHeight, List<Rectangle> hitTestSpots ) {
if( !isSupported() )
return;
try {
Object compAccessor = AWTAccessor_getComponentAccessor.invoke( null );
Object peer = AWTAccessor_ComponentAccessor_getPeer.invoke( compAccessor, window );
WWindowPeer_setCustomDecorationTitleBarHeight.invoke( peer, titleBarHeight );
WWindowPeer_setCustomDecorationHitTestSpots.invoke( peer, hitTestSpots );
} catch( Exception ex ) {
LoggingFacade.INSTANCE.logSevere( null, ex );
}
}
private static void initialize() {
if( supported != null )
return;
supported = false;
// requires JetBrains Runtime 11 and Windows 10
if( !SystemInfo.isJetBrainsJVM_11_orLater || !SystemInfo.isWindows_10_orLater )
return;
try {
Class<?> awtAcessorClass = Class.forName( "sun.awt.AWTAccessor" );
Class<?> compAccessorClass = Class.forName( "sun.awt.AWTAccessor$ComponentAccessor" );
AWTAccessor_getComponentAccessor = awtAcessorClass.getDeclaredMethod( "getComponentAccessor" );
AWTAccessor_ComponentAccessor_getPeer = compAccessorClass.getDeclaredMethod( "getPeer", Component.class );
Class<?> peerClass = Class.forName( "sun.awt.windows.WWindowPeer" );
WWindowPeer_setCustomDecorationTitleBarHeight = peerClass.getDeclaredMethod( "setCustomDecorationTitleBarHeight", int.class );
WWindowPeer_setCustomDecorationHitTestSpots = peerClass.getDeclaredMethod( "setCustomDecorationHitTestSpots", List.class );
WWindowPeer_setCustomDecorationTitleBarHeight.setAccessible( true );
WWindowPeer_setCustomDecorationHitTestSpots.setAccessible( true );
Window_hasCustomDecoration = Window.class.getDeclaredMethod( "hasCustomDecoration" );
Window_setHasCustomDecoration = Window.class.getDeclaredMethod( "setHasCustomDecoration" );
Window_hasCustomDecoration.setAccessible( true );
Window_setHasCustomDecoration.setAccessible( true );
supported = true;
} catch( Exception ex ) {
// ignore
}
}
//---- class JBRWindowTopBorder -------------------------------------------
static class JBRWindowTopBorder
extends BorderUIResource.EmptyBorderUIResource
{
private static JBRWindowTopBorder instance;
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;
static JBRWindowTopBorder getInstance() {
if( instance == null )
instance = new JBRWindowTopBorder();
return instance;
}
JBRWindowTopBorder() {
super( 1, 0, 0, 0 );
update();
installListeners();
}
void update() {
colorizationAffectsBorders = isColorizationColorAffectsBorders();
activeColor = calculateActiveBorderColor();
}
void installListeners() {
Toolkit toolkit = Toolkit.getDefaultToolkit();
toolkit.addPropertyChangeListener( "win.dwm.colorizationColor.affects.borders", e -> {
colorizationAffectsBorders = isColorizationColorAffectsBorders();
activeColor = calculateActiveBorderColor();
} );
PropertyChangeListener l = e -> {
activeColor = calculateActiveBorderColor();
};
toolkit.addPropertyChangeListener( "win.dwm.colorizationColor", l );
toolkit.addPropertyChangeListener( "win.dwm.colorizationColorBalance", l );
toolkit.addPropertyChangeListener( "win.frame.activeBorderColor", l );
}
boolean isColorizationColorAffectsBorders() {
Object value = Toolkit.getDefaultToolkit().getDesktopProperty( "win.dwm.colorizationColor.affects.borders" );
return (value instanceof Boolean) ? (Boolean) value : true;
}
Color getColorizationColor() {
return (Color) Toolkit.getDefaultToolkit().getDesktopProperty( "win.dwm.colorizationColor" );
}
int getColorizationColorBalance() {
Object value = Toolkit.getDefaultToolkit().getDesktopProperty( "win.dwm.colorizationColorBalance" );
return (value instanceof Integer) ? (Integer) value : -1;
}
private Color calculateActiveBorderColor() {
if( !colorizationAffectsBorders )
return null;
Color colorizationColor = getColorizationColor();
if( colorizationColor != null ) {
int colorizationColorBalance = getColorizationColorBalance();
if( colorizationColorBalance < 0 || colorizationColorBalance > 100 )
colorizationColorBalance = 100;
if( colorizationColorBalance == 0 )
return new Color( 0xD9D9D9 );
if( colorizationColorBalance == 100 )
return colorizationColor;
float alpha = colorizationColorBalance / 100.0f;
float remainder = 1 - alpha;
int r = Math.round( colorizationColor.getRed() * alpha + 0xD9 * remainder );
int g = Math.round( colorizationColor.getGreen() * alpha + 0xD9 * remainder );
int b = Math.round( colorizationColor.getBlue() * alpha + 0xD9 * remainder );
// avoid potential IllegalArgumentException in Color constructor
r = Math.min( Math.max( r, 0 ), 255 );
g = Math.min( Math.max( g, 0 ), 255 );
b = Math.min( Math.max( b, 0 ), 255 );
return new Color( r, g, b );
}
Color activeBorderColor = (Color) Toolkit.getDefaultToolkit().getDesktopProperty( "win.frame.activeBorderColor" );
return (activeBorderColor != null) ? activeBorderColor : UIManager.getColor( "MenuBar.borderColor" );
}
@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();
boolean dark = FlatLaf.isLafDark();
g.setColor( active
? (activeColor != null ? activeColor : (dark ? activeDarkColor : activeLightColor))
: (dark ? inactiveDarkColor : inactiveLightColor) );
HiDPIUtils.paintAtScale1x( (Graphics2D) g, x, y, width, height, this::paintImpl );
}
private void paintImpl( Graphics2D g, int x, int y, int width, int height, double scaleFactor ) {
g.fillRect( x, y, width, 1 );
}
void repaintBorder( Component c ) {
c.repaint( 0, 0, c.getWidth(), 1 );
}
}
}

View File

@@ -1,91 +0,0 @@
/*
* Copyright 2024 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.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.reflect.Method;
import javax.swing.JComponent;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JTable;
import javax.swing.JTree;
import javax.swing.plaf.ComponentUI;
import javax.swing.text.JTextComponent;
import com.formdev.flatlaf.util.LoggingFacade;
import com.formdev.flatlaf.util.SystemInfo;
/**
* Provides Java version compatibility methods.
* <p>
* WARNING: This is private API and may change.
*
* @author Karl Tauber
* @since 3.3
*/
public class JavaCompatibility2
{
private static boolean getUIMethodInitialized;
private static MethodHandle getUIMethod;
/**
* Java 8: getUI() method on various components (e.g. JButton, JList, etc)
* <br>
* Java 9: javax.swing.JComponent.getUI()
*/
public static ComponentUI getUI( JComponent c ) {
try {
// Java 9+
if( SystemInfo.isJava_9_orLater ) {
if( !getUIMethodInitialized ) {
getUIMethodInitialized = true;
try {
MethodType mt = MethodType.methodType( ComponentUI.class, new Class[0] );
getUIMethod = MethodHandles.publicLookup().findVirtual( JComponent.class, "getUI", mt );
} catch( Exception ex ) {
// ignore
LoggingFacade.INSTANCE.logSevere( null, ex );
}
}
if( getUIMethod != null )
return (ComponentUI) getUIMethod.invoke( c );
}
// components often used (e.g. as view in scroll panes)
if( c instanceof JPanel )
return ((JPanel)c).getUI();
if( c instanceof JList )
return ((JList<?>)c).getUI();
if( c instanceof JTable )
return ((JTable)c).getUI();
if( c instanceof JTree )
return ((JTree)c).getUI();
if( c instanceof JTextComponent )
return ((JTextComponent)c).getUI();
// Java 8 and fallback
Method m = c.getClass().getMethod( "getUI" );
return (ComponentUI) m.invoke( c );
} catch( Throwable ex ) {
// ignore
return null;
}
}
}

View File

@@ -80,7 +80,7 @@ public class FontUtils
/** /**
* Loads a font family previously registered via {@link #registerFontFamilyLoader(String, Runnable)}. * Loads a font family previously registered via {@link #registerFontFamilyLoader(String, Runnable)}.
* If the family is already loaded or no loader is registered for that family, nothing happens. * If the family is already loaded or no londer is registered for that family, nothing happens.
*/ */
public static void loadFontFamily( String family ) { public static void loadFontFamily( String family ) {
if( !hasLoaders() ) if( !hasLoaders() )
@@ -109,7 +109,7 @@ public class FontUtils
} }
/** /**
* Returns all font family names available in the graphics environment. * Returns all font familiy names available in the graphics environment.
* This invokes {@link GraphicsEnvironment#getAvailableFontFamilyNames()} and * This invokes {@link GraphicsEnvironment#getAvailableFontFamilyNames()} and
* appends families registered for lazy loading via {@link #registerFontFamilyLoader(String, Runnable)} * appends families registered for lazy loading via {@link #registerFontFamilyLoader(String, Runnable)}
* to the result. * to the result.

View File

@@ -192,8 +192,7 @@ public class HiDPIUtils
case "Inter": case "Inter":
case "Inter Light": case "Inter Light":
case "Inter Semi Bold": // Inter v3 case "Inter Semi Bold":
case "Inter SemiBold": // Inter v4
case "Roboto": case "Roboto":
case "Roboto Light": case "Roboto Light":
case "Roboto Medium": case "Roboto Medium":

View File

@@ -116,11 +116,7 @@ public class NativeLibrary
try { try {
// for development environment // for development environment
if( "file".equals( libraryUrl.getProtocol() ) ) { if( "file".equals( libraryUrl.getProtocol() ) ) {
String binPath = libraryUrl.getPath(); File libraryFile = new File( libraryUrl.getPath() );
String srcPath = binPath.replace( "flatlaf-core/bin/main/", "flatlaf-core/src/main/resources/" );
File libraryFile = new File( srcPath ); // use from 'src' folder if available
if( !libraryFile.isFile() )
libraryFile = new File( binPath ); // use from 'bin' or 'output' folder if available
if( libraryFile.isFile() ) { if( libraryFile.isFile() ) {
// load library without copying // load library without copying
System.load( libraryFile.getCanonicalPath() ); System.load( libraryFile.getCanonicalPath() );

View File

@@ -204,7 +204,7 @@ public class UIScale
if( SystemInfo.isWindows ) { if( SystemInfo.isWindows ) {
// Special handling for Windows to be compatible with OS scaling, // Special handling for Windows to be compatible with OS scaling,
// which distinguish between "screen scaling" and "text scaling". // which distinguish between "screen scaling" and "text scaling".
// - Windows "screen scaling" scales everything (text, icon, gaps, etc.) // - Windows "screen scaling" scales everything (text, icon, gaps, etc)
// and may have different scaling factors for each screen. // and may have different scaling factors for each screen.
// - Windows "text scaling" increases only the font size, but on all screens. // - Windows "text scaling" increases only the font size, but on all screens.
// //

View File

@@ -20,12 +20,9 @@ import java.awt.Dimension;
import java.awt.Image; import java.awt.Image;
import java.awt.image.AbstractMultiResolutionImage; import java.awt.image.AbstractMultiResolutionImage;
import java.awt.image.BaseMultiResolutionImage; import java.awt.image.BaseMultiResolutionImage;
import java.awt.image.ImageObserver;
import java.awt.image.ImageProducer;
import java.awt.image.MultiResolutionImage; import java.awt.image.MultiResolutionImage;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap;
import java.util.IdentityHashMap; import java.util.IdentityHashMap;
import java.util.List; import java.util.List;
import java.util.function.Function; import java.util.function.Function;
@@ -119,26 +116,6 @@ public class MultiResolutionImageSupport
return mapAndCacheImage( mrImage ); return mapAndCacheImage( mrImage );
} }
@Override
public int getWidth( ImageObserver observer ) {
return mrImage.getWidth( observer );
}
@Override
public int getHeight( ImageObserver observer ) {
return mrImage.getHeight( observer );
}
@Override
public ImageProducer getSource() {
return mrImage.getSource();
}
@Override
public Object getProperty( String name, ImageObserver observer ) {
return mrImage.getProperty( name, observer );
}
private Image mapAndCacheImage( Image image ) { private Image mapAndCacheImage( Image image ) {
return cache.computeIfAbsent( image, img -> { return cache.computeIfAbsent( image, img -> {
// using ImageIcon here makes sure that the image is loaded // using ImageIcon here makes sure that the image is loaded
@@ -157,7 +134,7 @@ public class MultiResolutionImageSupport
{ {
private final Dimension[] dimensions; private final Dimension[] dimensions;
private final Function<Dimension, Image> producer; private final Function<Dimension, Image> producer;
private final HashMap<Dimension, Image> cache = new HashMap<>(); private final IdentityHashMap<Dimension, Image> cache = new IdentityHashMap<>();
ProducerMultiResolutionImage( Dimension[] dimensions, Function<Dimension, Image> producer ) { ProducerMultiResolutionImage( Dimension[] dimensions, Function<Dimension, Image> producer ) {
this.dimensions = dimensions; this.dimensions = dimensions;
@@ -182,16 +159,6 @@ public class MultiResolutionImageSupport
return produceAndCacheImage( dimensions[0] ); return produceAndCacheImage( dimensions[0] );
} }
@Override
public int getWidth( ImageObserver observer ) {
return dimensions[0].width;
}
@Override
public int getHeight( ImageObserver observer ) {
return dimensions[0].height;
}
private Image produceAndCacheImage( Dimension size ) { private Image produceAndCacheImage( Dimension size ) {
return cache.computeIfAbsent( size, size2 -> { return cache.computeIfAbsent( size, size2 -> {
// using ImageIcon here makes sure that the image is loaded // using ImageIcon here makes sure that the image is loaded

View File

@@ -246,7 +246,6 @@ PasswordField.revealIconColor = @foreground
#---- Popup ---- #---- Popup ----
[mac]Popup.roundedBorderWidth = 1
Popup.dropShadowColor = #000 Popup.dropShadowColor = #000
Popup.dropShadowOpacity = 0.25 Popup.dropShadowOpacity = 0.25

View File

@@ -289,7 +289,6 @@ ComboBox.popupInsets = 0,0,0,0
ComboBox.selectionInsets = 0,0,0,0 ComboBox.selectionInsets = 0,0,0,0
ComboBox.selectionArc = 0 ComboBox.selectionArc = 0
ComboBox.borderCornerRadius = $Popup.borderCornerRadius ComboBox.borderCornerRadius = $Popup.borderCornerRadius
[mac]ComboBox.roundedBorderWidth = $Popup.roundedBorderWidth
#---- Component ---- #---- Component ----
@@ -506,7 +505,6 @@ PasswordField.revealIcon = com.formdev.flatlaf.icons.FlatRevealIcon
#---- Popup ---- #---- Popup ----
Popup.borderCornerRadius = 4 Popup.borderCornerRadius = 4
[mac]Popup.roundedBorderWidth = 0
Popup.dropShadowPainted = true Popup.dropShadowPainted = true
Popup.dropShadowInsets = -4,-4,4,4 Popup.dropShadowInsets = -4,-4,4,4
@@ -516,7 +514,6 @@ Popup.dropShadowInsets = -4,-4,4,4
PopupMenu.border = com.formdev.flatlaf.ui.FlatPopupMenuBorder PopupMenu.border = com.formdev.flatlaf.ui.FlatPopupMenuBorder
PopupMenu.borderInsets = 4,1,4,1 PopupMenu.borderInsets = 4,1,4,1
PopupMenu.borderCornerRadius = $Popup.borderCornerRadius PopupMenu.borderCornerRadius = $Popup.borderCornerRadius
[mac]PopupMenu.roundedBorderWidth = $Popup.roundedBorderWidth
PopupMenu.background = @menuBackground PopupMenu.background = @menuBackground
PopupMenu.scrollArrowColor = @buttonArrowColor PopupMenu.scrollArrowColor = @buttonArrowColor
@@ -600,15 +597,10 @@ ScrollBar.allowsAbsolutePositioning = true
#---- ScrollPane ---- #---- ScrollPane ----
ScrollPane.border = com.formdev.flatlaf.ui.FlatScrollPaneBorder ScrollPane.border = com.formdev.flatlaf.ui.FlatBorder
ScrollPane.background = $ScrollBar.track ScrollPane.background = $ScrollBar.track
ScrollPane.fillUpperCorner = true ScrollPane.fillUpperCorner = true
ScrollPane.smoothScrolling = true ScrollPane.smoothScrolling = true
ScrollPane.arc = 0
#ScrollPane.List.arc = -1
#ScrollPane.Table.arc = -1
#ScrollPane.TextComponent.arc = -1
#ScrollPane.Tree.arc = -1
#---- SearchField ---- #---- SearchField ----
@@ -705,8 +697,6 @@ TabbedPane.tabAreaAlignment = leading
TabbedPane.tabAlignment = center TabbedPane.tabAlignment = center
# allowed values: preferred, equal or compact # allowed values: preferred, equal or compact
TabbedPane.tabWidthMode = preferred TabbedPane.tabWidthMode = preferred
# allowed values: none, auto, left or right
TabbedPane.tabRotation = none
# allowed values: underlined or card # allowed values: underlined or card
TabbedPane.tabType = underlined TabbedPane.tabType = underlined
@@ -741,7 +731,7 @@ Table.showVerticalLines = false
Table.showTrailingVerticalLine = false Table.showTrailingVerticalLine = false
Table.consistentHomeEndKeyBehavior = true Table.consistentHomeEndKeyBehavior = true
Table.intercellSpacing = 0,0 Table.intercellSpacing = 0,0
Table.scrollPaneBorder = com.formdev.flatlaf.ui.FlatScrollPaneBorder Table.scrollPaneBorder = com.formdev.flatlaf.ui.FlatBorder
Table.ascendingSortIcon = com.formdev.flatlaf.icons.FlatAscendingSortIcon Table.ascendingSortIcon = com.formdev.flatlaf.icons.FlatAscendingSortIcon
Table.descendingSortIcon = com.formdev.flatlaf.icons.FlatDescendingSortIcon Table.descendingSortIcon = com.formdev.flatlaf.icons.FlatDescendingSortIcon
Table.sortIconColor = @icon Table.sortIconColor = @icon
@@ -912,7 +902,6 @@ ToolTipManager.enableToolTipMode = activeApplication
#---- ToolTip ---- #---- ToolTip ----
ToolTip.borderCornerRadius = $Popup.borderCornerRadius ToolTip.borderCornerRadius = $Popup.borderCornerRadius
[mac]ToolTip.roundedBorderWidth = $Popup.roundedBorderWidth
#---- Tree ---- #---- Tree ----

View File

@@ -295,8 +295,3 @@ ToggleButton.disabledBackground = $Button.disabledBackground
ToggleButton.selectedBackground = lighten($ToggleButton.background,20%,derived) ToggleButton.selectedBackground = lighten($ToggleButton.background,20%,derived)
ToggleButton.toolbar.selectedBackground = #fff3 ToggleButton.toolbar.selectedBackground = #fff3
#---- ToolBar ----
ToolBar.hoverButtonGroupArc = 14

View File

@@ -291,8 +291,3 @@ TextPane.selectionForeground = @textSelectionForeground
#---- ToggleButton ---- #---- ToggleButton ----
ToggleButton.disabledBackground = $Button.disabledBackground ToggleButton.disabledBackground = $Button.disabledBackground
#---- ToolBar ----
ToolBar.hoverButtonGroupArc = 14

View File

@@ -601,7 +601,7 @@ public class TestFlatStyleableInfo
); );
// border // border
flatScrollPaneBorder( expected ); flatBorder( expected );
assertMapEquals( expected, ui.getStyleableInfos( c ) ); assertMapEquals( expected, ui.getStyleableInfos( c ) );
} }
@@ -689,9 +689,6 @@ public class TestFlatStyleableInfo
Map<String, Class<?>> expected = expectedMap( Map<String, Class<?>> expected = expectedMap(
"arrowType", String.class, "arrowType", String.class,
"draggingColor", Color.class,
"hoverColor", Color.class,
"pressedColor", Color.class,
"oneTouchArrowColor", Color.class, "oneTouchArrowColor", Color.class,
"oneTouchHoverArrowColor", Color.class, "oneTouchHoverArrowColor", Color.class,
"oneTouchPressedArrowColor", Color.class, "oneTouchPressedArrowColor", Color.class,
@@ -755,7 +752,6 @@ public class TestFlatStyleableInfo
"tabAreaAlignment", String.class, "tabAreaAlignment", String.class,
"tabAlignment", String.class, "tabAlignment", String.class,
"tabWidthMode", String.class, "tabWidthMode", String.class,
"tabRotation", String.class,
"arrowType", String.class, "arrowType", String.class,
"buttonInsets", Insets.class, "buttonInsets", Insets.class,
@@ -929,10 +925,7 @@ public class TestFlatStyleableInfo
"hoverButtonGroupBackground", Color.class, "hoverButtonGroupBackground", Color.class,
"borderMargins", Insets.class, "borderMargins", Insets.class,
"gripColor", Color.class, "gripColor", Color.class
"separatorWidth", Integer.class,
"separatorColor", Color.class
); );
assertMapEquals( expected, ui.getStyleableInfos( c ) ); assertMapEquals( expected, ui.getStyleableInfos( c ) );
@@ -1012,23 +1005,17 @@ public class TestFlatStyleableInfo
expectedMap( expected, expectedMap( expected,
"arc", int.class, "arc", int.class,
"roundRect", Boolean.class "roundRect", Boolean.class
); );
} }
private void flatScrollPaneBorder( Map<String, Class<?>> expected ) {
flatBorder( expected );
expectedMap( expected,
"arc", int.class
);
}
private void flatTextBorder( Map<String, Class<?>> expected ) { private void flatTextBorder( Map<String, Class<?>> expected ) {
flatBorder( expected ); flatBorder( expected );
expectedMap( expected, expectedMap( expected,
"arc", int.class, "arc", int.class,
"roundRect", Boolean.class "roundRect", Boolean.class
); );
} }

View File

@@ -625,7 +625,7 @@ public class TestFlatStyleableValue
FlatScrollPaneUI ui = (FlatScrollPaneUI) c.getUI(); FlatScrollPaneUI ui = (FlatScrollPaneUI) c.getUI();
// border // border
flatScrollPaneBorder( c, ui ); flatBorder( c, ui );
testBoolean( c, ui, "showButtons", true ); testBoolean( c, ui, "showButtons", true );
} }
@@ -699,9 +699,6 @@ public class TestFlatStyleableValue
FlatSplitPaneUI ui = (FlatSplitPaneUI) c.getUI(); FlatSplitPaneUI ui = (FlatSplitPaneUI) c.getUI();
testString( c, ui, "arrowType", "chevron" ); testString( c, ui, "arrowType", "chevron" );
testColor( c, ui, "draggingColor", 0x123456 );
testColor( c, ui, "hoverColor", 0x123456 );
testColor( c, ui, "pressedColor", 0x123456 );
testColor( c, ui, "oneTouchArrowColor", 0x123456 ); testColor( c, ui, "oneTouchArrowColor", 0x123456 );
testColor( c, ui, "oneTouchHoverArrowColor", 0x123456 ); testColor( c, ui, "oneTouchHoverArrowColor", 0x123456 );
testColor( c, ui, "oneTouchPressedArrowColor", 0x123456 ); testColor( c, ui, "oneTouchPressedArrowColor", 0x123456 );
@@ -761,7 +758,6 @@ public class TestFlatStyleableValue
testString( c, ui, "tabAreaAlignment", "leading" ); testString( c, ui, "tabAreaAlignment", "leading" );
testString( c, ui, "tabAlignment", "center" ); testString( c, ui, "tabAlignment", "center" );
testString( c, ui, "tabWidthMode", "preferred" ); testString( c, ui, "tabWidthMode", "preferred" );
testString( c, ui, "tabRotation", "none" );
testString( c, ui, "arrowType", "chevron" ); testString( c, ui, "arrowType", "chevron" );
testInsets( c, ui, "buttonInsets", 1,2,3,4 ); testInsets( c, ui, "buttonInsets", 1,2,3,4 );
@@ -906,9 +902,6 @@ public class TestFlatStyleableValue
testInsets( c, ui, "borderMargins", 1,2,3,4 ); testInsets( c, ui, "borderMargins", 1,2,3,4 );
testColor( c, ui, "gripColor", 0x123456 ); testColor( c, ui, "gripColor", 0x123456 );
testInteger( c, ui, "separatorWidth", 123 );
testColor( c, ui, "separatorColor", 0x123456 );
} }
@Test @Test
@@ -972,19 +965,15 @@ public class TestFlatStyleableValue
flatBorder( c, ui ); flatBorder( c, ui );
testInteger( c, ui, "arc", 123 ); testInteger( c, ui, "arc", 123 );
testBoolean( c, ui, "roundRect", true ); testBoolean( c, ui, "roundRect", true );
} }
private void flatScrollPaneBorder( JComponent c, StyleableUI ui ) {
flatBorder( c, ui );
testInteger( c, ui, "arc", 123 );
}
private void flatTextBorder( JComponent c, StyleableUI ui ) { private void flatTextBorder( JComponent c, StyleableUI ui ) {
flatBorder( c, ui ); flatBorder( c, ui );
testInteger( c, ui, "arc", 123 ); testInteger( c, ui, "arc", 123 );
testBoolean( c, ui, "roundRect", true ); testBoolean( c, ui, "roundRect", true );
} }
@@ -1045,17 +1034,6 @@ public class TestFlatStyleableValue
// FlatRoundBorder extends FlatBorder // FlatRoundBorder extends FlatBorder
flatBorder( border ); flatBorder( border );
testValue( border, "arc", 6 );
testValue( border, "roundRect", true );
}
@Test
void flatScrollPaneBorder() {
FlatScrollPaneBorder border = new FlatScrollPaneBorder();
// FlatScrollPaneBorder extends FlatBorder
flatBorder( border );
testValue( border, "arc", 6 ); testValue( border, "arc", 6 );
} }
@@ -1067,7 +1045,6 @@ public class TestFlatStyleableValue
flatBorder( border ); flatBorder( border );
testValue( border, "arc", 6 ); testValue( border, "arc", 6 );
testValue( border, "roundRect", true );
} }
@Test @Test

View File

@@ -760,7 +760,7 @@ public class TestFlatStyling
FlatScrollPaneUI ui = (FlatScrollPaneUI) c.getUI(); FlatScrollPaneUI ui = (FlatScrollPaneUI) c.getUI();
// border // border
flatScrollPaneBorder( style -> ui.applyStyle( style ) ); flatBorder( style -> ui.applyStyle( style ) );
ui.applyStyle( "showButtons: true" ); ui.applyStyle( "showButtons: true" );
@@ -870,9 +870,6 @@ public class TestFlatStyling
FlatSplitPaneUI ui = (FlatSplitPaneUI) c.getUI(); FlatSplitPaneUI ui = (FlatSplitPaneUI) c.getUI();
ui.applyStyle( "arrowType: chevron" ); ui.applyStyle( "arrowType: chevron" );
ui.applyStyle( "draggingColor: #fff" );
ui.applyStyle( "hoverColor: #fff" );
ui.applyStyle( "pressedColor: #fff" );
ui.applyStyle( "oneTouchArrowColor: #fff" ); ui.applyStyle( "oneTouchArrowColor: #fff" );
ui.applyStyle( "oneTouchHoverArrowColor: #fff" ); ui.applyStyle( "oneTouchHoverArrowColor: #fff" );
ui.applyStyle( "oneTouchPressedArrowColor: #fff" ); ui.applyStyle( "oneTouchPressedArrowColor: #fff" );
@@ -940,7 +937,6 @@ public class TestFlatStyling
ui.applyStyle( "tabAreaAlignment: leading" ); ui.applyStyle( "tabAreaAlignment: leading" );
ui.applyStyle( "tabAlignment: center" ); ui.applyStyle( "tabAlignment: center" );
ui.applyStyle( "tabWidthMode: preferred" ); ui.applyStyle( "tabWidthMode: preferred" );
ui.applyStyle( "tabRotation: none" );
ui.applyStyle( "arrowType: chevron" ); ui.applyStyle( "arrowType: chevron" );
ui.applyStyle( "buttonInsets: 1,2,3,4" ); ui.applyStyle( "buttonInsets: 1,2,3,4" );
@@ -1150,9 +1146,6 @@ public class TestFlatStyling
ui.applyStyle( "borderMargins: 1,2,3,4" ); ui.applyStyle( "borderMargins: 1,2,3,4" );
ui.applyStyle( "gripColor: #fff" ); ui.applyStyle( "gripColor: #fff" );
ui.applyStyle( "separatorWidth: 6" );
ui.applyStyle( "separatorColor: #fff" );
// JComponent properties // JComponent properties
ui.applyStyle( "background: #fff" ); ui.applyStyle( "background: #fff" );
ui.applyStyle( "foreground: #fff" ); ui.applyStyle( "foreground: #fff" );
@@ -1241,19 +1234,15 @@ public class TestFlatStyling
flatBorder( applyStyle ); flatBorder( applyStyle );
applyStyle.accept( "arc: 6" ); applyStyle.accept( "arc: 6" );
applyStyle.accept( "roundRect: true" ); applyStyle.accept( "roundRect: true" );
} }
private void flatScrollPaneBorder( Consumer<String> applyStyle ) {
flatBorder( applyStyle );
applyStyle.accept( "arc: 6" );
}
private void flatTextBorder( Consumer<String> applyStyle ) { private void flatTextBorder( Consumer<String> applyStyle ) {
flatBorder( applyStyle ); flatBorder( applyStyle );
applyStyle.accept( "arc: 6" ); applyStyle.accept( "arc: 6" );
applyStyle.accept( "roundRect: true" ); applyStyle.accept( "roundRect: true" );
} }

View File

@@ -28,8 +28,6 @@ import javax.swing.plaf.metal.MetalLookAndFeel;
import javax.swing.plaf.nimbus.NimbusLookAndFeel; import javax.swing.plaf.nimbus.NimbusLookAndFeel;
import com.formdev.flatlaf.*; import com.formdev.flatlaf.*;
import com.formdev.flatlaf.extras.FlatAnimatedLafChange; import com.formdev.flatlaf.extras.FlatAnimatedLafChange;
import com.formdev.flatlaf.themes.FlatMacDarkLaf;
import com.formdev.flatlaf.themes.FlatMacLightLaf;
import com.formdev.flatlaf.util.LoggingFacade; import com.formdev.flatlaf.util.LoggingFacade;
import com.formdev.flatlaf.util.SystemInfo; import com.formdev.flatlaf.util.SystemInfo;
import com.formdev.flatlaf.util.UIScale; import com.formdev.flatlaf.util.UIScale;
@@ -68,8 +66,6 @@ class ControlBar
lafModel.addElement( new LookAndFeelInfo( "FlatLaf Dark (F2)", FlatDarkLaf.class.getName() ) ); lafModel.addElement( new LookAndFeelInfo( "FlatLaf Dark (F2)", FlatDarkLaf.class.getName() ) );
lafModel.addElement( new LookAndFeelInfo( "FlatLaf IntelliJ (F3)", FlatIntelliJLaf.class.getName() ) ); lafModel.addElement( new LookAndFeelInfo( "FlatLaf IntelliJ (F3)", FlatIntelliJLaf.class.getName() ) );
lafModel.addElement( new LookAndFeelInfo( "FlatLaf Darcula (F4)", FlatDarculaLaf.class.getName() ) ); lafModel.addElement( new LookAndFeelInfo( "FlatLaf Darcula (F4)", FlatDarculaLaf.class.getName() ) );
lafModel.addElement( new LookAndFeelInfo( "FlatLaf macOS Light (F5)", FlatMacLightLaf.class.getName() ) );
lafModel.addElement( new LookAndFeelInfo( "FlatLaf macOS Dark (F6)", FlatMacDarkLaf.class.getName() ) );
UIManager.LookAndFeelInfo[] lookAndFeels = UIManager.getInstalledLookAndFeels(); UIManager.LookAndFeelInfo[] lookAndFeels = UIManager.getInstalledLookAndFeels();
for( UIManager.LookAndFeelInfo lookAndFeel : lookAndFeels ) { for( UIManager.LookAndFeelInfo lookAndFeel : lookAndFeels ) {
@@ -131,8 +127,6 @@ class ControlBar
registerSwitchToLookAndFeel( KeyEvent.VK_F2, FlatDarkLaf.class.getName() ); registerSwitchToLookAndFeel( KeyEvent.VK_F2, FlatDarkLaf.class.getName() );
registerSwitchToLookAndFeel( KeyEvent.VK_F3, FlatIntelliJLaf.class.getName() ); registerSwitchToLookAndFeel( KeyEvent.VK_F3, FlatIntelliJLaf.class.getName() );
registerSwitchToLookAndFeel( KeyEvent.VK_F4, FlatDarculaLaf.class.getName() ); registerSwitchToLookAndFeel( KeyEvent.VK_F4, FlatDarculaLaf.class.getName() );
registerSwitchToLookAndFeel( KeyEvent.VK_F5, FlatMacLightLaf.class.getName() );
registerSwitchToLookAndFeel( KeyEvent.VK_F6, FlatMacDarkLaf.class.getName() );
if( SystemInfo.isWindows ) if( SystemInfo.isWindows )
registerSwitchToLookAndFeel( KeyEvent.VK_F9, "com.sun.java.swing.plaf.windows.WindowsLookAndFeel" ); registerSwitchToLookAndFeel( KeyEvent.VK_F9, "com.sun.java.swing.plaf.windows.WindowsLookAndFeel" );

View File

@@ -18,6 +18,7 @@ package com.formdev.flatlaf.demo;
import java.awt.Color; import java.awt.Color;
import java.awt.Dimension; import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.datatransfer.DataFlavor; import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.StringSelection; import java.awt.datatransfer.StringSelection;
import java.awt.datatransfer.Transferable; import java.awt.datatransfer.Transferable;
@@ -115,16 +116,15 @@ class DataComponentsPanel
table1.setGridColor( redGridColorCheckBox.isSelected() ? Color.red : UIManager.getColor( "Table.gridColor" ) ); table1.setGridColor( redGridColorCheckBox.isSelected() ? Color.red : UIManager.getColor( "Table.gridColor" ) );
} }
private void showHorizontalLinesPropertyChange() { @Override
showHorizontalLinesCheckBox.setSelected( table1.getShowHorizontalLines() ); public void updateUI() {
} super.updateUI();
private void showVerticalLinesPropertyChange() { EventQueue.invokeLater( () -> {
showVerticalLinesCheckBox.setSelected( table1.getShowVerticalLines() ); showHorizontalLinesChanged();
} showVerticalLinesChanged();
intercellSpacingChanged();
private void intercellSpacingPropertyChange() { } );
intercellSpacingCheckBox.setSelected( table1.getRowMargin() != 0 );
} }
@SuppressWarnings( { "unchecked", "rawtypes" } ) @SuppressWarnings( { "unchecked", "rawtypes" } )
@@ -333,10 +333,10 @@ class DataComponentsPanel
"Not editable", "Text", "Combo", "Combo Editable", "Integer", "Boolean" "Not editable", "Text", "Combo", "Combo Editable", "Integer", "Boolean"
} }
) { ) {
Class<?>[] columnTypes = { Class<?>[] columnTypes = new Class<?>[] {
Object.class, Object.class, String.class, String.class, Integer.class, Boolean.class Object.class, Object.class, String.class, String.class, Integer.class, Boolean.class
}; };
boolean[] columnEditable = { boolean[] columnEditable = new boolean[] {
false, true, true, true, true, true false, true, true, true, true, true
}; };
@Override @Override
@@ -383,9 +383,6 @@ class DataComponentsPanel
} }
table1.setAutoCreateRowSorter(true); table1.setAutoCreateRowSorter(true);
table1.setComponentPopupMenu(popupMenu2); table1.setComponentPopupMenu(popupMenu2);
table1.addPropertyChangeListener("showHorizontalLines", e -> showHorizontalLinesPropertyChange());
table1.addPropertyChangeListener("showVerticalLines", e -> showVerticalLinesPropertyChange());
table1.addPropertyChangeListener("rowMargin", e -> intercellSpacingPropertyChange());
scrollPane5.setViewportView(table1); scrollPane5.setViewportView(table1);
} }
add(scrollPane5, "cell 1 3 3 1,width 300"); add(scrollPane5, "cell 1 3 3 1,width 300");

View File

@@ -1,4 +1,4 @@
JFDML JFormDesigner: "8.2.0.0.331" Java: "21" encoding: "UTF-8" JFDML JFormDesigner: "8.0.0.0.194" Java: "17.0.2" encoding: "UTF-8"
new FormModel { new FormModel {
contentType: "form/swing" contentType: "form/swing"
@@ -333,9 +333,6 @@ new FormModel {
auxiliary() { auxiliary() {
"JavaCodeGenerator.variableLocal": false "JavaCodeGenerator.variableLocal": false
} }
addEvent( new FormEvent( "java.beans.PropertyChangeListener", "propertyChange", "showHorizontalLinesPropertyChange", false, "showHorizontalLines" ) )
addEvent( new FormEvent( "java.beans.PropertyChangeListener", "propertyChange", "showVerticalLinesPropertyChange", false, "showVerticalLines" ) )
addEvent( new FormEvent( "java.beans.PropertyChangeListener", "propertyChange", "intercellSpacingPropertyChange", false, "rowMargin" ) )
} ) } )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 3 3 1,width 300" "value": "cell 1 3 3 1,width 300"

View File

@@ -33,6 +33,7 @@ import com.formdev.flatlaf.FlatDarkLaf;
import com.formdev.flatlaf.FlatIntelliJLaf; import com.formdev.flatlaf.FlatIntelliJLaf;
import com.formdev.flatlaf.FlatLaf; import com.formdev.flatlaf.FlatLaf;
import com.formdev.flatlaf.FlatLightLaf; import com.formdev.flatlaf.FlatLightLaf;
import com.formdev.flatlaf.FlatSystemProperties;
import com.formdev.flatlaf.demo.HintManager.Hint; import com.formdev.flatlaf.demo.HintManager.Hint;
import com.formdev.flatlaf.demo.extras.*; import com.formdev.flatlaf.demo.extras.*;
import com.formdev.flatlaf.demo.intellijthemes.*; import com.formdev.flatlaf.demo.intellijthemes.*;
@@ -46,6 +47,7 @@ import com.formdev.flatlaf.icons.FlatAbstractIcon;
import com.formdev.flatlaf.themes.FlatMacDarkLaf; import com.formdev.flatlaf.themes.FlatMacDarkLaf;
import com.formdev.flatlaf.themes.FlatMacLightLaf; import com.formdev.flatlaf.themes.FlatMacLightLaf;
import com.formdev.flatlaf.extras.FlatSVGUtils; import com.formdev.flatlaf.extras.FlatSVGUtils;
import com.formdev.flatlaf.ui.JBRCustomDecorations;
import com.formdev.flatlaf.util.ColorFunctions; import com.formdev.flatlaf.util.ColorFunctions;
import com.formdev.flatlaf.util.FontUtils; import com.formdev.flatlaf.util.FontUtils;
import com.formdev.flatlaf.util.LoggingFacade; import com.formdev.flatlaf.util.LoggingFacade;
@@ -265,6 +267,18 @@ class DemoFrame
repaint(); repaint();
} }
private void animationChanged() {
boolean enabled = animationMenuItem.isSelected();
System.setProperty( FlatSystemProperties.ANIMATION, Boolean.toString( enabled ) );
smoothScrollingMenuItem.setEnabled( enabled );
animatedLafChangeMenuItem.setEnabled( enabled );
}
private void smoothScrollingChanged() {
UIManager.put( "ScrollPane.smoothScrolling", smoothScrollingMenuItem.isSelected() );
}
private void animatedLafChangeChanged() { private void animatedLafChangeChanged() {
System.setProperty( "flatlaf.animatedLafChange", String.valueOf( animatedLafChangeMenuItem.isSelected() ) ); System.setProperty( "flatlaf.animatedLafChange", String.valueOf( animatedLafChangeMenuItem.isSelected() ) );
} }
@@ -504,6 +518,8 @@ class DemoFrame
showTitleBarIconMenuItem = new JCheckBoxMenuItem(); showTitleBarIconMenuItem = new JCheckBoxMenuItem();
underlineMenuSelectionMenuItem = new JCheckBoxMenuItem(); underlineMenuSelectionMenuItem = new JCheckBoxMenuItem();
alwaysShowMnemonicsMenuItem = new JCheckBoxMenuItem(); alwaysShowMnemonicsMenuItem = new JCheckBoxMenuItem();
animationMenuItem = new JCheckBoxMenuItem();
smoothScrollingMenuItem = new JCheckBoxMenuItem();
animatedLafChangeMenuItem = new JCheckBoxMenuItem(); animatedLafChangeMenuItem = new JCheckBoxMenuItem();
JMenuItem showHintsMenuItem = new JMenuItem(); JMenuItem showHintsMenuItem = new JMenuItem();
JMenuItem showUIDefaultsInspectorMenuItem = new JMenuItem(); JMenuItem showUIDefaultsInspectorMenuItem = new JMenuItem();
@@ -791,6 +807,19 @@ class DemoFrame
alwaysShowMnemonicsMenuItem.setText("Always show mnemonics"); alwaysShowMnemonicsMenuItem.setText("Always show mnemonics");
alwaysShowMnemonicsMenuItem.addActionListener(e -> alwaysShowMnemonics()); alwaysShowMnemonicsMenuItem.addActionListener(e -> alwaysShowMnemonics());
optionsMenu.add(alwaysShowMnemonicsMenuItem); optionsMenu.add(alwaysShowMnemonicsMenuItem);
optionsMenu.addSeparator();
//---- animationMenuItem ----
animationMenuItem.setText("Animation");
animationMenuItem.setSelected(true);
animationMenuItem.addActionListener(e -> animationChanged());
optionsMenu.add(animationMenuItem);
//---- smoothScrollingMenuItem ----
smoothScrollingMenuItem.setText("Smooth Scrolling");
smoothScrollingMenuItem.setSelected(true);
smoothScrollingMenuItem.addActionListener(e -> smoothScrollingChanged());
optionsMenu.add(smoothScrollingMenuItem);
//---- animatedLafChangeMenuItem ---- //---- animatedLafChangeMenuItem ----
animatedLafChangeMenuItem.setText("Animated Laf Change"); animatedLafChangeMenuItem.setText("Animated Laf Change");
@@ -930,6 +959,12 @@ class DemoFrame
menuBarEmbeddedCheckBoxMenuItem.setSelected( UIManager.getBoolean( "TitlePane.menuBarEmbedded" ) ); menuBarEmbeddedCheckBoxMenuItem.setSelected( UIManager.getBoolean( "TitlePane.menuBarEmbedded" ) );
unifiedTitleBarMenuItem.setSelected( UIManager.getBoolean( "TitlePane.unifiedBackground" ) ); unifiedTitleBarMenuItem.setSelected( UIManager.getBoolean( "TitlePane.unifiedBackground" ) );
showTitleBarIconMenuItem.setSelected( UIManager.getBoolean( "TitlePane.showIcon" ) ); showTitleBarIconMenuItem.setSelected( UIManager.getBoolean( "TitlePane.showIcon" ) );
if( JBRCustomDecorations.isSupported() ) {
// If the JetBrains Runtime is used, it forces the use of it's own custom
// window decoration, which can not disabled.
windowDecorationsCheckBoxMenuItem.setEnabled( false );
}
} else { } else {
unsupported( windowDecorationsCheckBoxMenuItem ); unsupported( windowDecorationsCheckBoxMenuItem );
unsupported( menuBarEmbeddedCheckBoxMenuItem ); unsupported( menuBarEmbeddedCheckBoxMenuItem );
@@ -974,6 +1009,8 @@ class DemoFrame
private JCheckBoxMenuItem showTitleBarIconMenuItem; private JCheckBoxMenuItem showTitleBarIconMenuItem;
private JCheckBoxMenuItem underlineMenuSelectionMenuItem; private JCheckBoxMenuItem underlineMenuSelectionMenuItem;
private JCheckBoxMenuItem alwaysShowMnemonicsMenuItem; private JCheckBoxMenuItem alwaysShowMnemonicsMenuItem;
private JCheckBoxMenuItem animationMenuItem;
private JCheckBoxMenuItem smoothScrollingMenuItem;
private JCheckBoxMenuItem animatedLafChangeMenuItem; private JCheckBoxMenuItem animatedLafChangeMenuItem;
private JMenuItem aboutMenuItem; private JMenuItem aboutMenuItem;
private JToolBar toolBar; private JToolBar toolBar;

View File

@@ -418,6 +418,27 @@ new FormModel {
} }
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "alwaysShowMnemonics", false ) ) addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "alwaysShowMnemonics", false ) )
} ) } )
add( new FormComponent( "javax.swing.JPopupMenu$Separator" ) {
name: "separator9"
} )
add( new FormComponent( "javax.swing.JCheckBoxMenuItem" ) {
name: "animationMenuItem"
"text": "Animation"
"selected": true
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "animationChanged", false ) )
} )
add( new FormComponent( "javax.swing.JCheckBoxMenuItem" ) {
name: "smoothScrollingMenuItem"
"text": "Smooth Scrolling"
"selected": true
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "smoothScrollingChanged", false ) )
} )
add( new FormComponent( "javax.swing.JCheckBoxMenuItem" ) { add( new FormComponent( "javax.swing.JCheckBoxMenuItem" ) {
name: "animatedLafChangeMenuItem" name: "animatedLafChangeMenuItem"
"text": "Animated Laf Change" "text": "Animated Laf Change"

View File

@@ -34,7 +34,7 @@ public class ScrollablePanel
{ {
@Override @Override
public Dimension getPreferredScrollableViewportSize() { public Dimension getPreferredScrollableViewportSize() {
return new Dimension( getPreferredSize().width, UIScale.scale( 400 ) ); return UIScale.scale( new Dimension( 400, 400 ) );
} }
@Override @Override
@@ -49,7 +49,7 @@ public class ScrollablePanel
@Override @Override
public boolean getScrollableTracksViewportWidth() { public boolean getScrollableTracksViewportWidth() {
return true; return false;
} }
@Override @Override

View File

@@ -204,7 +204,7 @@ class TabsPanel
private void closeButtonStyleChanged() { private void closeButtonStyleChanged() {
// WARNING: // WARNING:
// Do not use this trick to style individual tabbed panes in own code. // Do not use this trick to style individual tabbed panes in own code.
// Instead, use one styling for all tabbed panes in your application. // Instead use one styling for all tabbed panes in your application.
if( circleCloseButton.isSelected() ) { if( circleCloseButton.isSelected() ) {
UIManager.put( "TabbedPane.closeArc", 999 ); UIManager.put( "TabbedPane.closeArc", 999 );
UIManager.put( "TabbedPane.closeCrossFilledSize", 5.5f ); UIManager.put( "TabbedPane.closeCrossFilledSize", 5.5f );
@@ -313,14 +313,6 @@ class TabsPanel
putTabbedPanesClientProperty( TABBED_PANE_SHOW_TAB_SEPARATORS, showTabSeparators ); putTabbedPanesClientProperty( TABBED_PANE_SHOW_TAB_SEPARATORS, showTabSeparators );
} }
private void tabRotationChanged() {
String tabRotation = rotationAutoButton.isSelected() ? TABBED_PANE_TAB_ROTATION_AUTO
: rotationLeftButton.isSelected() ? TABBED_PANE_TAB_ROTATION_LEFT
: rotationRightButton.isSelected() ? TABBED_PANE_TAB_ROTATION_RIGHT
: null;
putTabbedPanesClientProperty( TABBED_PANE_TAB_ROTATION, tabRotation );
}
private void putTabbedPanesClientProperty( String key, Object value ) { private void putTabbedPanesClientProperty( String key, Object value ) {
updateTabbedPanesRecur( this, tabbedPane -> tabbedPane.putClientProperty( key, value ) ); updateTabbedPanesRecur( this, tabbedPane -> tabbedPane.putClientProperty( key, value ) );
} }
@@ -339,8 +331,6 @@ class TabsPanel
private void initComponents() { private void initComponents() {
// JFormDesigner - Component initialization - DO NOT MODIFY //GEN-BEGIN:initComponents // JFormDesigner - Component initialization - DO NOT MODIFY //GEN-BEGIN:initComponents
JScrollPane tabsScrollPane = new JScrollPane();
ScrollablePanel panel6 = new ScrollablePanel();
JPanel panel1 = new JPanel(); JPanel panel1 = new JPanel();
JLabel tabPlacementLabel = new JLabel(); JLabel tabPlacementLabel = new JLabel();
tabPlacementToolBar = new JToolBar(); tabPlacementToolBar = new JToolBar();
@@ -407,369 +397,344 @@ class TabsPanel
scrollAsNeededSingleButton = new JToggleButton(); scrollAsNeededSingleButton = new JToggleButton();
scrollAsNeededButton = new JToggleButton(); scrollAsNeededButton = new JToggleButton();
scrollNeverButton = new JToggleButton(); scrollNeverButton = new JToggleButton();
tabsPopupPolicyLabel = new JLabel();
tabsPopupPolicyToolBar = new JToolBar();
popupAsNeededButton = new JToggleButton();
popupNeverButton = new JToggleButton();
showTabSeparatorsCheckBox = new JCheckBox();
scrollButtonsPlacementLabel = new JLabel(); scrollButtonsPlacementLabel = new JLabel();
scrollButtonsPlacementToolBar = new JToolBar(); scrollButtonsPlacementToolBar = new JToolBar();
scrollBothButton = new JToggleButton(); scrollBothButton = new JToggleButton();
scrollTrailingButton = new JToggleButton(); scrollTrailingButton = new JToggleButton();
showTabSeparatorsCheckBox = new JCheckBox();
tabsPopupPolicyLabel = new JLabel();
tabsPopupPolicyToolBar = new JToolBar();
popupAsNeededButton = new JToggleButton();
popupNeverButton = new JToggleButton();
tabTypeLabel = new JLabel(); tabTypeLabel = new JLabel();
tabTypeToolBar = new JToolBar(); tabTypeToolBar = new JToolBar();
underlinedTabTypeButton = new JToggleButton(); underlinedTabTypeButton = new JToggleButton();
cardTabTypeButton = new JToggleButton(); cardTabTypeButton = new JToggleButton();
tabRotationLabel = new JLabel();
tabRotationToolBar = new JToolBar();
rotationNoneButton = new JToggleButton();
rotationAutoButton = new JToggleButton();
rotationLeftButton = new JToggleButton();
rotationRightButton = new JToggleButton();
//======== this ======== //======== this ========
setLayout(new MigLayout( setLayout(new MigLayout(
"insets 0,hidemode 3", "insets dialog,hidemode 3",
// columns // columns
"[grow,fill]", "[grow,fill]para" +
"[fill]para" +
"[fill]",
// rows // rows
"[grow,fill]0" + "[grow,fill]para" +
"[]0" + "[]" +
"[]")); "[]"));
//======== tabsScrollPane ======== //======== panel1 ========
{ {
tabsScrollPane.setBorder(BorderFactory.createEmptyBorder()); panel1.setLayout(new MigLayout(
"insets 0,hidemode 3",
// columns
"[grow,fill]",
// rows
"[]" +
"[fill]para" +
"[]0" +
"[]" +
"[]para" +
"[]" +
"[]para" +
"[]" +
"[]"));
//======== panel6 ======== //---- tabPlacementLabel ----
tabPlacementLabel.setText("Tab placement");
tabPlacementLabel.putClientProperty("FlatLaf.styleClass", "h3");
panel1.add(tabPlacementLabel, "cell 0 0");
//======== tabPlacementToolBar ========
{ {
panel6.setLayout(new MigLayout( tabPlacementToolBar.setFloatable(false);
"insets dialog,hidemode 3", tabPlacementToolBar.setBorder(BorderFactory.createEmptyBorder());
//---- topPlacementButton ----
topPlacementButton.setText("top");
topPlacementButton.setSelected(true);
topPlacementButton.putClientProperty("FlatLaf.styleClass", "small");
topPlacementButton.addActionListener(e -> tabPlacementChanged());
tabPlacementToolBar.add(topPlacementButton);
//---- bottomPlacementButton ----
bottomPlacementButton.setText("bottom");
bottomPlacementButton.putClientProperty("FlatLaf.styleClass", "small");
bottomPlacementButton.addActionListener(e -> tabPlacementChanged());
tabPlacementToolBar.add(bottomPlacementButton);
//---- leftPlacementButton ----
leftPlacementButton.setText("left");
leftPlacementButton.putClientProperty("FlatLaf.styleClass", "small");
leftPlacementButton.addActionListener(e -> tabPlacementChanged());
tabPlacementToolBar.add(leftPlacementButton);
//---- rightPlacementButton ----
rightPlacementButton.setText("right");
rightPlacementButton.putClientProperty("FlatLaf.styleClass", "small");
rightPlacementButton.addActionListener(e -> tabPlacementChanged());
tabPlacementToolBar.add(rightPlacementButton);
tabPlacementToolBar.addSeparator();
//---- scrollButton ----
scrollButton.setText("scroll");
scrollButton.putClientProperty("FlatLaf.styleClass", "small");
scrollButton.addActionListener(e -> scrollChanged());
tabPlacementToolBar.add(scrollButton);
//---- borderButton ----
borderButton.setText("border");
borderButton.putClientProperty("FlatLaf.styleClass", "small");
borderButton.addActionListener(e -> borderChanged());
tabPlacementToolBar.add(borderButton);
}
panel1.add(tabPlacementToolBar, "cell 0 0,alignx right,growx 0");
panel1.add(tabPlacementTabbedPane, "cell 0 1,width 300:300,height 100:100");
//---- tabLayoutLabel ----
tabLayoutLabel.setText("Tab layout");
tabLayoutLabel.putClientProperty("FlatLaf.styleClass", "h3");
panel1.add(tabLayoutLabel, "cell 0 2");
//======== tabLayoutToolBar ========
{
tabLayoutToolBar.setFloatable(false);
tabLayoutToolBar.setBorder(BorderFactory.createEmptyBorder());
//---- scrollTabLayoutButton ----
scrollTabLayoutButton.setText("scroll");
scrollTabLayoutButton.setSelected(true);
scrollTabLayoutButton.putClientProperty("FlatLaf.styleClass", "small");
scrollTabLayoutButton.addActionListener(e -> tabLayoutChanged());
tabLayoutToolBar.add(scrollTabLayoutButton);
//---- wrapTabLayoutButton ----
wrapTabLayoutButton.setText("wrap");
wrapTabLayoutButton.putClientProperty("FlatLaf.styleClass", "small");
wrapTabLayoutButton.addActionListener(e -> tabLayoutChanged());
tabLayoutToolBar.add(wrapTabLayoutButton);
}
panel1.add(tabLayoutToolBar, "cell 0 2,alignx right,growx 0");
//---- scrollLayoutNoteLabel ----
scrollLayoutNoteLabel.setText("(use mouse wheel to scroll; arrow button shows hidden tabs)");
scrollLayoutNoteLabel.setEnabled(false);
scrollLayoutNoteLabel.putClientProperty("FlatLaf.styleClass", "small");
panel1.add(scrollLayoutNoteLabel, "cell 0 3");
//---- wrapLayoutNoteLabel ----
wrapLayoutNoteLabel.setText("(probably better to use scroll layout?)");
wrapLayoutNoteLabel.setEnabled(false);
wrapLayoutNoteLabel.putClientProperty("FlatLaf.styleClass", "small");
panel1.add(wrapLayoutNoteLabel, "cell 0 3");
panel1.add(scrollLayoutTabbedPane, "cell 0 4");
panel1.add(wrapLayoutTabbedPane, "cell 0 4,width 100:100,height pref*2px");
//---- closableTabsLabel ----
closableTabsLabel.setText("Closable tabs");
closableTabsLabel.putClientProperty("FlatLaf.styleClass", "h3");
panel1.add(closableTabsLabel, "cell 0 5");
//======== closableTabsToolBar ========
{
closableTabsToolBar.setFloatable(false);
closableTabsToolBar.setBorder(BorderFactory.createEmptyBorder());
//---- squareCloseButton ----
squareCloseButton.setText("square");
squareCloseButton.setSelected(true);
squareCloseButton.putClientProperty("FlatLaf.styleClass", "small");
squareCloseButton.addActionListener(e -> closeButtonStyleChanged());
closableTabsToolBar.add(squareCloseButton);
//---- circleCloseButton ----
circleCloseButton.setText("circle");
circleCloseButton.putClientProperty("FlatLaf.styleClass", "small");
circleCloseButton.addActionListener(e -> closeButtonStyleChanged());
closableTabsToolBar.add(circleCloseButton);
//---- redCrossCloseButton ----
redCrossCloseButton.setText("red cross");
redCrossCloseButton.putClientProperty("FlatLaf.styleClass", "small");
redCrossCloseButton.addActionListener(e -> closeButtonStyleChanged());
closableTabsToolBar.add(redCrossCloseButton);
}
panel1.add(closableTabsToolBar, "cell 0 5,alignx right,growx 0");
panel1.add(closableTabsTabbedPane, "cell 0 6");
//---- tabAreaComponentsLabel ----
tabAreaComponentsLabel.setText("Custom tab area components");
tabAreaComponentsLabel.putClientProperty("FlatLaf.styleClass", "h3");
panel1.add(tabAreaComponentsLabel, "cell 0 7");
//======== tabAreaComponentsToolBar ========
{
tabAreaComponentsToolBar.setFloatable(false);
tabAreaComponentsToolBar.setBorder(BorderFactory.createEmptyBorder());
//---- leadingComponentButton ----
leadingComponentButton.setText("leading");
leadingComponentButton.setSelected(true);
leadingComponentButton.putClientProperty("FlatLaf.styleClass", "small");
leadingComponentButton.addActionListener(e -> customComponentsChanged());
tabAreaComponentsToolBar.add(leadingComponentButton);
//---- trailingComponentButton ----
trailingComponentButton.setText("trailing");
trailingComponentButton.setSelected(true);
trailingComponentButton.putClientProperty("FlatLaf.styleClass", "small");
trailingComponentButton.addActionListener(e -> customComponentsChanged());
tabAreaComponentsToolBar.add(trailingComponentButton);
}
panel1.add(tabAreaComponentsToolBar, "cell 0 7,alignx right,growx 0");
panel1.add(customComponentsTabbedPane, "cell 0 8");
}
add(panel1, "cell 0 0");
//======== panel2 ========
{
panel2.setLayout(new MigLayout(
"insets 0,hidemode 3",
// columns
"[grow,fill]",
// rows
"[]0" +
"[]" +
"[fill]" +
"[center]" +
"[center]" +
"[center]para" +
"[center]0" +
"[]" +
"[center]" +
"[center]" +
"[center]" +
"[]"));
//---- tabIconPlacementLabel ----
tabIconPlacementLabel.setText("Tab icon placement");
tabIconPlacementLabel.putClientProperty("FlatLaf.styleClass", "h3");
panel2.add(tabIconPlacementLabel, "cell 0 0");
//---- tabIconPlacementNodeLabel ----
tabIconPlacementNodeLabel.setText("(top/bottom/leading/trailing)");
tabIconPlacementNodeLabel.setEnabled(false);
tabIconPlacementNodeLabel.putClientProperty("FlatLaf.styleClass", "small");
panel2.add(tabIconPlacementNodeLabel, "cell 0 1");
panel2.add(iconTopTabbedPane, "cell 0 2");
panel2.add(iconBottomTabbedPane, "cell 0 3");
panel2.add(iconLeadingTabbedPane, "cell 0 4");
panel2.add(iconTrailingTabbedPane, "cell 0 5");
//---- tabAreaAlignmentLabel ----
tabAreaAlignmentLabel.setText("Tab area alignment");
tabAreaAlignmentLabel.putClientProperty("FlatLaf.styleClass", "h3");
panel2.add(tabAreaAlignmentLabel, "cell 0 6");
//---- tabAreaAlignmentNoteLabel ----
tabAreaAlignmentNoteLabel.setText("(leading/center/trailing/fill)");
tabAreaAlignmentNoteLabel.setEnabled(false);
tabAreaAlignmentNoteLabel.putClientProperty("FlatLaf.styleClass", "small");
panel2.add(tabAreaAlignmentNoteLabel, "cell 0 7");
panel2.add(alignLeadingTabbedPane, "cell 0 8");
panel2.add(alignCenterTabbedPane, "cell 0 9");
panel2.add(alignTrailingTabbedPane, "cell 0 10");
panel2.add(alignFillTabbedPane, "cell 0 11");
}
add(panel2, "cell 1 0,growy");
//======== panel3 ========
{
panel3.setLayout(new MigLayout(
"insets 0,hidemode 3",
// columns
"[grow,fill]",
// rows
"[]0" +
"[]" +
"[]" +
"[]" +
"[]para" +
"[]" +
"[]" +
"[]para" +
"[]0" +
"[]"));
//---- tabWidthModeLabel ----
tabWidthModeLabel.setText("Tab width mode");
tabWidthModeLabel.putClientProperty("FlatLaf.styleClass", "h3");
panel3.add(tabWidthModeLabel, "cell 0 0");
//---- tabWidthModeNoteLabel ----
tabWidthModeNoteLabel.setText("(preferred/equal/compact)");
tabWidthModeNoteLabel.setEnabled(false);
tabWidthModeNoteLabel.putClientProperty("FlatLaf.styleClass", "small");
panel3.add(tabWidthModeNoteLabel, "cell 0 1");
panel3.add(widthPreferredTabbedPane, "cell 0 2");
panel3.add(widthEqualTabbedPane, "cell 0 3");
panel3.add(widthCompactTabbedPane, "cell 0 4");
//---- minMaxTabWidthLabel ----
minMaxTabWidthLabel.setText("Minimum/maximum tab width");
minMaxTabWidthLabel.putClientProperty("FlatLaf.styleClass", "h3");
panel3.add(minMaxTabWidthLabel, "cell 0 5");
panel3.add(minimumTabWidthTabbedPane, "cell 0 6");
panel3.add(maximumTabWidthTabbedPane, "cell 0 7");
//---- tabAlignmentLabel ----
tabAlignmentLabel.setText("Tab title alignment");
tabAlignmentLabel.putClientProperty("FlatLaf.styleClass", "h3");
panel3.add(tabAlignmentLabel, "cell 0 8");
//======== panel5 ========
{
panel5.setLayout(new MigLayout(
"insets 0,hidemode 3",
// columns // columns
"[grow,fill]para" + "[grow,fill]para" +
"[fill]para" +
"[fill]", "[fill]",
// rows // rows
"[grow,fill]")); "[]" +
"[]" +
"[]" +
"[]"));
//======== panel1 ======== //---- tabAlignmentNoteLabel ----
tabAlignmentNoteLabel.setText("(leading/center/trailing)");
tabAlignmentNoteLabel.setEnabled(false);
tabAlignmentNoteLabel.putClientProperty("FlatLaf.styleClass", "small");
panel5.add(tabAlignmentNoteLabel, "cell 0 0");
//---- tabAlignmentNoteLabel2 ----
tabAlignmentNoteLabel2.setText("(trailing)");
tabAlignmentNoteLabel2.setEnabled(false);
tabAlignmentNoteLabel2.putClientProperty("FlatLaf.styleClass", "small");
panel5.add(tabAlignmentNoteLabel2, "cell 1 0,alignx right,growx 0");
panel5.add(tabAlignLeadingTabbedPane, "cell 0 1");
//======== tabAlignVerticalTabbedPane ========
{ {
panel1.setLayout(new MigLayout( tabAlignVerticalTabbedPane.setTabPlacement(SwingConstants.LEFT);
"insets 0,hidemode 3",
// columns
"[grow,fill]",
// rows
"[]" +
"[fill]para" +
"[]0" +
"[]" +
"[]para" +
"[]" +
"[]para" +
"[]" +
"[]"));
//---- tabPlacementLabel ----
tabPlacementLabel.setText("Tab placement");
tabPlacementLabel.putClientProperty("FlatLaf.styleClass", "h3");
panel1.add(tabPlacementLabel, "cell 0 0");
//======== tabPlacementToolBar ========
{
tabPlacementToolBar.setFloatable(false);
tabPlacementToolBar.setBorder(BorderFactory.createEmptyBorder());
//---- topPlacementButton ----
topPlacementButton.setText("top");
topPlacementButton.setSelected(true);
topPlacementButton.putClientProperty("FlatLaf.styleClass", "small");
topPlacementButton.addActionListener(e -> tabPlacementChanged());
tabPlacementToolBar.add(topPlacementButton);
//---- bottomPlacementButton ----
bottomPlacementButton.setText("bottom");
bottomPlacementButton.putClientProperty("FlatLaf.styleClass", "small");
bottomPlacementButton.addActionListener(e -> tabPlacementChanged());
tabPlacementToolBar.add(bottomPlacementButton);
//---- leftPlacementButton ----
leftPlacementButton.setText("left");
leftPlacementButton.putClientProperty("FlatLaf.styleClass", "small");
leftPlacementButton.addActionListener(e -> tabPlacementChanged());
tabPlacementToolBar.add(leftPlacementButton);
//---- rightPlacementButton ----
rightPlacementButton.setText("right");
rightPlacementButton.putClientProperty("FlatLaf.styleClass", "small");
rightPlacementButton.addActionListener(e -> tabPlacementChanged());
tabPlacementToolBar.add(rightPlacementButton);
tabPlacementToolBar.addSeparator();
//---- scrollButton ----
scrollButton.setText("scroll");
scrollButton.putClientProperty("FlatLaf.styleClass", "small");
scrollButton.addActionListener(e -> scrollChanged());
tabPlacementToolBar.add(scrollButton);
//---- borderButton ----
borderButton.setText("border");
borderButton.putClientProperty("FlatLaf.styleClass", "small");
borderButton.addActionListener(e -> borderChanged());
tabPlacementToolBar.add(borderButton);
}
panel1.add(tabPlacementToolBar, "cell 0 0,alignx right,growx 0");
panel1.add(tabPlacementTabbedPane, "cell 0 1,width 300:300,height 100:100");
//---- tabLayoutLabel ----
tabLayoutLabel.setText("Tab layout");
tabLayoutLabel.putClientProperty("FlatLaf.styleClass", "h3");
panel1.add(tabLayoutLabel, "cell 0 2");
//======== tabLayoutToolBar ========
{
tabLayoutToolBar.setFloatable(false);
tabLayoutToolBar.setBorder(BorderFactory.createEmptyBorder());
//---- scrollTabLayoutButton ----
scrollTabLayoutButton.setText("scroll");
scrollTabLayoutButton.setSelected(true);
scrollTabLayoutButton.putClientProperty("FlatLaf.styleClass", "small");
scrollTabLayoutButton.addActionListener(e -> tabLayoutChanged());
tabLayoutToolBar.add(scrollTabLayoutButton);
//---- wrapTabLayoutButton ----
wrapTabLayoutButton.setText("wrap");
wrapTabLayoutButton.putClientProperty("FlatLaf.styleClass", "small");
wrapTabLayoutButton.addActionListener(e -> tabLayoutChanged());
tabLayoutToolBar.add(wrapTabLayoutButton);
}
panel1.add(tabLayoutToolBar, "cell 0 2,alignx right,growx 0");
//---- scrollLayoutNoteLabel ----
scrollLayoutNoteLabel.setText("(use mouse wheel to scroll; arrow button shows hidden tabs)");
scrollLayoutNoteLabel.setEnabled(false);
scrollLayoutNoteLabel.putClientProperty("FlatLaf.styleClass", "small");
panel1.add(scrollLayoutNoteLabel, "cell 0 3");
//---- wrapLayoutNoteLabel ----
wrapLayoutNoteLabel.setText("(probably better to use scroll layout?)");
wrapLayoutNoteLabel.setEnabled(false);
wrapLayoutNoteLabel.putClientProperty("FlatLaf.styleClass", "small");
panel1.add(wrapLayoutNoteLabel, "cell 0 3");
panel1.add(scrollLayoutTabbedPane, "cell 0 4");
panel1.add(wrapLayoutTabbedPane, "cell 0 4,width 100:100,height pref*2px");
//---- closableTabsLabel ----
closableTabsLabel.setText("Closable tabs");
closableTabsLabel.putClientProperty("FlatLaf.styleClass", "h3");
panel1.add(closableTabsLabel, "cell 0 5");
//======== closableTabsToolBar ========
{
closableTabsToolBar.setFloatable(false);
closableTabsToolBar.setBorder(BorderFactory.createEmptyBorder());
//---- squareCloseButton ----
squareCloseButton.setText("square");
squareCloseButton.setSelected(true);
squareCloseButton.putClientProperty("FlatLaf.styleClass", "small");
squareCloseButton.addActionListener(e -> closeButtonStyleChanged());
closableTabsToolBar.add(squareCloseButton);
//---- circleCloseButton ----
circleCloseButton.setText("circle");
circleCloseButton.putClientProperty("FlatLaf.styleClass", "small");
circleCloseButton.addActionListener(e -> closeButtonStyleChanged());
closableTabsToolBar.add(circleCloseButton);
//---- redCrossCloseButton ----
redCrossCloseButton.setText("red cross");
redCrossCloseButton.putClientProperty("FlatLaf.styleClass", "small");
redCrossCloseButton.addActionListener(e -> closeButtonStyleChanged());
closableTabsToolBar.add(redCrossCloseButton);
}
panel1.add(closableTabsToolBar, "cell 0 5,alignx right,growx 0");
panel1.add(closableTabsTabbedPane, "cell 0 6");
//---- tabAreaComponentsLabel ----
tabAreaComponentsLabel.setText("Custom tab area components");
tabAreaComponentsLabel.putClientProperty("FlatLaf.styleClass", "h3");
panel1.add(tabAreaComponentsLabel, "cell 0 7");
//======== tabAreaComponentsToolBar ========
{
tabAreaComponentsToolBar.setFloatable(false);
tabAreaComponentsToolBar.setBorder(BorderFactory.createEmptyBorder());
//---- leadingComponentButton ----
leadingComponentButton.setText("leading");
leadingComponentButton.setSelected(true);
leadingComponentButton.putClientProperty("FlatLaf.styleClass", "small");
leadingComponentButton.addActionListener(e -> customComponentsChanged());
tabAreaComponentsToolBar.add(leadingComponentButton);
//---- trailingComponentButton ----
trailingComponentButton.setText("trailing");
trailingComponentButton.setSelected(true);
trailingComponentButton.putClientProperty("FlatLaf.styleClass", "small");
trailingComponentButton.addActionListener(e -> customComponentsChanged());
tabAreaComponentsToolBar.add(trailingComponentButton);
}
panel1.add(tabAreaComponentsToolBar, "cell 0 7,alignx right,growx 0");
panel1.add(customComponentsTabbedPane, "cell 0 8");
} }
panel6.add(panel1, "cell 0 0"); panel5.add(tabAlignVerticalTabbedPane, "cell 1 1 1 3,growy");
panel5.add(tabAlignCenterTabbedPane, "cell 0 2");
//======== panel2 ======== panel5.add(tabAlignTrailingTabbedPane, "cell 0 3");
{
panel2.setLayout(new MigLayout(
"insets 0,hidemode 3",
// columns
"[grow,fill]",
// rows
"[]0" +
"[]" +
"[fill]" +
"[center]" +
"[center]" +
"[center]para" +
"[center]0" +
"[]" +
"[center]" +
"[center]" +
"[center]" +
"[]"));
//---- tabIconPlacementLabel ----
tabIconPlacementLabel.setText("Tab icon placement");
tabIconPlacementLabel.putClientProperty("FlatLaf.styleClass", "h3");
panel2.add(tabIconPlacementLabel, "cell 0 0");
//---- tabIconPlacementNodeLabel ----
tabIconPlacementNodeLabel.setText("(top/bottom/leading/trailing)");
tabIconPlacementNodeLabel.setEnabled(false);
tabIconPlacementNodeLabel.putClientProperty("FlatLaf.styleClass", "small");
panel2.add(tabIconPlacementNodeLabel, "cell 0 1");
panel2.add(iconTopTabbedPane, "cell 0 2");
panel2.add(iconBottomTabbedPane, "cell 0 3");
panel2.add(iconLeadingTabbedPane, "cell 0 4");
panel2.add(iconTrailingTabbedPane, "cell 0 5");
//---- tabAreaAlignmentLabel ----
tabAreaAlignmentLabel.setText("Tab area alignment");
tabAreaAlignmentLabel.putClientProperty("FlatLaf.styleClass", "h3");
panel2.add(tabAreaAlignmentLabel, "cell 0 6");
//---- tabAreaAlignmentNoteLabel ----
tabAreaAlignmentNoteLabel.setText("(leading/center/trailing/fill)");
tabAreaAlignmentNoteLabel.setEnabled(false);
tabAreaAlignmentNoteLabel.putClientProperty("FlatLaf.styleClass", "small");
panel2.add(tabAreaAlignmentNoteLabel, "cell 0 7");
panel2.add(alignLeadingTabbedPane, "cell 0 8");
panel2.add(alignCenterTabbedPane, "cell 0 9");
panel2.add(alignTrailingTabbedPane, "cell 0 10");
panel2.add(alignFillTabbedPane, "cell 0 11");
}
panel6.add(panel2, "cell 1 0,growy");
//======== panel3 ========
{
panel3.setLayout(new MigLayout(
"insets 0,hidemode 3",
// columns
"[grow,fill]",
// rows
"[]0" +
"[]" +
"[]" +
"[]" +
"[]para" +
"[]" +
"[]" +
"[]para" +
"[]0" +
"[]"));
//---- tabWidthModeLabel ----
tabWidthModeLabel.setText("Tab width mode");
tabWidthModeLabel.putClientProperty("FlatLaf.styleClass", "h3");
panel3.add(tabWidthModeLabel, "cell 0 0");
//---- tabWidthModeNoteLabel ----
tabWidthModeNoteLabel.setText("(preferred/equal/compact)");
tabWidthModeNoteLabel.setEnabled(false);
tabWidthModeNoteLabel.putClientProperty("FlatLaf.styleClass", "small");
panel3.add(tabWidthModeNoteLabel, "cell 0 1");
panel3.add(widthPreferredTabbedPane, "cell 0 2");
panel3.add(widthEqualTabbedPane, "cell 0 3");
panel3.add(widthCompactTabbedPane, "cell 0 4");
//---- minMaxTabWidthLabel ----
minMaxTabWidthLabel.setText("Minimum/maximum tab width");
minMaxTabWidthLabel.putClientProperty("FlatLaf.styleClass", "h3");
panel3.add(minMaxTabWidthLabel, "cell 0 5");
panel3.add(minimumTabWidthTabbedPane, "cell 0 6");
panel3.add(maximumTabWidthTabbedPane, "cell 0 7");
//---- tabAlignmentLabel ----
tabAlignmentLabel.setText("Tab title alignment");
tabAlignmentLabel.putClientProperty("FlatLaf.styleClass", "h3");
panel3.add(tabAlignmentLabel, "cell 0 8");
//======== panel5 ========
{
panel5.setLayout(new MigLayout(
"insets 0,hidemode 3",
// columns
"[grow,fill]para" +
"[fill]",
// rows
"[]" +
"[]" +
"[]" +
"[]" +
"[]"));
//---- tabAlignmentNoteLabel ----
tabAlignmentNoteLabel.setText("(leading/center/trailing)");
tabAlignmentNoteLabel.setEnabled(false);
tabAlignmentNoteLabel.putClientProperty("FlatLaf.styleClass", "small");
panel5.add(tabAlignmentNoteLabel, "cell 0 0");
//---- tabAlignmentNoteLabel2 ----
tabAlignmentNoteLabel2.setText("(trailing)");
tabAlignmentNoteLabel2.setEnabled(false);
tabAlignmentNoteLabel2.putClientProperty("FlatLaf.styleClass", "small");
panel5.add(tabAlignmentNoteLabel2, "cell 1 0,alignx right,growx 0");
panel5.add(tabAlignLeadingTabbedPane, "cell 0 1");
//======== tabAlignVerticalTabbedPane ========
{
tabAlignVerticalTabbedPane.setTabPlacement(SwingConstants.LEFT);
}
panel5.add(tabAlignVerticalTabbedPane, "cell 1 1 1 4,growy");
panel5.add(tabAlignCenterTabbedPane, "cell 0 2");
panel5.add(tabAlignTrailingTabbedPane, "cell 0 3");
}
panel3.add(panel5, "cell 0 9");
}
panel6.add(panel3, "cell 2 0");
} }
tabsScrollPane.setViewportView(panel6); panel3.add(panel5, "cell 0 9");
} }
add(tabsScrollPane, "cell 0 0"); add(panel3, "cell 2 0");
add(separator2, "cell 0 1"); add(separator2, "cell 0 1 3 1");
//======== panel4 ======== //======== panel4 ========
{ {
panel4.setLayout(new MigLayout( panel4.setLayout(new MigLayout(
"insets panel,hidemode 3", "insets 0,hidemode 3",
// columns // columns
"[]" + "[]" +
"[fill]para" + "[fill]para" +
"[fill]" + "[fill]" +
"[fill]para" + "[fill]para" +
"[fill]" +
"[fill]", "[fill]",
// rows // rows
"[]" + "[]" +
@@ -805,38 +770,9 @@ class TabsPanel
} }
panel4.add(scrollButtonsPolicyToolBar, "cell 1 0"); panel4.add(scrollButtonsPolicyToolBar, "cell 1 0");
//---- tabsPopupPolicyLabel ----
tabsPopupPolicyLabel.setText("Tabs popup policy:");
panel4.add(tabsPopupPolicyLabel, "cell 2 0");
//======== tabsPopupPolicyToolBar ========
{
tabsPopupPolicyToolBar.setFloatable(false);
tabsPopupPolicyToolBar.setBorder(BorderFactory.createEmptyBorder());
//---- popupAsNeededButton ----
popupAsNeededButton.setText("asNeeded");
popupAsNeededButton.setSelected(true);
popupAsNeededButton.putClientProperty("FlatLaf.styleClass", "small");
popupAsNeededButton.addActionListener(e -> tabsPopupPolicyChanged());
tabsPopupPolicyToolBar.add(popupAsNeededButton);
//---- popupNeverButton ----
popupNeverButton.setText("never");
popupNeverButton.putClientProperty("FlatLaf.styleClass", "small");
popupNeverButton.addActionListener(e -> tabsPopupPolicyChanged());
tabsPopupPolicyToolBar.add(popupNeverButton);
}
panel4.add(tabsPopupPolicyToolBar, "cell 3 0");
//---- showTabSeparatorsCheckBox ----
showTabSeparatorsCheckBox.setText("Show tab separators");
showTabSeparatorsCheckBox.addActionListener(e -> showTabSeparatorsChanged());
panel4.add(showTabSeparatorsCheckBox, "cell 4 0 2 1,alignx left,growx 0");
//---- scrollButtonsPlacementLabel ---- //---- scrollButtonsPlacementLabel ----
scrollButtonsPlacementLabel.setText("Scroll buttons placement:"); scrollButtonsPlacementLabel.setText("Scroll buttons placement:");
panel4.add(scrollButtonsPlacementLabel, "cell 0 1"); panel4.add(scrollButtonsPlacementLabel, "cell 2 0");
//======== scrollButtonsPlacementToolBar ======== //======== scrollButtonsPlacementToolBar ========
{ {
@@ -856,7 +792,36 @@ class TabsPanel
scrollTrailingButton.addActionListener(e -> scrollButtonsPlacementChanged()); scrollTrailingButton.addActionListener(e -> scrollButtonsPlacementChanged());
scrollButtonsPlacementToolBar.add(scrollTrailingButton); scrollButtonsPlacementToolBar.add(scrollTrailingButton);
} }
panel4.add(scrollButtonsPlacementToolBar, "cell 1 1"); panel4.add(scrollButtonsPlacementToolBar, "cell 3 0");
//---- showTabSeparatorsCheckBox ----
showTabSeparatorsCheckBox.setText("Show tab separators");
showTabSeparatorsCheckBox.addActionListener(e -> showTabSeparatorsChanged());
panel4.add(showTabSeparatorsCheckBox, "cell 4 0");
//---- tabsPopupPolicyLabel ----
tabsPopupPolicyLabel.setText("Tabs popup policy:");
panel4.add(tabsPopupPolicyLabel, "cell 0 1");
//======== tabsPopupPolicyToolBar ========
{
tabsPopupPolicyToolBar.setFloatable(false);
tabsPopupPolicyToolBar.setBorder(BorderFactory.createEmptyBorder());
//---- popupAsNeededButton ----
popupAsNeededButton.setText("asNeeded");
popupAsNeededButton.setSelected(true);
popupAsNeededButton.putClientProperty("FlatLaf.styleClass", "small");
popupAsNeededButton.addActionListener(e -> tabsPopupPolicyChanged());
tabsPopupPolicyToolBar.add(popupAsNeededButton);
//---- popupNeverButton ----
popupNeverButton.setText("never");
popupNeverButton.putClientProperty("FlatLaf.styleClass", "small");
popupNeverButton.addActionListener(e -> tabsPopupPolicyChanged());
tabsPopupPolicyToolBar.add(popupNeverButton);
}
panel4.add(tabsPopupPolicyToolBar, "cell 1 1");
//---- tabTypeLabel ---- //---- tabTypeLabel ----
tabTypeLabel.setText("Tab type:"); tabTypeLabel.setText("Tab type:");
@@ -880,44 +845,8 @@ class TabsPanel
tabTypeToolBar.add(cardTabTypeButton); tabTypeToolBar.add(cardTabTypeButton);
} }
panel4.add(tabTypeToolBar, "cell 3 1"); panel4.add(tabTypeToolBar, "cell 3 1");
//---- tabRotationLabel ----
tabRotationLabel.setText("Tab rotation:");
panel4.add(tabRotationLabel, "cell 4 1");
//======== tabRotationToolBar ========
{
tabRotationToolBar.setFloatable(false);
tabRotationToolBar.setBorder(BorderFactory.createEmptyBorder());
//---- rotationNoneButton ----
rotationNoneButton.setText("none");
rotationNoneButton.setSelected(true);
rotationNoneButton.putClientProperty("FlatLaf.styleClass", "small");
rotationNoneButton.addActionListener(e -> tabRotationChanged());
tabRotationToolBar.add(rotationNoneButton);
//---- rotationAutoButton ----
rotationAutoButton.setText("auto");
rotationAutoButton.putClientProperty("FlatLaf.styleClass", "small");
rotationAutoButton.addActionListener(e -> tabRotationChanged());
tabRotationToolBar.add(rotationAutoButton);
//---- rotationLeftButton ----
rotationLeftButton.setText("left");
rotationLeftButton.putClientProperty("FlatLaf.styleClass", "small");
rotationLeftButton.addActionListener(e -> tabRotationChanged());
tabRotationToolBar.add(rotationLeftButton);
//---- rotationRightButton ----
rotationRightButton.setText("right");
rotationRightButton.putClientProperty("FlatLaf.styleClass", "small");
rotationRightButton.addActionListener(e -> tabRotationChanged());
tabRotationToolBar.add(rotationRightButton);
}
panel4.add(tabRotationToolBar, "cell 5 1");
} }
add(panel4, "cell 0 2"); add(panel4, "cell 0 2 3 1");
//---- tabPlacementButtonGroup ---- //---- tabPlacementButtonGroup ----
ButtonGroup tabPlacementButtonGroup = new ButtonGroup(); ButtonGroup tabPlacementButtonGroup = new ButtonGroup();
@@ -943,27 +872,20 @@ class TabsPanel
scrollButtonsPolicyButtonGroup.add(scrollAsNeededButton); scrollButtonsPolicyButtonGroup.add(scrollAsNeededButton);
scrollButtonsPolicyButtonGroup.add(scrollNeverButton); scrollButtonsPolicyButtonGroup.add(scrollNeverButton);
//---- tabsPopupPolicyButtonGroup ----
ButtonGroup tabsPopupPolicyButtonGroup = new ButtonGroup();
tabsPopupPolicyButtonGroup.add(popupAsNeededButton);
tabsPopupPolicyButtonGroup.add(popupNeverButton);
//---- scrollButtonsPlacementButtonGroup ---- //---- scrollButtonsPlacementButtonGroup ----
ButtonGroup scrollButtonsPlacementButtonGroup = new ButtonGroup(); ButtonGroup scrollButtonsPlacementButtonGroup = new ButtonGroup();
scrollButtonsPlacementButtonGroup.add(scrollBothButton); scrollButtonsPlacementButtonGroup.add(scrollBothButton);
scrollButtonsPlacementButtonGroup.add(scrollTrailingButton); scrollButtonsPlacementButtonGroup.add(scrollTrailingButton);
//---- tabsPopupPolicyButtonGroup ----
ButtonGroup tabsPopupPolicyButtonGroup = new ButtonGroup();
tabsPopupPolicyButtonGroup.add(popupAsNeededButton);
tabsPopupPolicyButtonGroup.add(popupNeverButton);
//---- tabTypeButtonGroup ---- //---- tabTypeButtonGroup ----
ButtonGroup tabTypeButtonGroup = new ButtonGroup(); ButtonGroup tabTypeButtonGroup = new ButtonGroup();
tabTypeButtonGroup.add(underlinedTabTypeButton); tabTypeButtonGroup.add(underlinedTabTypeButton);
tabTypeButtonGroup.add(cardTabTypeButton); tabTypeButtonGroup.add(cardTabTypeButton);
//---- tabRotationButtonGroup ----
ButtonGroup tabRotationButtonGroup = new ButtonGroup();
tabRotationButtonGroup.add(rotationNoneButton);
tabRotationButtonGroup.add(rotationAutoButton);
tabRotationButtonGroup.add(rotationLeftButton);
tabRotationButtonGroup.add(rotationRightButton);
// JFormDesigner - End of component initialization //GEN-END:initComponents // JFormDesigner - End of component initialization //GEN-END:initComponents
if( FlatLafDemo.screenshotsMode ) { if( FlatLafDemo.screenshotsMode ) {
@@ -1039,24 +961,18 @@ class TabsPanel
private JToggleButton scrollAsNeededSingleButton; private JToggleButton scrollAsNeededSingleButton;
private JToggleButton scrollAsNeededButton; private JToggleButton scrollAsNeededButton;
private JToggleButton scrollNeverButton; private JToggleButton scrollNeverButton;
private JLabel tabsPopupPolicyLabel;
private JToolBar tabsPopupPolicyToolBar;
private JToggleButton popupAsNeededButton;
private JToggleButton popupNeverButton;
private JCheckBox showTabSeparatorsCheckBox;
private JLabel scrollButtonsPlacementLabel; private JLabel scrollButtonsPlacementLabel;
private JToolBar scrollButtonsPlacementToolBar; private JToolBar scrollButtonsPlacementToolBar;
private JToggleButton scrollBothButton; private JToggleButton scrollBothButton;
private JToggleButton scrollTrailingButton; private JToggleButton scrollTrailingButton;
private JCheckBox showTabSeparatorsCheckBox;
private JLabel tabsPopupPolicyLabel;
private JToolBar tabsPopupPolicyToolBar;
private JToggleButton popupAsNeededButton;
private JToggleButton popupNeverButton;
private JLabel tabTypeLabel; private JLabel tabTypeLabel;
private JToolBar tabTypeToolBar; private JToolBar tabTypeToolBar;
private JToggleButton underlinedTabTypeButton; private JToggleButton underlinedTabTypeButton;
private JToggleButton cardTabTypeButton; private JToggleButton cardTabTypeButton;
private JLabel tabRotationLabel;
private JToolBar tabRotationToolBar;
private JToggleButton rotationNoneButton;
private JToggleButton rotationAutoButton;
private JToggleButton rotationLeftButton;
private JToggleButton rotationRightButton;
// JFormDesigner - End of variables declaration //GEN-END:variables // JFormDesigner - End of variables declaration //GEN-END:variables
} }

View File

@@ -25,6 +25,7 @@ import java.awt.Window;
import java.awt.event.WindowAdapter; import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent; import java.awt.event.WindowEvent;
import java.awt.event.WindowListener; import java.awt.event.WindowListener;
import java.beans.Beans;
import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener; import java.beans.PropertyChangeListener;
import java.io.File; import java.io.File;
@@ -156,6 +157,9 @@ public class IJThemesPanel
} }
private void updateThemesList() { private void updateThemesList() {
if( Beans.isDesignTime() )
return; // disable if running in GUI builder
int filterLightDark = filterComboBox.getSelectedIndex(); int filterLightDark = filterComboBox.getSelectedIndex();
boolean showLight = (filterLightDark != 2); boolean showLight = (filterLightDark != 2);
boolean showDark = (filterLightDark != 1); boolean showDark = (filterLightDark != 1);

View File

@@ -33,7 +33,7 @@ build script:
artifactId: flatlaf-extras artifactId: flatlaf-extras
version: (see button below) version: (see button below)
Otherwise, download `flatlaf-extras-<version>.jar` here: Otherwise download `flatlaf-extras-<version>.jar` here:
[![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.formdev/flatlaf-extras/badge.svg?style=flat-square&color=007ec6)](https://maven-badges.herokuapp.com/maven-central/com.formdev/flatlaf-extras) [![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.formdev/flatlaf-extras/badge.svg?style=flat-square&color=007ec6)](https://maven-badges.herokuapp.com/maven-central/com.formdev/flatlaf-extras)

View File

@@ -67,7 +67,7 @@ public class FlatAnimatedLafChange
* Invoke before setting new look and feel. * Invoke before setting new look and feel.
*/ */
public static void showSnapshot() { public static void showSnapshot() {
if( !FlatSystemProperties.getBoolean( "flatlaf.animatedLafChange", true ) ) if( !Animator.useAnimation() || !FlatSystemProperties.getBoolean( "flatlaf.animatedLafChange", true ) )
return; return;
// stop already running animation // stop already running animation
@@ -133,12 +133,12 @@ public class FlatAnimatedLafChange
} }
/** /**
* Starts an animation that shows the snapshot (created by {@link #showSnapshot()}) * Starts an animation that shows the snapshot (created by {@link #showSnapshot()}
* with a decreasing alpha. At the end, the snapshot is removed and the new UI is shown. * with an decreasing alpha. At the end, the snapshot is removed and the new UI is shown.
* Invoke after updating UI. * Invoke after updating UI.
*/ */
public static void hideSnapshotWithAnimation() { public static void hideSnapshotWithAnimation() {
if( !FlatSystemProperties.getBoolean( "flatlaf.animatedLafChange", true ) ) if( !Animator.useAnimation() || !FlatSystemProperties.getBoolean( "flatlaf.animatedLafChange", true ) )
return; return;
if( oldUIsnapshots.isEmpty() ) if( oldUIsnapshots.isEmpty() )

View File

@@ -119,7 +119,7 @@ public class FlatDesktop
(proxy, method, args) -> { (proxy, method, args) -> {
// Use invokeLater to release the listener firing for the case // Use invokeLater to release the listener firing for the case
// that the action listener shows a modal dialog. // that the action listener shows a modal dialog.
// This (hopefully) prevents application hanging. // This (hopefully) prevents application hunging.
EventQueue.invokeLater( () -> { EventQueue.invokeLater( () -> {
handler.run(); handler.run();
} ); } );

View File

@@ -87,7 +87,7 @@ public class FlatSVGIcon
* in the tag {@code <svg>} are used as icon size. * in the tag {@code <svg>} are used as icon size.
* <p> * <p>
* If using Java modules, the package containing the icon must be opened in {@code module-info.java}. * If using Java modules, the package containing the icon must be opened in {@code module-info.java}.
* Otherwise, use {@link #FlatSVGIcon(URL)}. * Otherwise use {@link #FlatSVGIcon(URL)}.
* <p> * <p>
* This is cheap operation because the icon is only loaded when used. * This is cheap operation because the icon is only loaded when used.
* *
@@ -106,7 +106,7 @@ public class FlatSVGIcon
* in the tag {@code <svg>} are used as icon size. * in the tag {@code <svg>} are used as icon size.
* <p> * <p>
* If using Java modules, the package containing the icon must be opened in {@code module-info.java}. * If using Java modules, the package containing the icon must be opened in {@code module-info.java}.
* Otherwise, use {@link #FlatSVGIcon(URL)}. * Otherwise use {@link #FlatSVGIcon(URL)}.
* <p> * <p>
* This is cheap operation because the icon is only loaded when used. * This is cheap operation because the icon is only loaded when used.
* *
@@ -124,7 +124,7 @@ public class FlatSVGIcon
* The icon is scaled if the given size is different to the size specified in the SVG file. * The icon is scaled if the given size is different to the size specified in the SVG file.
* <p> * <p>
* If using Java modules, the package containing the icon must be opened in {@code module-info.java}. * If using Java modules, the package containing the icon must be opened in {@code module-info.java}.
* Otherwise, use {@link #FlatSVGIcon(URL)}. * Otherwise use {@link #FlatSVGIcon(URL)}.
* <p> * <p>
* This is cheap operation because the icon is only loaded when used. * This is cheap operation because the icon is only loaded when used.
* *
@@ -144,7 +144,7 @@ public class FlatSVGIcon
* The icon is scaled if the given size is different to the size specified in the SVG file. * The icon is scaled if the given size is different to the size specified in the SVG file.
* <p> * <p>
* If using Java modules, the package containing the icon must be opened in {@code module-info.java}. * If using Java modules, the package containing the icon must be opened in {@code module-info.java}.
* Otherwise, use {@link #FlatSVGIcon(URL)}. * Otherwise use {@link #FlatSVGIcon(URL)}.
* <p> * <p>
* This is cheap operation because the icon is only loaded when used. * This is cheap operation because the icon is only loaded when used.
* *
@@ -166,7 +166,7 @@ public class FlatSVGIcon
* by the given scale factor. * by the given scale factor.
* <p> * <p>
* If using Java modules, the package containing the icon must be opened in {@code module-info.java}. * If using Java modules, the package containing the icon must be opened in {@code module-info.java}.
* Otherwise, use {@link #FlatSVGIcon(URL)}. * Otherwise use {@link #FlatSVGIcon(URL)}.
* <p> * <p>
* This is cheap operation because the icon is only loaded when used. * This is cheap operation because the icon is only loaded when used.
* *
@@ -187,7 +187,7 @@ public class FlatSVGIcon
* by the given scale factor. * by the given scale factor.
* <p> * <p>
* If using Java modules, the package containing the icon must be opened in {@code module-info.java}. * If using Java modules, the package containing the icon must be opened in {@code module-info.java}.
* Otherwise, use {@link #FlatSVGIcon(URL)}. * Otherwise use {@link #FlatSVGIcon(URL)}.
* <p> * <p>
* This is cheap operation because the icon is only loaded when used. * This is cheap operation because the icon is only loaded when used.
* *
@@ -259,7 +259,7 @@ public class FlatSVGIcon
* <p> * <p>
* The input stream is loaded, parsed and closed immediately. * The input stream is loaded, parsed and closed immediately.
* *
* @param in the input stream for reading an SVG resource * @param in the input stream for reading a SVG resource
* @throws IOException if an I/O exception occurs * @throws IOException if an I/O exception occurs
* @since 2 * @since 2
*/ */

View File

@@ -46,10 +46,10 @@ public class FlatSVGUtils
* then a single multi-resolution image is returned that creates images on demand * then a single multi-resolution image is returned that creates images on demand
* for requested sizes from SVG. * for requested sizes from SVG.
* This has the advantage that only images for used sizes are created. * This has the advantage that only images for used sizes are created.
* Also, if unusual sizes are requested (e.g. 18x18), then they are created from SVG. * Also if unusual sizes are requested (e.g. 18x18), then they are created from SVG.
* <p> * <p>
* If using Java modules, the package containing the SVG must be opened in {@code module-info.java}. * If using Java modules, the package containing the SVG must be opened in {@code module-info.java}.
* Otherwise, use {@link #createWindowIconImages(URL)}. * Otherwise use {@link #createWindowIconImages(URL)}.
* *
* @param svgName the name of the SVG resource (a '/'-separated path) * @param svgName the name of the SVG resource (a '/'-separated path)
* @return list of icon images with different sizes (16x16, 20x20, 24x24, 28x28, 32x32, 48x48 and 64x64) * @return list of icon images with different sizes (16x16, 20x20, 24x24, 28x28, 32x32, 48x48 and 64x64)
@@ -69,7 +69,7 @@ public class FlatSVGUtils
* then a single multi-resolution image is returned that creates images on demand * then a single multi-resolution image is returned that creates images on demand
* for requested sizes from SVG. * for requested sizes from SVG.
* This has the advantage that only images for used sizes are created. * This has the advantage that only images for used sizes are created.
* Also, if unusual sizes are requested (e.g. 18x18), then they are created from SVG. * Also if unusual sizes are requested (e.g. 18x18), then they are created from SVG.
* <p> * <p>
* This method is useful if using Java modules and the package containing the SVG * This method is useful if using Java modules and the package containing the SVG
* is not opened in {@code module-info.java}. * is not opened in {@code module-info.java}.
@@ -92,7 +92,7 @@ public class FlatSVGUtils
// any size is created on demand when // any size is created on demand when
// MultiResolutionImage.getResolutionVariant(double destImageWidth, double destImageHeight) // MultiResolutionImage.getResolutionVariant(double destImageWidth, double destImageHeight)
// is invoked. // is invoked.
// These sizes are only used by MultiResolutionImage.getResolutionVariants(). // This sizes are only used by MultiResolutionImage.getResolutionVariants().
new Dimension( 16, 16 ), // 100% new Dimension( 16, 16 ), // 100%
new Dimension( 20, 20 ), // 125% new Dimension( 20, 20 ), // 125%
new Dimension( 24, 24 ), // 150% new Dimension( 24, 24 ), // 150%
@@ -120,7 +120,7 @@ public class FlatSVGUtils
* Creates a buffered image and renders the given SVG into it. * Creates a buffered image and renders the given SVG into it.
* <p> * <p>
* If using Java modules, the package containing the SVG must be opened in {@code module-info.java}. * If using Java modules, the package containing the SVG must be opened in {@code module-info.java}.
* Otherwise, use {@link #svg2image(URL, int, int)}. * Otherwise use {@link #svg2image(URL, int, int)}.
* *
* @param svgName the name of the SVG resource (a '/'-separated path) * @param svgName the name of the SVG resource (a '/'-separated path)
* @param width the width of the image * @param width the width of the image
@@ -154,7 +154,7 @@ public class FlatSVGUtils
* Creates a buffered image and renders the given SVG into it. * Creates a buffered image and renders the given SVG into it.
* <p> * <p>
* If using Java modules, the package containing the SVG must be opened in {@code module-info.java}. * If using Java modules, the package containing the SVG must be opened in {@code module-info.java}.
* Otherwise, use {@link #svg2image(URL, float)}. * Otherwise use {@link #svg2image(URL, float)}.
* *
* @param svgName the name of the SVG resource (a '/'-separated path) * @param svgName the name of the SVG resource (a '/'-separated path)
* @param scaleFactor the amount by which the SVG size is scaled * @param scaleFactor the amount by which the SVG size is scaled

View File

@@ -557,7 +557,7 @@ public class FlatUIDefaultsInspector
// JFormDesigner - Component initialization - DO NOT MODIFY //GEN-BEGIN:initComponents // JFormDesigner - Component initialization - DO NOT MODIFY //GEN-BEGIN:initComponents
panel = new JPanel(); panel = new JPanel();
filterPanel = new JPanel(); filterPanel = new JPanel();
filterLabel = new JLabel(); flterLabel = new JLabel();
filterField = new FlatTextField(); filterField = new FlatTextField();
valueTypeLabel = new JLabel(); valueTypeLabel = new JLabel();
valueTypeField = new JComboBox<>(); valueTypeField = new JComboBox<>();
@@ -580,11 +580,11 @@ public class FlatUIDefaultsInspector
((GridBagLayout)filterPanel.getLayout()).columnWeights = new double[] {0.0, 1.0, 0.0, 0.0, 1.0E-4}; ((GridBagLayout)filterPanel.getLayout()).columnWeights = new double[] {0.0, 1.0, 0.0, 0.0, 1.0E-4};
((GridBagLayout)filterPanel.getLayout()).rowWeights = new double[] {0.0, 1.0E-4}; ((GridBagLayout)filterPanel.getLayout()).rowWeights = new double[] {0.0, 1.0E-4};
//---- filterLabel ---- //---- flterLabel ----
filterLabel.setText("Filter:"); flterLabel.setText("Filter:");
filterLabel.setLabelFor(filterField); flterLabel.setLabelFor(filterField);
filterLabel.setDisplayedMnemonic('F'); flterLabel.setDisplayedMnemonic('F');
filterPanel.add(filterLabel, new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0, filterPanel.add(flterLabel, new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0,
GridBagConstraints.CENTER, GridBagConstraints.BOTH, GridBagConstraints.CENTER, GridBagConstraints.BOTH,
new Insets(0, 0, 0, 10), 0, 0)); new Insets(0, 0, 0, 10), 0, 0));
@@ -668,7 +668,7 @@ public class FlatUIDefaultsInspector
// JFormDesigner - Variables declaration - DO NOT MODIFY //GEN-BEGIN:variables // JFormDesigner - Variables declaration - DO NOT MODIFY //GEN-BEGIN:variables
private JPanel panel; private JPanel panel;
private JPanel filterPanel; private JPanel filterPanel;
private JLabel filterLabel; private JLabel flterLabel;
private FlatTextField filterField; private FlatTextField filterField;
private JLabel valueTypeLabel; private JLabel valueTypeLabel;
private JComboBox<String> valueTypeField; private JComboBox<String> valueTypeField;

View File

@@ -15,7 +15,7 @@ new FormModel {
} ) { } ) {
name: "filterPanel" name: "filterPanel"
add( new FormComponent( "javax.swing.JLabel" ) { add( new FormComponent( "javax.swing.JLabel" ) {
name: "filterLabel" name: "flterLabel"
"text": "Filter:" "text": "Filter:"
"labelFor": new FormReference( "filterField" ) "labelFor": new FormReference( "filterField" )
"displayedMnemonic": 70 "displayedMnemonic": 70

View File

@@ -33,14 +33,14 @@ public class FlatButton
public enum ButtonType { none, square, roundRect, tab, help, toolBarButton, borderless } public enum ButtonType { none, square, roundRect, tab, help, toolBarButton, borderless }
/** /**
* Returns type of button. * Returns type of a button.
*/ */
public ButtonType getButtonType() { public ButtonType getButtonType() {
return getClientPropertyEnumString( BUTTON_TYPE, ButtonType.class, null, ButtonType.none ); return getClientPropertyEnumString( BUTTON_TYPE, ButtonType.class, null, ButtonType.none );
} }
/** /**
* Specifies type of button. * Specifies type of a button.
*/ */
public void setButtonType( ButtonType buttonType ) { public void setButtonType( ButtonType buttonType ) {
if( buttonType == ButtonType.none ) if( buttonType == ButtonType.none )

View File

@@ -106,7 +106,7 @@ public class FlatFormattedTextField
* The component should be not opaque because the text field border is painted * The component should be not opaque because the text field border is painted
* slightly inside the usually visible border in some cases. * slightly inside the usually visible border in some cases.
* E.g. when focused (in some themes) or when an outline color is specified * E.g. when focused (in some themes) or when an outline color is specified
* (see {@link #setOutline(Object)}). * (see {@link #setOutline(Object)}.
* *
* @since 2 * @since 2
*/ */
@@ -135,7 +135,7 @@ public class FlatFormattedTextField
* The component should be not opaque because the text field border is painted * The component should be not opaque because the text field border is painted
* slightly inside the usually visible border in some cases. * slightly inside the usually visible border in some cases.
* E.g. when focused (in some themes) or when an outline color is specified * E.g. when focused (in some themes) or when an outline color is specified
* (see {@link #setOutline(Object)}). * (see {@link #setOutline(Object)}.
* *
* @since 2 * @since 2
*/ */

View File

@@ -106,7 +106,7 @@ public class FlatPasswordField
* The component should be not opaque because the text field border is painted * The component should be not opaque because the text field border is painted
* slightly inside the usually visible border in some cases. * slightly inside the usually visible border in some cases.
* E.g. when focused (in some themes) or when an outline color is specified * E.g. when focused (in some themes) or when an outline color is specified
* (see {@link #setOutline(Object)}). * (see {@link #setOutline(Object)}.
* *
* @since 2 * @since 2
*/ */
@@ -135,7 +135,7 @@ public class FlatPasswordField
* The component should be not opaque because the text field border is painted * The component should be not opaque because the text field border is painted
* slightly inside the usually visible border in some cases. * slightly inside the usually visible border in some cases.
* E.g. when focused (in some themes) or when an outline color is specified * E.g. when focused (in some themes) or when an outline color is specified
* (see {@link #setOutline(Object)}). * (see {@link #setOutline(Object)}.
* *
* @since 2 * @since 2
*/ */

View File

@@ -502,29 +502,6 @@ public class FlatTabbedPane
} }
// NOTE: enum names must be equal to allowed strings
/** @since 3.3 */ public enum TabRotation { none, auto, left, right }
/**
* Returns how the tabs should be rotated.
*
* @since 3.3
*/
public TabRotation getTabRotation() {
return getClientPropertyEnumString( TABBED_PANE_TAB_ROTATION, TabRotation.class,
"TabbedPane.tabRotation", TabRotation.none );
}
/**
* Specifies how the tabs should be rotated.
*
* @since 3.3
*/
public void setTabRotation( TabRotation tabRotation ) {
putClientPropertyEnumString( TABBED_PANE_TAB_ROTATION, tabRotation );
}
/** /**
* Returns the tab icon placement (relative to tab title). * Returns the tab icon placement (relative to tab title).
*/ */

View File

@@ -105,7 +105,7 @@ public class FlatTextField
* The component should be not opaque because the text field border is painted * The component should be not opaque because the text field border is painted
* slightly inside the usually visible border in some cases. * slightly inside the usually visible border in some cases.
* E.g. when focused (in some themes) or when an outline color is specified * E.g. when focused (in some themes) or when an outline color is specified
* (see {@link #setOutline(Object)}). * (see {@link #setOutline(Object)}.
* *
* @since 2 * @since 2
*/ */
@@ -134,7 +134,7 @@ public class FlatTextField
* The component should be not opaque because the text field border is painted * The component should be not opaque because the text field border is painted
* slightly inside the usually visible border in some cases. * slightly inside the usually visible border in some cases.
* E.g. when focused (in some themes) or when an outline color is specified * E.g. when focused (in some themes) or when an outline color is specified
* (see {@link #setOutline(Object)}). * (see {@link #setOutline(Object)}.
* *
* @since 2 * @since 2
*/ */

View File

@@ -31,14 +31,14 @@ public class FlatToggleButton
implements FlatComponentExtension, FlatStyleableComponent implements FlatComponentExtension, FlatStyleableComponent
{ {
/** /**
* Returns type of button. * Returns type of a button.
*/ */
public ButtonType getButtonType() { public ButtonType getButtonType() {
return getClientPropertyEnumString( BUTTON_TYPE, ButtonType.class, null, ButtonType.none ); return getClientPropertyEnumString( BUTTON_TYPE, ButtonType.class, null, ButtonType.none );
} }
/** /**
* Specifies type of button. * Specifies type of a button.
*/ */
public void setButtonType( ButtonType buttonType ) { public void setButtonType( ButtonType buttonType ) {
if( buttonType == ButtonType.none ) if( buttonType == ButtonType.none )

View File

@@ -30,8 +30,8 @@ import com.formdev.flatlaf.FlatLaf;
* <p> * <p>
* The initial state is {@link State#INDETERMINATE}. * The initial state is {@link State#INDETERMINATE}.
* <p> * <p>
* By default, the third state is allowed and clicking on the checkbox cycles through all * By default the third state is allowed and clicking on the checkbox cycles thru all
* three states. If you want that the user can cycle only through two states, disallow * three states. If you want that the user can cycle only thru two states, disallow
* intermediate state using {@link #setAllowIndeterminate(boolean)}. Then you can still * intermediate state using {@link #setAllowIndeterminate(boolean)}. Then you can still
* set the indeterminate state via API if necessary, but the user can not. * set the indeterminate state via API if necessary, but the user can not.
* <p> * <p>

View File

@@ -18,8 +18,8 @@ package com.formdev.flatlaf.extras.resources;
/** /**
* The only purpose of this file is to add a .class file to this package to make it non-empty. * The only purpose of this file is to add a .class file to this package to make it non-empty.
* Otherwise, the compiler outputs a warning because this package is opened in module-info.java. * 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. * Also when using --patch-module (e.g. from an IDE), an error would occur for empty packages.
* *
* @author Karl Tauber * @author Karl Tauber
*/ */

View File

@@ -175,9 +175,6 @@ Spinner.buttonPressedArrowColor = Spinner.buttonArrowColor
#---- SplitPaneDivider ---- #---- SplitPaneDivider ----
SplitPaneDivider.draggingColor = SplitPane.background
SplitPaneDivider.hoverColor = SplitPane.background
SplitPaneDivider.pressedColor = SplitPane.background
SplitPaneDivider.oneTouchHoverArrowColor = SplitPaneDivider.oneTouchArrowColor SplitPaneDivider.oneTouchHoverArrowColor = SplitPaneDivider.oneTouchArrowColor
SplitPaneDivider.oneTouchPressedArrowColor = SplitPaneDivider.oneTouchArrowColor SplitPaneDivider.oneTouchPressedArrowColor = SplitPaneDivider.oneTouchArrowColor

View File

@@ -100,6 +100,6 @@ build script:
artifactId: flatlaf-fonts-inter artifactId: flatlaf-fonts-inter
version: (see button below) version: (see button below)
Otherwise, download `flatlaf-fonts-inter-<version>.jar` here: Otherwise download `flatlaf-fonts-inter-<version>.jar` here:
[![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.formdev/flatlaf-fonts-inter/badge.svg?style=flat-square&color=007ec6)](https://maven-badges.herokuapp.com/maven-central/com.formdev/flatlaf-fonts-inter) [![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.formdev/flatlaf-fonts-inter/badge.svg?style=flat-square&color=007ec6)](https://maven-badges.herokuapp.com/maven-central/com.formdev/flatlaf-fonts-inter)

View File

@@ -18,7 +18,7 @@
// For maven compatibility, <font-version> should be in format <major>.<minor>[.<micro>]. // For maven compatibility, <font-version> should be in format <major>.<minor>[.<micro>].
// <build-number> is optional and should be incremented only if a new release is // <build-number> is optional and should be incremented only if a new release is
// necessary, but the <font-version> has not changed. // necessary, but the <font-version> has not changed.
version = "4.0" version = "3.19"
if( !rootProject.hasProperty( "release" ) ) if( !rootProject.hasProperty( "release" ) )
version = version.toString() + "-SNAPSHOT" version = version.toString() + "-SNAPSHOT"

View File

@@ -117,7 +117,7 @@ public class FlatInterFont
* new Font( FlatInterFont.FAMILY_SEMIBOLD, Font.ITALIC, 12 ); * new Font( FlatInterFont.FAMILY_SEMIBOLD, Font.ITALIC, 12 );
* }</pre> * }</pre>
*/ */
public static final String FAMILY_SEMIBOLD = "Inter SemiBold"; public static final String FAMILY_SEMIBOLD = "Inter Semi Bold";
/** /**
* Use for {@link #installStyle(String)} to install single font style. * Use for {@link #installStyle(String)} to install single font style.

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