279 lines
7.1 KiB
D
279 lines
7.1 KiB
D
/* MinWin Component class
|
|
*
|
|
* A Component is the root of the widget inheritance tree.
|
|
*
|
|
* Written by Ben Hinkle and released to the public domain, as
|
|
* explained at http://creativecommons.org/licenses/publicdomain
|
|
* Report comments and bugs at dsource: http://www.dsource.org/projects/minwin
|
|
*/
|
|
|
|
module minwin.component;
|
|
|
|
public {
|
|
import minwin.multidg;
|
|
import minwin.geometry;
|
|
import minwin.peer;
|
|
}
|
|
private {
|
|
import minwin.event;
|
|
import minwin.layout;
|
|
import minwin.logging;
|
|
}
|
|
|
|
abstract class Component {
|
|
|
|
// Peer management
|
|
mixin PeerMixin!();
|
|
|
|
// disposePeer will dipose of the system resource associated
|
|
// with this object if it owns the peer and the object does
|
|
// not have any parent. It is assumed that an object with a
|
|
// parent will have its peer disposed when the parent is disposed.
|
|
void disposePeer();
|
|
|
|
PeerForAdd getPeerForAdd() { return null; }
|
|
|
|
void getPeerOffset(inout int x, inout int y) {
|
|
x = 0;
|
|
y = 0;
|
|
}
|
|
|
|
void dispose() {
|
|
Component ch = child;
|
|
while (ch !is null) {
|
|
ch.dispose();
|
|
ch = ch.next;
|
|
}
|
|
disposePeer();
|
|
}
|
|
|
|
// Name property used for resource lookup on X and debugging
|
|
char[] name;
|
|
|
|
// Application data associative array for user data
|
|
void*[char[]] userdata;
|
|
|
|
void doCommand(int cmd) {
|
|
if (parent)
|
|
parent.doCommand(cmd);
|
|
}
|
|
|
|
void requestFocus() {}
|
|
|
|
// size management
|
|
void getMinimumSize(inout int width, inout int height) {
|
|
//todo: ask layout
|
|
width = 0;
|
|
height = 0;
|
|
}
|
|
void getMaximumSize(inout int width, inout int height) {
|
|
//todo: ask layout
|
|
width = int.max;
|
|
height = int.max;
|
|
}
|
|
abstract void getBounds(inout Rect r);
|
|
abstract void setBounds(inout Rect r);
|
|
Rect bounds() {
|
|
Rect r;
|
|
getBounds(r);
|
|
return r;
|
|
}
|
|
void bounds(Rect r) {
|
|
setBounds(r);
|
|
}
|
|
void getLayoutBounds(inout Rect r) {
|
|
Point s = size;
|
|
version(LOG)log.writefln("getLayoutBounds got size %d %d",s.x,s.y);
|
|
r.LTWH(0,0,s.x,s.y);
|
|
}
|
|
void size(Point s);
|
|
Point size() {
|
|
Rect bounds;
|
|
getBounds(bounds);
|
|
version(LOG)log.writefln("size function got width,height %d %d",bounds.width,bounds.height);
|
|
Point res;
|
|
res.x = bounds.width;
|
|
res.y = bounds.height;
|
|
return res;
|
|
}
|
|
int width() {
|
|
Rect bounds;
|
|
getBounds(bounds);
|
|
return bounds.width;
|
|
}
|
|
int height() {
|
|
Rect bounds;
|
|
getBounds(bounds);
|
|
return bounds.height;
|
|
}
|
|
|
|
// property to request a particular width no matter what layout
|
|
int userPreferredWidth_data;
|
|
final int userPreferredWidth() { return userPreferredWidth_data; }
|
|
void userPreferredWidth(int width) {
|
|
userPreferredWidth_data = width;
|
|
// childLayoutDirty = true;
|
|
// repaint();
|
|
}
|
|
|
|
// property to request a particular height no matter what layout
|
|
int userPreferredHeight_data;
|
|
final int userPreferredHeight() { return userPreferredHeight_data; }
|
|
void userPreferredHeight(int height) {
|
|
userPreferredHeight_data = height;
|
|
// childLayoutDirty = true;
|
|
// repaint();
|
|
}
|
|
|
|
void userPreferredSize(int width, int height) {
|
|
userPreferredHeight = height;
|
|
userPreferredWidth = width;
|
|
}
|
|
|
|
Point preferredSize(){
|
|
Point p;
|
|
if (layoutMgr) {
|
|
p = layoutMgr.preferredSize(this);
|
|
}
|
|
if (userPreferredWidth() > 0)
|
|
p.x = userPreferredWidth();
|
|
if (userPreferredHeight() > 0)
|
|
p.y = userPreferredHeight();
|
|
return p;
|
|
}
|
|
|
|
// visible property
|
|
void visible(bool vis);
|
|
bool visible();
|
|
|
|
// enabled property
|
|
void enabled(bool ena);
|
|
bool enabled();
|
|
|
|
Component getRootAncestor() {
|
|
Component top = this;
|
|
while (top.parent !is null) {
|
|
top = top.parent;
|
|
}
|
|
return top;
|
|
}
|
|
|
|
// hierarchy management
|
|
Component parent;
|
|
Component child;
|
|
Component next;
|
|
Component prev;
|
|
|
|
Component last() {
|
|
return child ? child.prev : null;
|
|
}
|
|
|
|
// loop over children
|
|
int opApply(int delegate(inout Component c) dg) {
|
|
int res = 0;
|
|
Component c = child;
|
|
if (c) {
|
|
do {
|
|
res = dg(c);
|
|
c = c.next;
|
|
} while(!res && c !is child);
|
|
}
|
|
return res;
|
|
}
|
|
|
|
// link two component as siblings.
|
|
// should an api be exposed to re-arrange without getting the peer?
|
|
void link(Component left, Component right) {
|
|
left.next = right;
|
|
right.prev = left;
|
|
}
|
|
|
|
void opCatAssign(Component x) {
|
|
addChild(x);
|
|
}
|
|
|
|
void addChild(Component x) {
|
|
x.parent = this;
|
|
if (!child) {
|
|
child = x;
|
|
x.prev = x.next = x;
|
|
} else {
|
|
link(last,x);
|
|
link(x,child);
|
|
}
|
|
}
|
|
|
|
void removeChild(Component x) {
|
|
Component old = last;
|
|
if (x is child) {
|
|
if (child is last)
|
|
child = null;
|
|
else
|
|
child = child.next;
|
|
}
|
|
link(x.prev,x.next);
|
|
x.prev = null;
|
|
x.next = null;
|
|
x.parent = null;
|
|
}
|
|
|
|
// layout management. TODO: invalidate and repaint on change
|
|
LayoutManager layoutMgr;
|
|
bool childLayoutDirty = true;
|
|
|
|
bool parentOwnsLayout_data = true;
|
|
bool parentOwnsLayout() { return parentOwnsLayout_data; }
|
|
void parentOwnsLayout(bool x) {
|
|
parentOwnsLayout_data = x;
|
|
if (parent) {
|
|
parent.childLayoutDirty = true;
|
|
parent.repaint();
|
|
}
|
|
}
|
|
|
|
bool parentOwnsVisibility = true;
|
|
|
|
void layout(bool validateParent) {
|
|
if (parent && validateParent)
|
|
parent.layout(true);
|
|
if (childLayoutDirty) {
|
|
if (layoutMgr)
|
|
layoutMgr.layout(this);
|
|
childLayoutDirty = false;
|
|
}
|
|
foreach( Component ch; this) {
|
|
ch.layout(false);
|
|
}
|
|
}
|
|
|
|
void pack() {
|
|
int width,height;
|
|
Point s;
|
|
if (layoutMgr) {
|
|
s = layoutMgr.preferredSize(this);
|
|
version(LOG) log.writefln("preferred size %d %d",s.x,s.y);
|
|
size = s;
|
|
childLayoutDirty = true;
|
|
version(LOG) log.printf("in pack about to repaint %x\n",this);
|
|
repaint();
|
|
version(LOG) log.printf("in pack done repaint about to layout %x\n",this);
|
|
layout(false);
|
|
version(LOG) log.printf("in pack done layout %x\n",this);
|
|
}
|
|
}
|
|
|
|
// painting and graphics context management
|
|
void repaint() {
|
|
version(LOG) log.printf("in component.repaint for %x\n",this);
|
|
foreach ( Component c; this) {
|
|
version(LOG) log.printf(" repaint for child %x of %x\n",c,this);
|
|
c.repaint();
|
|
}
|
|
}
|
|
bool clearBackgroundOnPaint = true;
|
|
void repaint(inout Rect r) {}
|
|
}
|
|
|
|
class WindowChild : Component {
|
|
WindowChildPeer getPeer() { return null; }
|
|
}
|