Add JBValue.UpdateTracker to track for JBUI.scale change

+ unit test
This commit is contained in:
Anton Tarasov
2018-04-11 18:37:02 +03:00
parent 30c8a1ebe8
commit 9eba4b437c
3 changed files with 249 additions and 2 deletions
@@ -0,0 +1,95 @@
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.util.ui;
import org.junit.Test;
import javax.swing.*;
import static junit.framework.TestCase.assertEquals;
/**
* Tests {@link JBValue}.
*
* @author tav
*/
public class JBValueTest extends TestScaleHelper {
@Test
public void testInt() {
JBUI.setUserScaleFactor(1);
JBValue value1 = JBUI.intValue(2);
JBValue.SelfCachedInteger value2 = new JBValue.SelfCachedInteger(2);
JBUI.setUserScaleFactor(2);
assertEquals(JBUI.scale(2), value1.get());
assertEquals(JBUI.scale(2), value2.get());
}
@Test
public void testFloat() {
JBUI.setUserScaleFactor(1);
JBValue.Float value1 = JBUI.floatValue(2.6f);
JBValue.SelfCachedFloat value2 = new JBValue.SelfCachedFloat(2.6f);
JBUI.setUserScaleFactor(2);
assertEquals(Math.round(JBUI.scale(2.6f)), value1.get());
assertEquals(Math.round(JBUI.scale(2.6f)), value2.get());
assertEquals(JBUI.scale(2.6f), value1.getFloat());
assertEquals(JBUI.scale(2.6f), value2.getFloat());
}
@Test
public void testUpdateTracker() {
JBUI.setUserScaleFactor(1);
JBValue.UpdateTracker tracker = new JBValue.UpdateTracker();
JBValue value1 = JBUI.intValue(1, tracker);
JBValue value2 = JBUI.intValue(2, tracker);
JBValue.Float value3 = JBUI.floatValue(3.6f, tracker);
JBUI.setUserScaleFactor(2);
assertEquals(JBUI.scale(1), value1.get());
assertEquals(JBUI.scale(2), value2.get());
assertEquals(Math.round(JBUI.scale(3.6f)), value3.get());
assertEquals(JBUI.scale(3.6f), value3.getFloat());
tracker.forget((JBValue.Cacheable)value3);
int scale = JBUI.scale(1);
JBUI.setUserScaleFactor(3);
assertEquals(Math.round(scale * 3.6f), value3.get());
assertEquals(scale * 3.6f, value3.getFloat());
tracker.dispose();
scale = JBUI.scale(1);
JBUI.setUserScaleFactor(1);
assertEquals(scale, value1.get());
assertEquals(scale * 2, value2.get());
}
@Test
public void testUIManagerInteger() {
JBUI.setUserScaleFactor(1);
String key = "JBValue.int";
UIManager.put(key, 2);
JBValue.UIManagerInteger value = new JBValue.UIManagerInteger(key);
JBUI.setUserScaleFactor(2);
assertEquals(JBUI.scale(2), value.get());
UIManager.put(key, 3);
assertEquals(JBUI.scale(3), value.get());
UIManager.put(key, null);
}
}
@@ -487,10 +487,18 @@ public class JBUI {
return new JBValue.Integer(value);
}
public static JBValue.Integer intValue(int value, JBValue.UpdateTracker tracker) {
return new JBValue.CachedInteger(value, tracker);
}
public static JBValue.Float floatValue(float value) {
return new JBValue.Float(value);
}
public static JBValue.Float floatValue(float value, JBValue.UpdateTracker tracker) {
return new JBValue.CachedFloat(value, tracker);
}
public static JBDimension size(int width, int height) {
return new JBDimension(width, height);
}
@@ -6,12 +6,28 @@ import org.jetbrains.annotations.NotNull;
import javax.swing.*;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.LinkedList;
import java.util.List;
import static com.intellij.ui.paint.PaintUtil.RoundingMode.ROUND;
/**
* A wrapper over an unscaled value which lazily scales it via {@link JBUI#scale}.
* A wrapper over an unscaled numeric value, auto-scaled via {@link JBUI#scale}.
* <p>
* Use {@link Integer} or {@link Float} classes for on-demand value scaling.
* <p>
* If the same JBValue object is used multiple times in a code block, then in order to save scaling ops,
* a {@link CachedInteger} or {@link CachedFloat} classes can be used instead. Either with a separate
* {@link UpdateTracker} (better to use for many JBValue objects), or with a dedicated one (for a single
* JBValue object) as in the {@link SelfCachedInteger} or {@link SelfCachedFloat} classes.
*
* @author tav
* @see JBUI#intValue(int)
* @see JBUI#intValue(int,UpdateTracker)
* @see JBUI#floatValue(float)
* @see JBUI#floatValue(float,UpdateTracker)
*/
public abstract class JBValue {
protected JBValue() {}
@@ -36,11 +52,18 @@ public abstract class JBValue {
*/
protected abstract float getUnscaled();
public interface Cacheable {
/**
* Scales and caches the value.
*/
void cache();
}
/**
* JBValue wrapper over an integer value in {@link UIManager}.
*/
public static class UIManagerInteger extends JBValue {
private final String key;
private final @NotNull String key;
public UIManagerInteger(@NotNull String key) {
this.key = key;
@@ -103,4 +126,125 @@ public abstract class JBValue {
return value;
}
}
/**
* Integer JBValue which caches its scaled value on JBUI.scale change.
*/
public static class CachedInteger extends Integer implements Cacheable {
private int cachedScaledValue;
protected CachedInteger(int value) {
super(value);
}
/**
* @param value unscaled value
* @param tracker updates the value
* @see JBUI#intValue(int, UpdateTracker)
*/
public CachedInteger(int value, @NotNull UpdateTracker tracker) {
super(value);
cachedScaledValue = JBUI.scale(value);
tracker.track(this);
}
@Override
public int get() {
return cachedScaledValue;
}
public void cache() {
cachedScaledValue = JBUI.scale((int)getUnscaled());
}
}
/**
* CachedInteger with a dedicated UpdateTracker.
*/
public static class SelfCachedInteger extends CachedInteger {
private final @NotNull UpdateTracker tracker = new UpdateTracker();
public SelfCachedInteger(int value) {
super(value);
this.tracker.track(this);
}
}
/**
* Float JBValue which caches its scaled value on JBUI.scale change.
*/
public static class CachedFloat extends Float implements Cacheable {
private float cachedScaledValue;
protected CachedFloat(float value) {
super(value);
}
/**
* @param value unscaled value
* @param tracker updates the value
* @see JBUI#floatValue(float, UpdateTracker)
*/
public CachedFloat(float value, UpdateTracker tracker) {
super(value);
cachedScaledValue = JBUI.scale(value);
tracker.track(this);
}
@Override
public int get() {
return ROUND.round(cachedScaledValue);
}
@Override
public float getFloat() {
return cachedScaledValue;
}
public void cache() {
cachedScaledValue = JBUI.scale(getUnscaled());
}
}
/**
* CachedFloat with a dedicated UpdateTracker.
*/
public static class SelfCachedFloat extends CachedFloat {
private final @NotNull UpdateTracker tracker = new UpdateTracker();
public SelfCachedFloat(float value) {
super(value);
this.tracker.track(this);
}
}
/**
* Tracks for a list of {@link Cacheable} values and auto-updates them on {@link JBUI#USER_SCALE_FACTOR_PROPERTY}} change.
*/
public static class UpdateTracker {
private final List<Cacheable> list = new LinkedList<Cacheable>();
private final PropertyChangeListener listener = new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
for (Cacheable value : list) value.cache();
}
};
public UpdateTracker() {
JBUI.addPropertyChangeListener(JBUI.USER_SCALE_FACTOR_PROPERTY, listener);
}
public void track(Cacheable value) {
list.add(value);
}
public void forget(Cacheable value) {
list.remove(value);
}
public void dispose() {
JBUI.removePropertyChangeListener(JBUI.USER_SCALE_FACTOR_PROPERTY, listener);
list.clear();
}
}
}