diff dynamin/gui/window.d @ 55:c138461bf845

Add focusing and other changes that are related like descendantAdded/Removed events, Window.activated event, and updating List. Window.state was also added, even though focusing does not depend on it.
author Jordan Miner <jminer7@gmail.com>
date Sat, 08 Aug 2009 15:42:27 -0500
parents 3738a2d0bac3
children 84beb40c1665
line wrap: on
line diff
--- a/dynamin/gui/window.d	Sat Aug 08 15:31:24 2009 -0500
+++ b/dynamin/gui/window.d	Sat Aug 08 15:42:27 2009 -0500
@@ -37,6 +37,7 @@
 import tango.io.Stdout;
 import tango.core.Exception;
 import tango.core.Thread;
+import tango.text.Util;
 
 ///
 static class Application {
@@ -109,6 +110,26 @@
 }
 
 /**
+ * The different states a window may be in. It may not be in more than one of
+ * these states at a time.
+ */
+enum WindowState {
+	/**
+	 * Specifies that the window is neither minimized or maximized.
+	 */
+	Normal,
+	/**
+	 * Specifies that the window is only visible as an icon and/or text on
+	 * the taskbar or dock.
+	 */
+	Minimized,
+	/**
+	 * Specifies that the window covers the screen in at least one direction.
+	 */
+	Maximized
+}
+
+/**
  * The different types of borders that a window may have.
  * These do not affect whether the window is resizable--
  * use Window.resizable for that.
@@ -154,21 +175,107 @@
 	mixin WindowBackend;
 	BorderSize _borderSize;
 	Window _owner;
+	package bool _active;
+	package WindowState _state;
 	WindowBorderStyle _borderStyle;
 	bool _resizable = true;
 	Panel _content;
+	bool _showFocus;
+	// _focusedControl might not be focused at the current time (that is
+	// getFocusedControl()), but will at least be focused when this
+	// window is active
 	Control _focusedControl;
 	package Control focusedControl() { return _focusedControl; }
 	package void focusedControl(Control c) {
 		_focusedControl = c;
+		if(active)
+			setFocusedControl(_focusedControl);
 	}
 	override void dispatchPainting(PaintingEventArgs e) {
 		Theme.current.Window_paint(this, e.graphics);
 		super.dispatchPainting(e);
 	}
+	override void whenDescendantAdded(HierarchyEventArgs e) {
+		super.whenDescendantAdded(e);
+		if(focusedControl is null && e.descendant.focusable) {
+				// && e.descendant.enabled) {
+			focusedControl = e.descendant;
+		}
+	}
+
+	//{{{ focusing
+	public override bool showFocus() { return _showFocus; }
+	override void whenKeyDown(KeyEventArgs e) {
+		if(e.key == Key.Tab) {
+			getNextFocusable().focus();
+			_showFocus = true;
+		}
+	}
+
+	// will not return null
+	Control getNextFocusable() {
+		Control foc = focusedControl;
+
+		Control[32] buffer;
+		auto des = getFocusableDescendants(buffer);
+		if(des.length == 0)
+			return this;
+		else if(des.length == 1)
+			return des[0];
+
+		int focI = locate(des, foc);
+
+		// look _after_ this control for one with the same tab index
+		foreach(c; des[focI+1..$])
+			if(c.tabIndex == foc.tabIndex)
+				return c;
+
+		// if none are found, look for the next largest tab index
+		// from the beginning of the array
+		Control smallest;
+		Control nextLargest;
+		foreach(c; des) {
+			if(c.tabIndex > foc.tabIndex)
+				if(nextLargest is null || c.tabIndex < nextLargest.tabIndex)
+					nextLargest = c;
+			if(smallest is null || c.tabIndex < smallest.tabIndex)
+				smallest = c;
+		}
+
+		if(nextLargest)
+			return nextLargest;
+		else
+			return smallest;
+	}
+
+	// will not return null
+	Control getPreviousFocusable() {
+		return null;
+	}
+	//}}}
+
 public:
+	/// Override this method in a subclass to handle the activated event.
+	protected void whenActivated(EventArgs e) {
+		setFocusedControl(_focusedControl is null ? content : _focusedControl);
+	}
+	/// This event occurs after this window is activated.
+	Event!(whenActivated) activated;
+
+	/// Override this method in a subclass to handle the deactivated event.
+	protected void whenDeactivated(EventArgs e) {
+		setFocusedControl(null);
+	}
+	/// This event occurs after this window is deactivated.
+	Event!(whenDeactivated) deactivated;
+
+	/**
+	 *
+	 */
 	this() {
-		_children = new ControlList();
+		activated.mainHandler = &whenActivated;
+		deactivated.mainHandler = &whenDeactivated;
+
 		content = new Panel;
 
 		_visible = false;
@@ -183,6 +290,13 @@
 		this.text = text;
 	}
 
+	/**
+	 *
+	 */
+	Panel content() {
+		return _content;
+	}
+	/// ditto
 	void content(Panel panel) {
 		if(panel is null)
 			throw new IllegalArgumentException("content must not be null");
@@ -228,9 +342,6 @@
 		_content.size = _size-_borderSize;
 		ignoreResize = false;
 	}
-	Panel content() {
-		return _content;
-	}
 
 	/**
 	 * If the handle has not yet been created, calling this will cause it to be.
@@ -293,6 +404,32 @@
 	}
 
 	/**
+	 *
+	 */
+	bool active() { return _active; }
+	/**
+	 *
+	 */
+	void activate() {
+		if(!handleCreated)
+			return;
+		backend_activate();
+	}
+
+	/**
+	 * Gets or sets whether the window's state is normal, minimized, or
+	 * maximized.
+	 */
+	WindowState state() { return _state; }
+	/// ditto
+	void state(WindowState s) {
+		_state = s;
+		if(!handleCreated)
+			return;
+		backend_state = s;
+	}
+
+	/**
 	 * Gets or sets what border this window will have around its contents.
 	 * The default is WindowBorderStyle.Normal.
 	 */