diff dynamin/gui/control.d @ 0:aa4efef0f0b1

Initial commit of code.
author Jordan Miner <jminer7@gmail.com>
date Mon, 15 Jun 2009 22:10:48 -0500
parents
children 4029d5af7542
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dynamin/gui/control.d	Mon Jun 15 22:10:48 2009 -0500
@@ -0,0 +1,516 @@
+// Written in the D programming language
+// www.digitalmars.com/d/
+
+/*
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (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.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Dynamin library.
+ *
+ * The Initial Developer of the Original Code is Jordan Miner.
+ * Portions created by the Initial Developer are Copyright (C) 2006-2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Jordan Miner <jminer7@gmail.com>
+ *
+ */
+
+module dynamin.gui.control;
+
+import dynamin.all_core;
+import dynamin.all_painting;
+import dynamin.gui.container;
+import dynamin.gui.events;
+import dynamin.gui.window;
+import dynamin.gui.cursor;
+import tango.io.Stdout;
+
+Control hotControl;
+// the hot control is the one the mouse is over
+package void setHotControl(Control c) {
+	if(c !is hotControl) {
+		if(hotControl)
+			hotControl.mouseLeft(new EventArgs);
+		hotControl = c;
+		if(c)
+			c.mouseEntered(new EventArgs);
+	}
+}
+package Control getHotControl() { return hotControl; }
+Control captorControl;
+package void setCaptorControl(Control c) {
+	captorControl = c;
+}
+package Control getCaptorControl() { return captorControl; }
+
+/**
+ * The painting event is an exception to the rule that added handlers are called
+ * before whenPainting. The painting event is far more useful since
+ * added handles are called after the control's whenPainting has finished.
+ * Otherwise, anything handlers painted would likely be painted over when the
+ * control painted.
+ */
+class Control {
+protected:
+	bool _visible;
+	bool _focusable;
+	bool _focused;
+	package Point _location;
+	package Size _size;
+	string _text;
+	Color _backColor;
+	Color _foreColor;
+	Font _font;
+	Cursor _cursor;
+	Container _parent;
+	bool _elasticX, _elasticY;
+public:
+	/// This event occurs after the control has been moved.
+	Event!() moved;
+	/// Override this method in a subclass to handle the Moved event.
+	protected void whenMoved(EventArgs e) { }
+
+	/// This event occurs after the control has been resized.
+	Event!() resized;
+	/// Override this method in a subclass to handle the Resized event.
+	protected void whenResized(EventArgs e) { }
+
+	/// This event occurs after the mouse has entered the control.
+	Event!() mouseEntered;
+	/// Override this method in a subclass to handle the MouseEntered event.
+	protected void whenMouseEntered(EventArgs e) { }
+
+	/// This event occurs after the mouse has left the control.
+	Event!() mouseLeft;
+	/// Override this method in a subclass to handle the MouseLeft event.
+	protected void whenMouseLeft(EventArgs e) { }
+
+	/// This event occurs after a mouse button is pressed.
+	Event!(MouseEventArgs) mouseDown;
+	/// Override this method in a subclass to handle the MouseDown event.
+	protected void whenMouseDown(MouseEventArgs e) { }
+
+	/// This event occurs after a mouse button is released.
+	Event!(MouseEventArgs) mouseUp;
+	/// Override this method in a subclass to handle the MouseUp event.
+	protected void whenMouseUp(MouseEventArgs e) { }
+
+	/// This event occurs after the mouse has been moved.
+	Event!(MouseEventArgs) mouseMoved;
+	/// Override this method in a subclass to handle the MouseMoved event.
+	protected void whenMouseMoved(MouseEventArgs e) { }
+
+	/// This event occurs after the mouse has been moved.
+	Event!(MouseEventArgs) mouseDragged;
+	/// Override this method in a subclass to handle the MouseMoved event.
+	protected void whenMouseDragged(MouseEventArgs e) { }
+
+	/// This event occurs after the mouse wheel has been turned.
+	Event!(MouseTurnedEventArgs) mouseTurned;
+	/// Override this method in a subclass to handle the MouseTurned event.
+	protected void whenMouseTurned(MouseTurnedEventArgs e) { }
+
+	/// This event occurs after a key is pressed.
+	Event!(KeyEventArgs) keyDown;
+	/// Override this method in a subclass to handle the KeyDown event.
+	protected void whenKeyDown(KeyEventArgs e) { }
+
+	/// This event occurs after a character key is pressed.
+	Event!(KeyTypedEventArgs) keyTyped;
+	/// Override this method in a subclass to handle the KeyTyped event.
+	protected void whenKeyTyped(KeyTypedEventArgs e) { }
+
+	/// This event occurs after a key is released.
+	Event!(KeyEventArgs) keyUp;
+	/// Override this method in a subclass to handle the KeyUp event.
+	protected void whenKeyUp(KeyEventArgs e) { }
+
+	/// This event occurs when the control needs to be painted.
+	Event!(PaintingEventArgs) painting;
+	/// Override this method in a subclass to handle the painting event.
+	protected void whenPainting(PaintingEventArgs e) {
+		e.graphics.source = backColor;
+		e.graphics.paint();
+	}
+
+	protected void dispatchMouseEntered(EventArgs e) {
+		Cursor.setCurrent(this, _cursor);
+		mouseEntered.callHandlers(e);
+		mouseEntered.callMainHandler(e);
+	}
+	protected void dispatchMouseDown(MouseEventArgs e) {
+		setCaptorControl(this);
+		mouseDown.callHandlers(e);
+		mouseDown.callMainHandler(e);
+	}
+	protected void dispatchMouseUp(MouseEventArgs e) {
+		setCaptorControl(null);
+		mouseUp.callHandlers(e);
+		mouseUp.callMainHandler(e);
+	}
+	protected void dispatchMouseMoved(MouseEventArgs e) {
+		setHotControl(this);
+		mouseMoved.callHandlers(e);
+		mouseMoved.callMainHandler(e);
+	}
+	protected void dispatchMouseDragged(MouseEventArgs e) {
+		setHotControl(this);
+		mouseDragged.callHandlers(e);
+		mouseDragged.callMainHandler(e);
+	}
+	protected void dispatchMouseTurned(MouseTurnedEventArgs e) {
+		mouseTurned.callHandlers(e);
+		mouseTurned.callMainHandler(e);
+		if(!e.stopped && _parent)
+			_parent.mouseTurned(e);
+	}
+	protected void dispatchPainting(PaintingEventArgs e) {
+		e.graphics.save();
+		painting.callMainHandler(e);
+		e.graphics.restore();
+		e.graphics.save();
+		// TODO: every call to a handler should be wrapped in a save/restore
+		painting.callHandlers(e);
+		e.graphics.restore();
+	}
+	this() {
+		moved = new Event!()(&whenMoved);
+		resized = new Event!()(&whenResized);
+		mouseEntered = new Event!()(&whenMouseEntered, &dispatchMouseEntered);
+		mouseLeft = new Event!()(&whenMouseLeft);
+		mouseDown = new Event!(MouseEventArgs)(&whenMouseDown, &dispatchMouseDown);
+		mouseUp = new Event!(MouseEventArgs)(&whenMouseUp, &dispatchMouseUp);
+		mouseMoved = new Event!(MouseEventArgs)(&whenMouseMoved, &dispatchMouseMoved);
+		mouseDragged = new Event!(MouseEventArgs)(&whenMouseDragged, &dispatchMouseDragged);
+		mouseTurned = new Event!(MouseTurnedEventArgs)(&whenMouseTurned, &dispatchMouseTurned);
+		keyDown = new Event!(KeyEventArgs)(&whenKeyDown);
+		keyTyped = new Event!(KeyTypedEventArgs)(&whenKeyTyped);
+		keyUp = new Event!(KeyEventArgs)(&whenKeyUp);
+		painting = new Event!(PaintingEventArgs)(&whenPainting, &dispatchPainting);
+
+		_location = Point(0, 0);
+		_size = Size(100, 100);
+		_text = "";
+		_focusable = false;
+		_focused = false;
+		_visible = true;
+		_cursor = Cursor.Arrow;
+		_elasticX = false;
+		_elasticY = false;
+
+		// TODO: remove these when themes mature
+		_foreColor = Color.Black;
+		_font = new Font("Tahoma", 11);
+	}
+	protected Graphics quickCreateGraphics() {
+		if(_parent is null)
+			return null;
+		auto g = _parent.quickCreateGraphics();
+		g.translate(location);
+		return g;
+	}
+	/**
+	 * Sets the specified Graphics' font and source to this control's font
+	 * and foreground color. Also, clips to this control's rectangle.
+	 */
+	void setupGraphics(Graphics g) {
+		g.rectangle(0, 0, width, height);
+		g.clip();
+		g.font = font;
+		g.source = foreColor;
+	}
+	/**
+	 * Creates a Graphics, calls the specified delegate with it, and deletes
+	 * it to release resources.
+	 */
+	void withGraphics(void delegate(Graphics g) dg) {
+		auto g = quickCreateGraphics();
+		setupGraphics(g);
+		dg(g);
+		delete g;
+	}
+	/**
+	 *
+	 */
+	bool focused() {
+		return _focused;
+	}
+	/**
+	 *
+	 */
+	void focus() {
+		if(!_focusable)
+			return;
+		Control c = this;
+		while(c.parent)
+			c = c.parent;
+		if(auto win = cast(Window)c) {
+			if(win.focusedControl) {
+				win.focusedControl._focused = false;
+				win.focusedControl.repaint();
+			}
+			win.focusedControl = this;
+			_focused = true;
+			repaint();
+		}
+	}
+	/**
+	 * Returns whether this control is on the screen. A control is
+	 * on screen if one of its ancestors is a top level window. Whether or
+	 * not the control is visible has no bearing on this value. If a control
+	 * has no parent, then it is clearly not on the screen.
+	 */
+	bool onScreen() {
+		return _parent && _parent.onScreen;
+	}
+	/**
+	 * Gets the location of this control in screen coordinates. An exception is
+	 * thrown if this control is not on the screen.
+	 */
+	Point screenLocation() {
+		if(!_parent)
+			throw new Exception("control is not on screen");
+		return _parent.screenLocation + location;
+	}
+	/**
+	 * Converts the specified point in content coordinates into screen
+	 * coordinates. An exception is thrown if this control is not on the screen.
+	 */
+	Point contentToScreen(Point pt) { // TODO: content?? even on Window??
+		if(!_parent)
+			throw new Exception("control is not on screen");
+		return _parent.contentToScreen(pt + location);
+	}
+	/**
+	 * Converts the specified point in screen coordinates into content
+	 * coordinates. An exception is thrown if this control is not on the screen.
+	 */
+	Point screenToContent(Point pt) {
+		if(!_parent)
+			throw new Exception("control is not on screen");
+		return _parent.screenToContent(pt) - location; // TODO: borders
+	}
+	/**
+	 * Converts the specified point in this control's content coordinates
+	 * into the specified control's content coordinates. An exception is
+	 * thrown if this control is not on the screen.
+	 */
+	Point contentToContent(Point pt, Control c) {
+		return c.screenToContent(contentToScreen(pt));
+	}
+	/**
+	 * Returns whether the specified point is inside this control.
+	 */
+	bool contains(Point pt) {
+		return pt.x >= 0 && pt.y >= 0 && pt.x < width && pt.y < height;
+	}
+	/// ditto
+	bool contains(real x, real y) {
+		return contains(Point(x, y));
+	}
+	bool topLevel() { return false; }
+	// TODO: return NativeControl/Window?
+	Control getTopLevel() {
+		Control c = this;
+		while(c.parent)
+			c = c.parent;
+		return c.topLevel ? c : null;
+	}
+	/**
+	 * Gets this control's parent.
+	 */
+	Container parent() { return _parent; }
+	package void parent(Container c) {
+		_parent = c;
+		//ParentChanged(new EventArgs); // TODO: add event
+	}
+	/**
+	 * Gets or sets whether is control is visible. The default is true, except on top-level windows.
+	 */
+	//void visible(bool b) { visible = b; }
+	bool visible() { return _visible; }
+
+	/**
+	 * Gets or sets the location of this control in its parent's content
+	 * coordinates.
+	 * Examples:
+	 * -----
+	 * control.location = [5, 35];
+	 * control.location = Point(50, 50);
+	 * -----
+	 */
+	Point location() { return _location; }
+	/// ditto
+	void location(Point pt) {
+		if((cast(Window)_parent) !is null)
+			throw new Exception("cannot set location of a window's content");
+		pt.x = round(pt.x);
+		pt.y = round(pt.y);
+		repaint();
+		_location = pt;
+		repaint();
+		moved(new EventArgs);
+	}
+	/// ditto
+	void location(real[] pt) {
+		assert(pt.length == 2, "pt must be just an x and y");
+		location = Point(pt[0], pt[1]);
+	}
+
+	/**
+	 * Gets or sets the size of this control.
+	 * Examples:
+	 * -----
+	 * control.size = [75, 23];
+	 * control.size = Size(80, 20);
+	 * -----
+	 */
+	Size size() { return _size; }
+	/// ditto
+	void size(Size newSize) {
+		if(newSize.width < 0)
+			newSize.width = 0;
+		if(newSize.height < 0)
+			newSize.height = 0;
+		newSize.width = round(newSize.width);
+		newSize.height = round(newSize.height);
+		repaint();
+		_size = newSize;
+		repaint();
+		resized(new EventArgs);
+	}
+	/// ditto
+	void size(real[] newSize) {
+		assert(newSize.length == 2, "size must be just a width and height");
+		size = Size(newSize[0], newSize[1]);
+	}
+
+	/**
+	 * Gets the size at which this control looks the best. It is intended that
+	 * the control not be made smaller than this size, and only be made larger
+	 * if it is elastic, or if it needs to be aligned with other controls.
+	 *
+	 * This property should be overridden in subclasses to return a best size.
+	 */
+	Size bestSize() { return Size(100, 100); }
+	/**
+	 * Gets the distance from the top of this control to the baseline of
+	 * the first line of this control's text. If this control does not have
+	 * text, 0 may be returned.
+	 */
+	int baseline() { return 0; }
+
+	/// Same as location.x
+	real x() { return location.x; }
+	/// Same as location.y
+	real y() { return location.y; }
+	/// Same as size.width
+	real width() { return size.width; }
+	/// Same as size.height
+	real height() { return size.height; }
+
+	/**
+	 * Gets or sets whether this control is elastic horizontally or vertically.
+	 * If a control is elastic, then it is intended to be made larger than its
+	 * best size.
+	 */
+	bool elasticX() { return _elasticX; }
+	/// ditto
+	void elasticX(bool b) { _elasticX = b; }
+	/// ditto
+	bool elasticY() { return _elasticY; }
+	/// ditto
+	void elasticY(bool b) { _elasticY = b; }
+	/**
+	 * Gets or sets the text that this control shows.
+	 */
+	string text() { return _text.dup; }
+	/// ditto
+	void text(string str) {
+		_text = str.dup;
+		repaint();
+		//TextChanged(EventArgs e) // TODO: add event
+	}
+
+	/**
+	 * Gets or sets the background color of this control.
+	 */
+	Color backColor() { return _backColor; }
+	/// ditto
+	void backColor(Color c) {
+		_backColor = c;
+		repaint();
+	}
+
+	/**
+	 * Gets or sets the foreground color of this control.
+	 */
+	Color foreColor() { return _foreColor; }
+	/// ditto
+	void foreColor(Color c) {
+		_foreColor = c;
+		repaint();
+	}
+
+	/**
+	 * Gets or sets the font of this control uses to display text. A value of
+	 * null means that the font is unset. When the font is null, the
+	 * current theme's font is used. The default is null.
+	 */
+	Font font() {
+		// TODO: if font is null (unset), return from theme
+		//if(font is null)
+		//	return Theme.Current.Control_Font(this);
+		//else
+		return _font;
+	}
+	/// ditto
+	void font(Font f) {
+		_font = f;
+		repaint();
+	}
+	Cursor cursor() {
+		return _cursor;
+	}
+	void cursor(Cursor cur) {
+		if(_cursor is cur)
+			return;
+		_cursor = cur;
+		if(getHotControl() is this)
+			Cursor.setCurrent(this, _cursor);
+	}
+
+	/**
+	 * Causes the part of the control inside the specified
+	 * rectangle to be repainted. The rectangle is in content coordinates.
+	 *
+	 * The control will not be repainted before this method returns; rather,
+	 * the area is just marked as needing to be repainted. The next time there
+	 * are no other system events to be processed, a painting event will called.
+	 */
+	void repaint(Rect rect) {
+		// TODO: make sure that parts clipped off by the parent are not invalidated
+		if(_parent)
+			_parent.repaint(rect + location);
+	}
+	/// ditto
+	void repaint(real x, real y, real width, real height) {
+		repaint(Rect(x, y, width, height));
+	}
+	/**
+	 * Causes the entire control to be repainted.
+	 */
+	void repaint() {
+		repaint(Rect(0, 0, width, height));
+	}
+}
+
+