view dynamin/gui/button.d @ 78:651082a9b364

Add Event.setUp() and use in place of mainHandler and dispatcher.
author Jordan Miner <jminer7@gmail.com>
date Sat, 06 Feb 2010 15:12:52 -0600
parents 84beb40c1665
children 73060bc3f004
line wrap: on
line source

// 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.button;

import dynamin.all_core;
import dynamin.all_gui;
import dynamin.all_painting;
import dynamin.gui.control;
import dynamin.gui.events;
import tango.io.Stdout;
import dynamin.c.cairo; // TODO: remove

// TODO: maybe change to ControlState and add Disabled ?
enum ButtonState {
	Normal, Hot, Pressed
}
// TODO: move to another file
enum TextImageRelation {
	Overlay, TextBeforeImage, ImageBeforeText, TextAboveImage, ImageAboveText
}

/**
 * A button is a control that can be clicked to perform an action.
 *
 * The appearance of a button with Windows Classic:
 *
 * $(IMAGE ../web/example_button.png)
 */
class Button : Control {
protected:
	package bool _isDefault;
	ButtonState _state;
	override void whenFocusGained(EventArgs e) {
		super.whenFocusGained(e);
		auto anc = findAncestor((Container c) {
			return c.defaultButton !is null;
		});
		if(anc) {
			foreach(d; &anc.descendants) {
				if(auto b = cast(Button)d)
					b._isDefault = false;
			}
		}
		_isDefault = true;
	}
	override void whenFocusLost(EventArgs e) {
		super.whenFocusLost(e);
		_isDefault = false;
		auto anc = findAncestor((Container c) {
			return c.defaultButton !is null;
		});
		if(anc)
			anc.defaultButton._isDefault = true;
	}
	override void whenMouseDragged(MouseEventArgs e) {
		if(contains(e.x, e.y))
			state = ButtonState.Pressed;
		else
			state = ButtonState.Normal;
	}
	override void whenMouseEntered(EventArgs e) {
		state = ButtonState.Hot;
	}
	override void whenMouseLeft(EventArgs e) {
		state = ButtonState.Normal;
	}
	override void whenMouseDown(MouseEventArgs e) {
		if(e.button == MouseButton.Left) {
			state = ButtonState.Pressed;
			focus();
		}
	}
	override void whenMouseUp(MouseEventArgs e) {
		if(state != ButtonState.Pressed) return;
		state = ButtonState.Hot;
		clicked(new EventArgs);
	}
	override void whenKeyDown(KeyEventArgs e) {
		if(e.key == Key.Space) {
			state = ButtonState.Pressed;
			e.stopped = true;
		} else if(e.key == Key.Enter) { // being default is implied in keyDown
			scope e2 = new EventArgs;
			clicked(e2);
			e.stopped = true;
		}
	}
	override void whenKeyUp(KeyEventArgs e) {
		if(e.key == Key.Space) {
			if(state != ButtonState.Pressed) return;
			state = ButtonState.Normal;
			scope e2 = new EventArgs;
			clicked(e2);
			e.stopped = true;
		}
	}
	override void whenPainting(PaintingEventArgs e) {
		Theme.current.Button_paint(this, e.graphics);
		return;
		// TODO: move to a theme or delete
		with(e.graphics) {
			if(_state == ButtonState.Hot)
				source = Color(200, 0, 0);
			else if(_state == ButtonState.Pressed)
				source = Color(110, 0, 0);
			else
				source = Color(220, 80, 80);
			moveTo(3, 0);
			lineTo(width-3, 0);
			lineTo(width, 3);
			lineTo(width, height-3);
			lineTo(width-3, height);
			lineTo(3, height);
			lineTo(0, height-3);
			lineTo(0, 3);
			closePath();
			fill();
			auto grad = cairo_pattern_create_linear(0, 0, 0, height/2+3);
			cairo_pattern_add_color_stop_rgba(grad, 0, 1, 1, 1, .4);
			cairo_pattern_add_color_stop_rgba(grad, 1, 1, 1, 1, .05);
			cairo_set_source(handle, grad);
			cairo_pattern_destroy(grad);
			moveTo(3, 0);
			lineTo(width-3, 0);
			lineTo(width, 3);
			lineTo(width, cast(int)height/2-3);
			lineTo(width-3, cast(int)height/2);
			lineTo(3, cast(int)height/2);
			lineTo(0, cast(int)height/2+3);
			lineTo(0, 3);
			closePath();
			fill();

			source = _state == ButtonState.Pressed ? Color.White : Color.Black;
			fontSize = 13;
			drawText(text, 5, height/2);
		}
		return;

	}
	public void paintFore(Graphics g) {
		auto extents = g.getTextExtents(text);
		g.drawText(text, (width-extents.width)/2, (height-extents.height)/2);
	}

public:
	/// Override this method in a subclass to handle the clicked event.
	protected void whenClicked(EventArgs e) { }
	/// This event occurs after the button has been clicked.
	Event!(whenClicked) clicked;

	///
	this() {
		clicked.setUp(&whenClicked);
		_focusable = true;
	}
	///
	this(string text) {
		this();
		this.text = text;
	}
	override Size bestSize() { return Theme.current.Button_bestSize(this); }
	///
	ButtonState state() { return _state; }
	///
	void state(ButtonState s) { _state = s; repaint(); }
	/**
	  * Returns true if this button should be painted as being default.
	  * Mainly for themes.
	  */
	bool isDefault() { return _isDefault; }
}