Dave Jarvis' Repositories

git clone https://repo.autonoma.ca/repo/kmcaster.git

Fix paint event collapse by sync'ing

Author DaveJarvis <email>
Date 2020-07-26 19:27:29 GMT-0700
Commit 1d59bf3b2346e8c887987d4f5f7439b32ed06401
Parent 519fc54
Delta 350 lines added, 338 lines removed, 12-line increase
src/main/com/whitemagicsoftware/kmcaster/EventHandler.java
import static com.whitemagicsoftware.kmcaster.HardwareState.SWITCH_RELEASED;
import static com.whitemagicsoftware.kmcaster.ui.Constants.*;
+import static java.awt.Toolkit.getDefaultToolkit;
import static javax.swing.SwingConstants.*;
+import static javax.swing.SwingUtilities.invokeLater;
/**
@Override
public void propertyChange( final PropertyChangeEvent e ) {
+ invokeLater(
+ () -> {
+ update( e );
+
+ // Prevent collapsing multiple paint events.
+ getDefaultToolkit().sync();
+ }
+ );
+ }
+
+ /**
+ * Called to update the user interface after a keyboard or mouse event
+ * has fired. This must be invoked from Swing's event dispatch thread.
+ *
+ * @param e Contains the identifier for the switch, its previous value,
+ * and its new value.
+ */
+ private void update( final PropertyChangeEvent e ) {
final var switchName = e.getPropertyName();
final var oldValue = e.getOldValue().toString();
src/main/com/whitemagicsoftware/kmcaster/listeners/KeyboardListener.java
import static java.util.Map.entry;
import static java.util.Optional.ofNullable;
-import static javax.swing.SwingUtilities.invokeLater;
-import static org.jnativehook.keyboard.NativeKeyEvent.getKeyText;
-
-/**
- * Responsible for sending property change events for keyboard state changes.
- */
-public final class KeyboardListener
- extends PropertyDispatcher<HardwareSwitch>
- implements NativeKeyListener {
- private final static String KEY_SPACE = "Space";
- private final static String KEY_BACKSPACE = "Back ⌫";
- private final static String KEY_TAB = "Tab ↹";
- private final static String KEY_ENTER = "Enter ⏎";
-
- /**
- * The key is the raw key code return from the {@link NativeKeyEvent}, the
- * value is the human-readable text to display on screen.
- */
- @SuppressWarnings("JavacQuirks")
- private final static Map<Integer, String> KEY_CODES =
- Map.ofEntries(
- entry( 32, KEY_SPACE ),
- entry( 33, "!" ),
- entry( 34, "\"" ),
- entry( 35, "#" ),
- entry( 36, "$" ),
- entry( 37, "%" ),
- entry( 38, "&" ),
- entry( 39, "'" ),
- entry( 40, "(" ),
- entry( 41, ")" ),
- entry( 42, "*" ),
- entry( 43, "+" ),
- entry( 44, "," ),
- entry( 45, "-" ),
- entry( 46, "." ),
- entry( 47, "/" ),
- entry( 58, ":" ),
- entry( 59, ";" ),
- entry( 60, "<" ),
- entry( 61, "=" ),
- entry( 62, ">" ),
- entry( 63, "?" ),
- entry( 64, "@" ),
- entry( 91, "[" ),
- entry( 92, "\\" ),
- entry( 93, "]" ),
- entry( 94, "^" ),
- entry( 95, "_" ),
- entry( 96, "`" ),
- entry( 97, "a" ),
- entry( 98, "b" ),
- entry( 99, "c" ),
- entry( 100, "d" ),
- entry( 101, "e" ),
- entry( 102, "f" ),
- entry( 103, "g" ),
- entry( 104, "h" ),
- entry( 105, "i" ),
- entry( 106, "j" ),
- entry( 107, "k" ),
- entry( 108, "l" ),
- entry( 109, "m" ),
- entry( 110, "n" ),
- entry( 111, "o" ),
- entry( 112, "p" ),
- entry( 113, "q" ),
- entry( 114, "r" ),
- entry( 115, "s" ),
- entry( 116, "t" ),
- entry( 117, "u" ),
- entry( 118, "v" ),
- entry( 119, "w" ),
- entry( 120, "x" ),
- entry( 121, "y" ),
- entry( 122, "z" ),
- entry( 123, "{" ),
- entry( 124, "|" ),
- entry( 125, "}" ),
- entry( 126, "~" ),
- entry( 65056, KEY_TAB ),
- entry( 65289, KEY_TAB ),
- entry( 65293, KEY_ENTER ),
- entry( 65288, KEY_BACKSPACE ),
- entry( 65301, "SysRq" ),
- entry( 65377, "Print" ),
- entry( 65361, "←" ),
- entry( 65362, "↑" ),
- entry( 65363, "→" ),
- entry( 65364, "↓" ),
- entry( 65307, "Esc" ),
- entry( 65365, "PgUp" ),
- entry( 65366, "PgDn" ),
- entry( 65379, "Ins" ),
- entry( 65535, "Del" ),
- entry( 65506, "Shift" ),
- entry( 65407, "Num" ),
- entry( 65421, "Num ⏎" ),
- entry( 65430, "Num ←" ),
- entry( 65431, "Num ↑" ),
- entry( 65432, "Num →" ),
- entry( 65433, "Num ↓" ),
- entry( 65429, "Num Home" ),
- entry( 65434, "Num PgUp" ),
- entry( 65435, "Num PgDn" ),
- entry( 65436, "Num End" ),
- entry( 65437, "Num Clear" ),
- entry( 65438, "Num Ins" ),
- entry( 65439, "Num Del" ),
- entry( 65450, "Num *" ),
- entry( 65451, "Num +" ),
- entry( 65452, "Num Sep" ),
- entry( 65453, "Num -" ),
- entry( 65454, "Num ." ),
- entry( 65455, "Num /" ),
- entry( 65456, "Num 0" ),
- entry( 65457, "Num 1" ),
- entry( 65458, "Num 2" ),
- entry( 65459, "Num 3" ),
- entry( 65460, "Num 4" ),
- entry( 65461, "Num 5" ),
- entry( 65462, "Num 6" ),
- entry( 65463, "Num 7" ),
- entry( 65464, "Num 8" ),
- entry( 65465, "Num 9" ),
- entry( 65300, "Scrl" ),
- entry( 65509, "Caps" )
- );
-
- /**
- * Whether a modifier key state is pressed or released depends on the state
- * of multiple keys (left and right). This map assigns the left and right
- * key codes to the same modifier key so that the physical state can be
- * represented by a single on-screen button (the logical state).
- * <p>
- * The 65511, 65512 are shifted alt key codes.
- * </p>
- */
- private final Map<Integer, HardwareSwitch> mModifierCodes =
- Map.ofEntries(
- entry( 65505, KEY_SHIFT ),
- entry( 65506, KEY_SHIFT ),
- entry( 65507, KEY_CTRL ),
- entry( 65508, KEY_CTRL ),
- entry( 65511, KEY_ALT ),
- entry( 65512, KEY_ALT ),
- entry( 65513, KEY_ALT ),
- entry( 65514, KEY_ALT )
- );
-
- /**
- * Most recently pressed non-modifier key value, empty signifies release.
- */
- private String mRegularHeld = "";
-
- /**
- * Stores the state of modifier keys. The contents of the map reflect the
- * state of each switch, so the reference can be final but not its contents.
- * An integer is used because keyboards usually have two separate keys for
- * each modifier, both can be pressed and released independently.
- */
- private final Map<HardwareSwitch, Integer> mModifiers = new HashMap<>();
-
- /**
- * Informing the application of a key release is delayed so that the user
- * interface will give the end user a momentary glance of what key was
- * pressed before it is released. Without this delay the keys disappear
- * as fast as a typist can type, which can be too quick to read as individual
- * keystrokes.
- * <p>
- * Track the number of key release timers are running so that they can
- * all be stopped to prevent releasing the key when another key has been
- * pressed in the mean time.
- * </p>
- */
- private final Stack<Timer> mTimerStack = new Stack<>();
-
- private final int mDelayRegular;
- private final int mDelayModifier;
-
- /**
- * Creates a keyboard listener that publishes events when keys are either
- * pressed or released. The constructor initializes all modifier keys to
- * the released state because the native keyboard hook API does not offer
- * a way to query what keys are currently pressed.
- *
- * @param delayRegular Milliseconds to wait before releasing a regular key.
- * @param delayModifier Milliseconds to wait before releasing a modifier key.
- */
- public KeyboardListener( final int delayRegular, final int delayModifier ) {
- mDelayRegular = delayRegular;
- mDelayModifier = delayModifier;
-
- for( final var key : modifierSwitches() ) {
- mModifiers.put( key, 0 );
- }
- }
-
- @Override
- public void nativeKeyPressed( final NativeKeyEvent e ) {
- getKey( e ).ifPresentOrElse(
- keyValue -> updateModifier( keyValue, 1 ),
- () -> {
- while( !mTimerStack.isEmpty() ) {
- mTimerStack.pop().stop();
- }
-
- invokeLater(
- () -> updateRegular( mRegularHeld, getDisplayText( e ) )
- );
- } );
- }
-
- @Override
- public void nativeKeyReleased( final NativeKeyEvent e ) {
- getKey( e ).ifPresentOrElse(
- keyValue -> delayedAction( mDelayModifier, ( action ) ->
- updateModifier( keyValue, -1 )
- ),
- () -> {
- final var timer = delayedAction( mDelayRegular, ( action ) ->
- invokeLater(
- () -> updateRegular( getDisplayText( e ), "" )
- )
- );
-
- mTimerStack.push( timer );
- }
- );
- }
-
- /**
- * Convenience method to start a one-time action at a relative time in
- * the future.
- *
- * @param delay When to perform the action.
- * @param listener The listener that will perform some future action.
- * @return The {@link Timer} that will perform a one-time action.
- */
- @SuppressWarnings("SameParameterValue")
- private Timer delayedAction(
- final int delay, final ActionListener listener ) {
- final var timer = new Timer( delay, listener );
-
- timer.setRepeats( false );
- timer.start();
-
- return timer;
- }
-
- /**
- * Unused. Key up and key down are tracked separately from a typed key.
- *
- * @param e Ignored.
- */
- @Override
- public void nativeKeyTyped( final NativeKeyEvent e ) {
- }
-
- /**
- * Sets the initial state of the modifiers.
- */
- public void initModifiers() {
- for( final var key : mModifiers.keySet() ) {
- final var state = mModifiers.get( key );
-
- // All modifiers keys are "false" by default, so firing fake transition
- // events from "true" to "false" will cause the GUI to repaint with the
- // text label affixed to each key, drawn in the released state. This
- // happens before the frame is set to visible.
- tryFire( key, state == 0, state == 1 );
- }
- }
-
- /**
- * Notifies of any modifier state changes. There's a bug whereby this
- * method is never called by the native library when both Left/Right Ctrl
- * keys are pressed followed by pressing either Shift key. Similarly,
- * holding both Left/Right Shift keys followed by pressing either Ctrl key
- * fails to call this method.
- *
- * @param key A modifier key.
- * @param increment {@code -1} means released, {@code 1} means pressed.
- */
- private void updateModifier( final HardwareSwitch key, final int increment ) {
- final var oldCount = mModifiers.get( key );
- final var newCount = oldCount + increment;
-
- // Only fire the event if the state has changed.
- tryFire( key, oldCount > 0, newCount > 0 );
- mModifiers.put( key, newCount );
- }
-
- /**
- * State for a regular (non-modifier) key has changed.
- *
- * @param o Previous key value.
- * @param n Current key value.
- */
- private void updateRegular( final String o, final String n ) {
- assert o != null;
- assert n != null;
-
- // Always fire the event, which permits double-key taps.
- fire( KEY_REGULAR, o, n );
- mRegularHeld = n;
- }
-
- /**
- * Looks up the key code for the given event. If the key code is not mapped,
- * this will return the default value from the native implementation.
- *
- * @param e The keyboard event that was triggered.
- * @return The human-readable name for the key relating to the event.
- */
- private String getDisplayText( final NativeKeyEvent e ) {
- return KEY_CODES.getOrDefault(
- e.getRawCode(), getKeyText( e.getKeyCode() )
- );
- }
-
- /**
- * Returns the modifier key that corresponds to the raw key code from
- * the given event. This is necessary to ensure that both left and right
- * modifier keys return the same {@link HardwareSwitch} value.
- *
- * @param e The event containing a raw key code to look up.
- * @return The switch matching the raw key code, or {@code null} if the
- * raw key code does not represent a modifier.
- */
- private Optional<HardwareSwitch> getKey( final NativeKeyEvent e ) {
- return ofNullable( mModifierCodes.get( e.getRawCode() ) );
- }
-
- @SuppressWarnings("unused")
- private void log( final String s, final NativeKeyEvent e ) {
- System.out.printf( "%s: %d %s%n", s, e.getRawCode(), getDisplayText( e ) );
+import static org.jnativehook.keyboard.NativeKeyEvent.getKeyText;
+
+/**
+ * Responsible for sending property change events for keyboard state changes.
+ */
+public final class KeyboardListener
+ extends PropertyDispatcher<HardwareSwitch>
+ implements NativeKeyListener {
+ private final static String KEY_SPACE = "Space";
+ private final static String KEY_BACKSPACE = "Back ⌫";
+ private final static String KEY_TAB = "Tab ↹";
+ private final static String KEY_ENTER = "Enter ⏎";
+
+ /**
+ * The key is the raw key code return from the {@link NativeKeyEvent}, the
+ * value is the human-readable text to display on screen.
+ */
+ @SuppressWarnings("JavacQuirks")
+ private final static Map<Integer, String> KEY_CODES =
+ Map.ofEntries(
+ entry( 32, KEY_SPACE ),
+ entry( 33, "!" ),
+ entry( 34, "\"" ),
+ entry( 35, "#" ),
+ entry( 36, "$" ),
+ entry( 37, "%" ),
+ entry( 38, "&" ),
+ entry( 39, "'" ),
+ entry( 40, "(" ),
+ entry( 41, ")" ),
+ entry( 42, "*" ),
+ entry( 43, "+" ),
+ entry( 44, "," ),
+ entry( 45, "-" ),
+ entry( 46, "." ),
+ entry( 47, "/" ),
+ entry( 58, ":" ),
+ entry( 59, ";" ),
+ entry( 60, "<" ),
+ entry( 61, "=" ),
+ entry( 62, ">" ),
+ entry( 63, "?" ),
+ entry( 64, "@" ),
+ entry( 91, "[" ),
+ entry( 92, "\\" ),
+ entry( 93, "]" ),
+ entry( 94, "^" ),
+ entry( 95, "_" ),
+ entry( 96, "`" ),
+ entry( 97, "a" ),
+ entry( 98, "b" ),
+ entry( 99, "c" ),
+ entry( 100, "d" ),
+ entry( 101, "e" ),
+ entry( 102, "f" ),
+ entry( 103, "g" ),
+ entry( 104, "h" ),
+ entry( 105, "i" ),
+ entry( 106, "j" ),
+ entry( 107, "k" ),
+ entry( 108, "l" ),
+ entry( 109, "m" ),
+ entry( 110, "n" ),
+ entry( 111, "o" ),
+ entry( 112, "p" ),
+ entry( 113, "q" ),
+ entry( 114, "r" ),
+ entry( 115, "s" ),
+ entry( 116, "t" ),
+ entry( 117, "u" ),
+ entry( 118, "v" ),
+ entry( 119, "w" ),
+ entry( 120, "x" ),
+ entry( 121, "y" ),
+ entry( 122, "z" ),
+ entry( 123, "{" ),
+ entry( 124, "|" ),
+ entry( 125, "}" ),
+ entry( 126, "~" ),
+ entry( 65056, KEY_TAB ),
+ entry( 65289, KEY_TAB ),
+ entry( 65293, KEY_ENTER ),
+ entry( 65288, KEY_BACKSPACE ),
+ entry( 65301, "SysRq" ),
+ entry( 65377, "Print" ),
+ entry( 65361, "←" ),
+ entry( 65362, "↑" ),
+ entry( 65363, "→" ),
+ entry( 65364, "↓" ),
+ entry( 65307, "Esc" ),
+ entry( 65365, "PgUp" ),
+ entry( 65366, "PgDn" ),
+ entry( 65379, "Ins" ),
+ entry( 65535, "Del" ),
+ entry( 65506, "Shift" ),
+ entry( 65407, "Num" ),
+ entry( 65421, "Num ⏎" ),
+ entry( 65430, "Num ←" ),
+ entry( 65431, "Num ↑" ),
+ entry( 65432, "Num →" ),
+ entry( 65433, "Num ↓" ),
+ entry( 65429, "Num Home" ),
+ entry( 65434, "Num PgUp" ),
+ entry( 65435, "Num PgDn" ),
+ entry( 65436, "Num End" ),
+ entry( 65437, "Num Clear" ),
+ entry( 65438, "Num Ins" ),
+ entry( 65439, "Num Del" ),
+ entry( 65450, "Num *" ),
+ entry( 65451, "Num +" ),
+ entry( 65452, "Num Sep" ),
+ entry( 65453, "Num -" ),
+ entry( 65454, "Num ." ),
+ entry( 65455, "Num /" ),
+ entry( 65456, "Num 0" ),
+ entry( 65457, "Num 1" ),
+ entry( 65458, "Num 2" ),
+ entry( 65459, "Num 3" ),
+ entry( 65460, "Num 4" ),
+ entry( 65461, "Num 5" ),
+ entry( 65462, "Num 6" ),
+ entry( 65463, "Num 7" ),
+ entry( 65464, "Num 8" ),
+ entry( 65465, "Num 9" ),
+ entry( 65300, "Scrl" ),
+ entry( 65509, "Caps" )
+ );
+
+ /**
+ * Whether a modifier key state is pressed or released depends on the state
+ * of multiple keys (left and right). This map assigns the left and right
+ * key codes to the same modifier key so that the physical state can be
+ * represented by a single on-screen button (the logical state).
+ * <p>
+ * The 65511, 65512 are shifted alt key codes (a.k.a. the meta key).
+ * </p>
+ */
+ private final Map<Integer, HardwareSwitch> mModifierCodes =
+ Map.ofEntries(
+ entry( 65505, KEY_SHIFT ),
+ entry( 65506, KEY_SHIFT ),
+ entry( 65507, KEY_CTRL ),
+ entry( 65508, KEY_CTRL ),
+ entry( 65511, KEY_ALT ),
+ entry( 65512, KEY_ALT ),
+ entry( 65513, KEY_ALT ),
+ entry( 65514, KEY_ALT )
+ );
+
+ /**
+ * Most recently pressed non-modifier key value, empty signifies release.
+ */
+ private String mRegularHeld = "";
+
+ /**
+ * Stores the state of modifier keys. The contents of the map reflect the
+ * state of each switch, so the reference can be final but not its contents.
+ * An integer is used because keyboards usually have two separate keys for
+ * each modifier, both can be pressed and released independently.
+ */
+ private final Map<HardwareSwitch, Integer> mModifiers = new HashMap<>();
+
+ /**
+ * Informing the application of a key release is delayed so that the user
+ * interface will give the end user a momentary glance of what key was
+ * pressed before it is released. Without this delay the keys disappear
+ * as fast as a typist can type, which can be too quick to read as individual
+ * keystrokes.
+ * <p>
+ * Track the number of key release timers are running so that they can
+ * all be stopped to prevent releasing the key when another key has been
+ * pressed in the mean time.
+ * </p>
+ */
+ private final Stack<Timer> mTimerStack = new Stack<>();
+
+ private final int mDelayRegular;
+ private final int mDelayModifier;
+
+ /**
+ * Creates a keyboard listener that publishes events when keys are either
+ * pressed or released. The constructor initializes all modifier keys to
+ * the released state because the native keyboard hook API does not offer
+ * a way to query what keys are currently pressed.
+ *
+ * @param delayRegular Milliseconds to wait before releasing a regular key.
+ * @param delayModifier Milliseconds to wait before releasing a modifier key.
+ */
+ public KeyboardListener( final int delayRegular, final int delayModifier ) {
+ mDelayRegular = delayRegular;
+ mDelayModifier = delayModifier;
+
+ for( final var key : modifierSwitches() ) {
+ mModifiers.put( key, 0 );
+ }
+ }
+
+ @Override
+ public void nativeKeyPressed( final NativeKeyEvent e ) {
+ getKey( e ).ifPresentOrElse(
+ keyValue -> updateModifier( keyValue, 1 ),
+ () -> {
+ while( !mTimerStack.isEmpty() ) {
+ mTimerStack.pop().stop();
+ }
+
+ updateRegular( mRegularHeld, getDisplayText( e ) );
+ } );
+ }
+
+ @Override
+ public void nativeKeyReleased( final NativeKeyEvent e ) {
+ getKey( e ).ifPresentOrElse(
+ keyValue -> delayedAction( mDelayModifier, ( action ) ->
+ updateModifier( keyValue, -1 )
+ ),
+ () -> {
+ final var timer = delayedAction( mDelayRegular, ( action ) ->
+ updateRegular( getDisplayText( e ), "" ) );
+
+ mTimerStack.push( timer );
+ }
+ );
+ }
+
+ /**
+ * Convenience method to start a one-time action at a relative time in
+ * the future.
+ *
+ * @param delay When to perform the action.
+ * @param listener The listener that will perform some future action.
+ * @return The {@link Timer} that will perform a one-time action.
+ */
+ @SuppressWarnings("SameParameterValue")
+ private Timer delayedAction(
+ final int delay, final ActionListener listener ) {
+ final var timer = new Timer( delay, listener );
+
+ timer.setRepeats( false );
+ timer.start();
+
+ return timer;
+ }
+
+ /**
+ * Unused. Key up and key down are tracked separately from a typed key.
+ *
+ * @param e Ignored.
+ */
+ @Override
+ public void nativeKeyTyped( final NativeKeyEvent e ) {
+ }
+
+ /**
+ * Sets the initial state of the modifiers.
+ */
+ public void initModifiers() {
+ for( final var key : mModifiers.keySet() ) {
+ final var state = mModifiers.get( key );
+
+ // All modifiers keys are "false" by default, so firing fake transition
+ // events from "true" to "false" will cause the GUI to repaint with the
+ // text label affixed to each key, drawn in the released state. This
+ // happens before the frame is set to visible.
+ tryFire( key, state == 0, state == 1 );
+ }
+ }
+
+ /**
+ * Notifies of any modifier state changes. There's a bug whereby this
+ * method is never called by the native library when both Left/Right Ctrl
+ * keys are pressed followed by pressing either Shift key. Similarly,
+ * holding both Left/Right Shift keys followed by pressing either Ctrl key
+ * fails to call this method.
+ *
+ * @param key Must be a modifier key.
+ * @param increment {@code -1} means released, {@code 1} means pressed.
+ */
+ private void updateModifier( final HardwareSwitch key, final int increment ) {
+ final var oldCount = mModifiers.get( key );
+ final var newCount = oldCount + increment;
+
+ // Only fire the event if the state has changed.
+ tryFire( key, oldCount > 0, newCount > 0 );
+ mModifiers.put( key, newCount );
+ }
+
+ /**
+ * State for a regular (non-modifier) key has changed.
+ *
+ * @param o Previous key value.
+ * @param n Current key value.
+ */
+ private void updateRegular( final String o, final String n ) {
+ assert o != null;
+ assert n != null;
+
+ // Always fire the event, which permits double-key taps.
+ fire( KEY_REGULAR, o, n );
+ mRegularHeld = n;
+ }
+
+ /**
+ * Looks up the key code for the given event. If the key code is not mapped,
+ * this will return the default value from the native implementation.
+ *
+ * @param e The keyboard event that was triggered.
+ * @return The human-readable name for the key relating to the event.
+ */
+ private String getDisplayText( final NativeKeyEvent e ) {
+ return KEY_CODES.getOrDefault(
+ e.getRawCode(), getKeyText( e.getKeyCode() )
+ );
+ }
+
+ /**
+ * Returns the modifier key that corresponds to the raw key code from
+ * the given event. This is necessary to ensure that both left and right
+ * modifier keys return the same {@link HardwareSwitch} value.
+ *
+ * @param e The event containing a raw key code to look up.
+ * @return The switch matching the raw key code, or {@code null} if the
+ * raw key code does not represent a modifier.
+ */
+ private Optional<HardwareSwitch> getKey( final NativeKeyEvent e ) {
+ return ofNullable( mModifierCodes.get( e.getRawCode() ) );
}
}
src/main/com/whitemagicsoftware/kmcaster/ui/AutofitLabel.java
import java.awt.*;
+import static java.awt.Toolkit.getDefaultToolkit;
import static java.awt.event.HierarchyEvent.PARENT_CHANGED;
import static java.lang.Math.floor;
* Without following the prescribed steps, the label may display at an
* unexpected size.
- * </p>
* </p>
*
private void rescale() {
setFont( computeScaledFont() );
+ paintImmediately( getBounds() );
}
scaledPt = (minSizePt + maxSizePt) / 2;
}
+
+ graphics.dispose();
// Round down to guarantee fit.