mirror of
https://github.com/JFormDesigner/FlatLaf.git
synced 2025-12-06 14:00:55 +03:00
Styling: basic implementation of styling support using client property JComponent.style and CSS syntax
only for JSlider (at the moment) e.g. `mySlider.putClientProperty( "JComponent.style", "trackValueColor: #00f; trackColor: #f00; thumbColor: #0f0; trackWidth: 6; thumbSize: 40,20; focusWidth: 20" );` (issues #117 and #340)
This commit is contained in:
@@ -126,6 +126,16 @@ public interface FlatClientProperties
|
||||
|
||||
//---- JComponent ---------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Specifies the style of a component in CSS syntax ("key1: value1; key2: value2; ...").
|
||||
* <p>
|
||||
* <strong>Components</strong> {@link javax.swing.JComponent}<br>
|
||||
* <strong>Value type</strong> {@link java.lang.String}<br>
|
||||
*
|
||||
* @since TODO
|
||||
*/
|
||||
String COMPONENT_STYLE = "JComponent.style";
|
||||
|
||||
/**
|
||||
* Specifies minimum width of a component.
|
||||
* <p>
|
||||
|
||||
@@ -726,6 +726,21 @@ public abstract class FlatLaf
|
||||
customDefaultsSources.remove( folder );
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a UI defaults value string and converts it into a binary object.
|
||||
* <p>
|
||||
* See: <a href="https://www.formdev.com/flatlaf/properties-files/">https://www.formdev.com/flatlaf/properties-files/</a>
|
||||
*
|
||||
* @param key the key, which is used to determine the value type
|
||||
* @param value the value string
|
||||
* @return the binary value
|
||||
* @throws IllegalArgumentException on syntax errors
|
||||
* @since TODO
|
||||
*/
|
||||
public static Object parseDefaultsValue( String key, String value ) throws IllegalArgumentException {
|
||||
return UIDefaultsLoader.parseValue( key, value );
|
||||
}
|
||||
|
||||
private static void reSetLookAndFeel() {
|
||||
EventQueue.invokeLater( () -> {
|
||||
LookAndFeel lookAndFeel = UIManager.getLookAndFeel();
|
||||
|
||||
@@ -27,6 +27,8 @@ import java.awt.event.MouseEvent;
|
||||
import java.awt.geom.Ellipse2D;
|
||||
import java.awt.geom.Path2D;
|
||||
import java.awt.geom.RoundRectangle2D;
|
||||
import java.beans.PropertyChangeListener;
|
||||
import java.util.Map;
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.JSlider;
|
||||
import javax.swing.LookAndFeel;
|
||||
@@ -34,6 +36,8 @@ import javax.swing.SwingUtilities;
|
||||
import javax.swing.UIManager;
|
||||
import javax.swing.plaf.ComponentUI;
|
||||
import javax.swing.plaf.basic.BasicSliderUI;
|
||||
import com.formdev.flatlaf.FlatClientProperties;
|
||||
import com.formdev.flatlaf.util.Graphics2DProxy;
|
||||
import com.formdev.flatlaf.util.HiDPIUtils;
|
||||
import com.formdev.flatlaf.util.UIScale;
|
||||
|
||||
@@ -90,6 +94,7 @@ public class FlatSliderUI
|
||||
protected Color disabledTrackColor;
|
||||
protected Color disabledThumbColor;
|
||||
protected Color disabledThumbBorderColor;
|
||||
protected Color tickColor;
|
||||
|
||||
private Color defaultBackground;
|
||||
private Color defaultForeground;
|
||||
@@ -98,6 +103,7 @@ public class FlatSliderUI
|
||||
protected boolean thumbPressed;
|
||||
|
||||
private Object[] oldRenderingHints;
|
||||
private Map<String, Object> oldStyleValues;
|
||||
|
||||
public static ComponentUI createUI( JComponent c ) {
|
||||
return new FlatSliderUI();
|
||||
@@ -107,6 +113,13 @@ public class FlatSliderUI
|
||||
super( null );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void installUI( JComponent c ) {
|
||||
super.installUI( c );
|
||||
|
||||
applyStyle( FlatStyleSupport.getStyle( slider ) );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void installDefaults( JSlider slider ) {
|
||||
super.installDefaults( slider );
|
||||
@@ -134,6 +147,7 @@ public class FlatSliderUI
|
||||
disabledTrackColor = UIManager.getColor( "Slider.disabledTrackColor" );
|
||||
disabledThumbColor = UIManager.getColor( "Slider.disabledThumbColor" );
|
||||
disabledThumbBorderColor = FlatUIUtils.getUIColor( "Slider.disabledThumbBorderColor", "Component.disabledBorderColor" );
|
||||
tickColor = FlatUIUtils.getUIColor( "Slider.tickColor", Color.BLACK ); // see BasicSliderUI.paintTicks()
|
||||
|
||||
defaultBackground = UIManager.getColor( "Slider.background" );
|
||||
defaultForeground = UIManager.getColor( "Slider.foreground" );
|
||||
@@ -155,9 +169,12 @@ public class FlatSliderUI
|
||||
disabledTrackColor = null;
|
||||
disabledThumbColor = null;
|
||||
disabledThumbBorderColor = null;
|
||||
tickColor = null;
|
||||
|
||||
defaultBackground = null;
|
||||
defaultForeground = null;
|
||||
|
||||
oldStyleValues = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -165,6 +182,57 @@ public class FlatSliderUI
|
||||
return new FlatTrackListener();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected PropertyChangeListener createPropertyChangeListener( JSlider slider ) {
|
||||
PropertyChangeListener superListener = super.createPropertyChangeListener( slider );
|
||||
return e -> {
|
||||
superListener.propertyChange( e );
|
||||
|
||||
switch( e.getPropertyName() ) {
|
||||
case FlatClientProperties.COMPONENT_STYLE:
|
||||
applyStyle( FlatStyleSupport.toString( e.getNewValue() ) );
|
||||
slider.revalidate();
|
||||
slider.repaint();
|
||||
break;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @since TODO
|
||||
*/
|
||||
protected void applyStyle( String style ) {
|
||||
oldStyleValues = FlatStyleSupport.parse( oldStyleValues, style, this::applyStyleProperty );
|
||||
}
|
||||
|
||||
/**
|
||||
* @since TODO
|
||||
*/
|
||||
protected Object applyStyleProperty( String key, Object value ) {
|
||||
Object oldValue;
|
||||
switch( key ) {
|
||||
case "trackWidth": oldValue = trackWidth; trackWidth = (int) value; break;
|
||||
case "thumbSize": oldValue = thumbSize; thumbSize = (Dimension) value; break;
|
||||
case "focusWidth": oldValue = focusWidth; focusWidth = (int) value; break;
|
||||
|
||||
case "trackValueColor": oldValue = trackValueColor; trackValueColor = (Color) value; break;
|
||||
case "trackColor": oldValue = trackColor; trackColor = (Color) value; break;
|
||||
case "thumbColor": oldValue = thumbColor; thumbColor = (Color) value; break;
|
||||
case "thumbBorderColor": oldValue = thumbBorderColor; thumbBorderColor = (Color) value; break;
|
||||
case "focusedColor": oldValue = focusedColor; focusedColor = (Color) value; break;
|
||||
case "focusedThumbBorderColor": oldValue = focusedThumbBorderColor; focusedThumbBorderColor = (Color) value; break;
|
||||
case "hoverThumbColor": oldValue = hoverThumbColor; hoverThumbColor = (Color) value; break;
|
||||
case "pressedThumbColor": oldValue = pressedThumbColor; pressedThumbColor = (Color) value; break;
|
||||
case "disabledTrackColor": oldValue = disabledTrackColor; disabledTrackColor = (Color) value; break;
|
||||
case "disabledThumbColor": oldValue = disabledThumbColor; disabledThumbColor = (Color) value; break;
|
||||
case "disabledThumbBorderColor": oldValue = disabledThumbBorderColor; disabledThumbBorderColor = (Color) value; break;
|
||||
case "tickColor": oldValue = tickColor; tickColor = (Color) value; break;
|
||||
|
||||
default: throw new IllegalArgumentException( "unknown style '" + key + "'" );
|
||||
}
|
||||
return oldValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getBaseline( JComponent c, int width, int height ) {
|
||||
if( c == null )
|
||||
@@ -306,6 +374,19 @@ debug*/
|
||||
((Graphics2D)g).fill( track );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void paintTicks( Graphics g ) {
|
||||
// because BasicSliderUI.paintTicks() always uses
|
||||
// g.setColor( UIManager.getColor("Slider.tickColor") )
|
||||
// we override this method and use our tickColor field to allow styling
|
||||
super.paintTicks( new Graphics2DProxy( (Graphics2D) g ) {
|
||||
@Override
|
||||
public void setColor( Color c ) {
|
||||
super.setColor( tickColor );
|
||||
}
|
||||
} );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void paintThumb( Graphics g ) {
|
||||
Color thumbColor = getThumbColor();
|
||||
|
||||
@@ -0,0 +1,108 @@
|
||||
/*
|
||||
* Copyright 2021 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.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.function.BiFunction;
|
||||
import javax.swing.JComponent;
|
||||
import com.formdev.flatlaf.FlatClientProperties;
|
||||
import com.formdev.flatlaf.FlatLaf;
|
||||
import com.formdev.flatlaf.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Support for styling components in CSS syntax.
|
||||
*
|
||||
* @author Karl Tauber
|
||||
* @since TODO
|
||||
*/
|
||||
public class FlatStyleSupport
|
||||
{
|
||||
/**
|
||||
* Parses styles in CSS syntax ("key1: value1; key2: value2; ..."),
|
||||
* converts the value strings into binary and invokes the given function
|
||||
* to apply the properties.
|
||||
*
|
||||
* @param oldStyleValues map of old values modified by the previous invocation, or {@code null}
|
||||
* @param style the style in CSS syntax
|
||||
* @param applyProperty function that is invoked to apply the properties;
|
||||
* first parameter is the key, second the binary value;
|
||||
* the function must return the old value
|
||||
* @return map of old values modified by the given style, or {@code null}
|
||||
* @throws IllegalArgumentException on syntax errors
|
||||
*/
|
||||
public static Map<String, Object> parse( Map<String, Object> oldStyleValues,
|
||||
String style, BiFunction<String, Object, Object> applyProperty ) throws IllegalArgumentException
|
||||
{
|
||||
// restore previous values
|
||||
if( oldStyleValues != null ) {
|
||||
for( Entry<String, Object> e : oldStyleValues.entrySet() )
|
||||
applyProperty.apply( e.getKey(), e.getValue() );
|
||||
}
|
||||
|
||||
// ignore empty style
|
||||
if( style == null || style.trim().isEmpty() )
|
||||
return null;
|
||||
|
||||
Map<String, Object> oldValues = null;
|
||||
|
||||
// split style into parts and process them
|
||||
for( String part : StringUtils.split( style, ';' ) ) {
|
||||
// ignore empty parts
|
||||
part = part.trim();
|
||||
if( part.isEmpty() )
|
||||
continue;
|
||||
|
||||
// find separator colon
|
||||
int sepIndex = part.indexOf( ':' );
|
||||
if( sepIndex < 0 )
|
||||
throw new IllegalArgumentException( "missing colon in '" + part + "'" );
|
||||
|
||||
// split into key and value
|
||||
String key = part.substring( 0, sepIndex ).trim();
|
||||
String value = part.substring( sepIndex + 1 ).trim();
|
||||
if( key.isEmpty() )
|
||||
throw new IllegalArgumentException( "missing key in '" + part + "'" );
|
||||
if( value.isEmpty() )
|
||||
throw new IllegalArgumentException( "missing value in '" + part + "'" );
|
||||
|
||||
// parse value string and convert it into binary value
|
||||
Object val = FlatLaf.parseDefaultsValue( key, value );
|
||||
Object oldValue = applyProperty.apply( key, val );
|
||||
|
||||
// remember previous value
|
||||
if( oldValues == null )
|
||||
oldValues = new HashMap<>();
|
||||
oldValues.put( key, oldValue );
|
||||
}
|
||||
|
||||
return oldValues;
|
||||
}
|
||||
|
||||
public static boolean hasStyle( JComponent c ) {
|
||||
return getStyle( c ) != null;
|
||||
}
|
||||
|
||||
public static String getStyle( JComponent c ) {
|
||||
return toString( c.getClientProperty( FlatClientProperties.COMPONENT_STYLE ) );
|
||||
}
|
||||
|
||||
static String toString( Object style ) {
|
||||
return (style instanceof String) ? (String) style : null;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user