266 lines
7.5 KiB
D
266 lines
7.5 KiB
D
|
/* MinWin Group class
|
||
|
*
|
||
|
* A Group is a lightweight component (meaning it has
|
||
|
* no peer) that contains and lays out other components.
|
||
|
*
|
||
|
* A GroupBox is a potentially heavyweight component that
|
||
|
* puts a box and label around other components.
|
||
|
*
|
||
|
* 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.group;
|
||
|
|
||
|
private {
|
||
|
import minwin.component;
|
||
|
import minwin.window;
|
||
|
import minwin.peerimpl;
|
||
|
import minwin.logging;
|
||
|
import std.string;
|
||
|
}
|
||
|
|
||
|
// lightweight group of other children - no peer
|
||
|
class Group : Component {
|
||
|
Rect bounds;
|
||
|
this(Component parent, char[] name = "") {
|
||
|
hasPeer = NO_PEER;
|
||
|
this.name = name;
|
||
|
if (parent)
|
||
|
parent.addChild(this);
|
||
|
}
|
||
|
void disposePeer(){}
|
||
|
PeerForAdd getPeerForAdd() {
|
||
|
return parent.getPeerForAdd();
|
||
|
}
|
||
|
void getPeerOffset(inout int x, inout int y) {
|
||
|
int px, py;
|
||
|
if (parent)
|
||
|
parent.getPeerOffset(px,py);
|
||
|
x = bounds.left+px;
|
||
|
y = bounds.top+py;
|
||
|
}
|
||
|
void getBounds(inout Rect r) {
|
||
|
r = bounds;
|
||
|
}
|
||
|
void setBounds(inout Rect r) {
|
||
|
bounds = r;
|
||
|
version(LOG) log.writefln("setting group bounds to %d %d %d %d",
|
||
|
r.left,r.top,r.width,r.height);
|
||
|
childLayoutDirty = true;
|
||
|
repaint();
|
||
|
}
|
||
|
void size(Point s) {
|
||
|
bounds.LTWH(bounds.left,bounds.top,s.x,s.y);
|
||
|
childLayoutDirty = true;
|
||
|
repaint();
|
||
|
}
|
||
|
void visible(bool vis) {
|
||
|
foreach(Component ch; this) {
|
||
|
ch.visible = vis;
|
||
|
}
|
||
|
childLayoutDirty = true;
|
||
|
layout(false);
|
||
|
}
|
||
|
bool visible() {
|
||
|
bool vis = false;
|
||
|
foreach(Component ch; this) {
|
||
|
vis = vis || ch.visible();
|
||
|
}
|
||
|
return vis;
|
||
|
}
|
||
|
void enabled(bool b) {
|
||
|
foreach(Component ch; this) {
|
||
|
ch.enabled = b;
|
||
|
}
|
||
|
}
|
||
|
bool enabled() {
|
||
|
bool b = false;
|
||
|
foreach(Component ch; this) {
|
||
|
b = b || ch.enabled;
|
||
|
}
|
||
|
return b;
|
||
|
}
|
||
|
}
|
||
|
private import minwin.layout;
|
||
|
unittest {
|
||
|
Group g = new Group(null);
|
||
|
g.layoutMgr = new FlowLayout;
|
||
|
Group g2 = new Group(g);
|
||
|
g2.layoutMgr = new FlowLayout;
|
||
|
g2.userPreferredSize(40,50);
|
||
|
Group g3 = new Group(g);
|
||
|
g3.layoutMgr = new FlowLayout;
|
||
|
Group g4 = new Group(g3);
|
||
|
g4.userPreferredSize(50,10);
|
||
|
Group g5 = new Group(g3);
|
||
|
g5.userPreferredSize(60,20);
|
||
|
Group g6 = new Group(g3);
|
||
|
g6.userPreferredSize(70,30);
|
||
|
g.pack();
|
||
|
Rect r2;
|
||
|
g.getBounds(r2);
|
||
|
assert( r2 == LTWH(0,0,70,110) );
|
||
|
|
||
|
g6.getBounds(r2);
|
||
|
assert( r2 == LTWH(0,30,70,30) );
|
||
|
g5.getBounds(r2);
|
||
|
assert( r2 == LTWH(5,10,60,20) );
|
||
|
g4.getBounds(r2);
|
||
|
assert( r2 == LTWH(10,0,50,10) );
|
||
|
|
||
|
int x,y;
|
||
|
g6.getPeerOffset(x,y);
|
||
|
assert( x == 0 );
|
||
|
assert( y == 80 );
|
||
|
}
|
||
|
|
||
|
version (MinWin32) {
|
||
|
|
||
|
private {
|
||
|
import minwin.app;
|
||
|
import minwin.mswindows;
|
||
|
import std.utf;
|
||
|
}
|
||
|
|
||
|
class GroupBox : WindowChild {
|
||
|
int labelHeight;
|
||
|
|
||
|
char[] text_data;
|
||
|
char[] text() { return text_data; }
|
||
|
void text(char[] c) {
|
||
|
text_data = c;
|
||
|
SendMessageX(peer,WM_SETTEXT,0,c);
|
||
|
}
|
||
|
|
||
|
this(Component parent, char[] text, char[] name = "") {
|
||
|
PeerForAdd parentp = parent.getPeerForAdd();
|
||
|
text_data = text;
|
||
|
peer = CreateWindowX("BUTTON",text,
|
||
|
BS_GROUPBOX | WS_CHILD | WS_VISIBLE,
|
||
|
0,0,10,10,parentp,
|
||
|
cast(HMENU)0,gApp.hInstance,null);
|
||
|
sysAssert(peer !is null, "Failed to create peer Button");
|
||
|
setWindowChildPeer(this,peer,OWNS_PEER);
|
||
|
this.name = name;
|
||
|
Font f = standardFont(StandardFont.Gui);
|
||
|
SendMessageA(peer,WM_SETFONT,cast(WPARAM)f.peer,0);
|
||
|
HDC dc = GetDC(peer);
|
||
|
HFONT oldFont = SelectObject(dc,f.peer);
|
||
|
TEXTMETRICA metrics;
|
||
|
GetTextMetricsA(dc,&metrics);
|
||
|
labelHeight = metrics.tmHeight;
|
||
|
SelectObject(dc,oldFont);
|
||
|
ReleaseDC(peer,dc);
|
||
|
parent.addChild(this);
|
||
|
}
|
||
|
|
||
|
|
||
|
void getLayoutBounds(inout Rect r) {
|
||
|
super.getLayoutBounds(r);
|
||
|
r.LTWH(r.left+2, r.top+labelHeight+2, r.width-4, r.height-labelHeight-4);
|
||
|
}
|
||
|
|
||
|
Point preferredSize() {
|
||
|
Point s;
|
||
|
if (layoutMgr) {
|
||
|
s = layoutMgr.preferredSize(this);
|
||
|
}
|
||
|
s.y = s.y + labelHeight+4;
|
||
|
s.x = s.x + 4;
|
||
|
if (userPreferredWidth() > 0)
|
||
|
s.x = userPreferredWidth();
|
||
|
if (userPreferredHeight() > 0)
|
||
|
s.y = userPreferredHeight();
|
||
|
return s;
|
||
|
}
|
||
|
mixin WindowChildImpl!();
|
||
|
}
|
||
|
|
||
|
} else version (GTK) {
|
||
|
|
||
|
private import minwin.gtk;
|
||
|
private import minwin.gtk_peers;
|
||
|
private import std.c.string;
|
||
|
|
||
|
class GroupBox : WindowChild {
|
||
|
|
||
|
MinWinGtkPeer* content;
|
||
|
|
||
|
char[] text() {
|
||
|
char* str = gtk_frame_get_label(cast(GtkFrame*)peer);
|
||
|
if (str is null)
|
||
|
return "";
|
||
|
else
|
||
|
return str[0..strlen(str)].dup;
|
||
|
}
|
||
|
void text(char[] c) {
|
||
|
gtk_frame_set_label(cast(GtkFrame*)peer,g_strdup(c.ptr));
|
||
|
}
|
||
|
|
||
|
this(Component parent, char[] text, char[] name = "") {
|
||
|
PeerForAdd parentp = parent.getPeerForAdd();
|
||
|
this.name = name;
|
||
|
char* str = toStringz(text);
|
||
|
peer = gtk_frame_new(str);
|
||
|
gtk_container_add(cast(GtkContainer*)parentp,peer);
|
||
|
|
||
|
// add our peer to hook into GTK size allocation algorithm
|
||
|
GtkWidget* wcontent = MinWinGtkPeer_new();
|
||
|
content = cast(MinWinGtkPeer*)wcontent;
|
||
|
gtk_widget_set_sensitive(wcontent,true);
|
||
|
content.sizeRequest = >kRequest;
|
||
|
content.sizeAllocate = >kAllocate;
|
||
|
gtk_container_add(cast(GtkContainer*)peer,wcontent);
|
||
|
|
||
|
setWindowChildPeer(this,peer,OWNS_PEER);
|
||
|
parent.addChild(this);
|
||
|
|
||
|
gtk_widget_realize(wcontent);
|
||
|
gtk_widget_show(wcontent);
|
||
|
gtk_widget_realize(peer);
|
||
|
visible = true;
|
||
|
}
|
||
|
|
||
|
mixin WindowChildImpl!();
|
||
|
|
||
|
PeerForAdd getPeerForAdd() {
|
||
|
return cast(GtkWidget*)content;
|
||
|
}
|
||
|
|
||
|
// callback from the GTK peer asking for preferred size
|
||
|
private void gtkRequest(GtkWidget *w, GtkRequisition* req) {
|
||
|
version(LOG)log.writefln("gtkRequest for groupbox");
|
||
|
Point s;
|
||
|
if (layoutMgr) {
|
||
|
s = layoutMgr.preferredSize(this);
|
||
|
req.width = s.x;
|
||
|
req.height = s.y;
|
||
|
} else {
|
||
|
req.width = 0;
|
||
|
req.height = 0;
|
||
|
}
|
||
|
version(LOG)log.writefln("done gtkRequest for groupbox");
|
||
|
}
|
||
|
|
||
|
// callback from GTK peer telling us our position
|
||
|
private void gtkAllocate(GtkWidget *w, GtkAllocation* req) {
|
||
|
version(LOG)log.writefln("gtkAllocate for groupbox");
|
||
|
if (layoutMgr) {
|
||
|
layoutMgr.layout(this);
|
||
|
}
|
||
|
childLayoutDirty = false;
|
||
|
version(LOG)log.writefln("done gtkAllocate for groupbox");
|
||
|
}
|
||
|
|
||
|
void getLayoutBounds(inout Rect r) {
|
||
|
GtkWidget* w = cast(GtkWidget*)content;
|
||
|
r = toRect(w.allocation);
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|