cmake-d/samples/minwin_gtk/minwin/canvas.d

273 lines
12 KiB
D

/* MinWin Canvas class
*
* A Canvas is a component that fires key and mouse events and has no
* other builtin capabilities
*
* 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.canvas;
private {
import minwin.component;
import minwin.event;
import minwin.app;
import minwin.paint;
import minwin.logging;
import minwin.window;
import minwin.peerimpl;
import minwin.image;
import std.string;
}
template SharedCanvasImpl() {
// events
MultiDelegate!(Component,KeyEvent*) keyDelegate;
MultiDelegate!(Component,MouseEvent*) mouseDelegate;
MultiDelegate!(Component,GContext) paintDelegate;
}
version (MinWin32) {
private import minwin.mswindows;
// generic heavyweight window child
class Canvas : WindowChild {
this(Component parent, char[] name = "") {
PeerForAdd parentp = parent.getPeerForAdd();
peer = CreateWindowA("MinWinCanvas","",
WS_CHILD | WS_VISIBLE,
0,0,10,10,parentp,
cast(HMENU)0,gApp.hInstance,null);
sysAssert(peer !is null, "Failed to create peer Canvas");
this.name = name;
version(LOG)log.printf("canvas ctor %x peer %x\n",this,peer);
setWindowChildPeer(this,peer,OWNS_PEER);
parent.addChild(this);
}
mixin SharedCanvasImpl!();
mixin WindowChildImpl!();
Point preferredSize(){
int width = 60;
int height = 60;
if (userPreferredWidth > 0)
width = userPreferredWidth;
if (userPreferredHeight > 0)
height = userPreferredHeight;
return XY(width,height);
}
void backgroundColor(Color c) {
// TODO
}
mixin CommonEventSourceImpl!();
int WindowProc(HWND hWnd, uint uMsg, WPARAM wParam, LPARAM lParam) {
version (LOG) log.printf(" in canvas windowproc msg %d\n",uMsg);
bool doDefault = true;
if (uMsg <= WM_KEYLAST && uMsg >= WM_KEYFIRST) {
version(LOG) log.writefln("got canvas key event %d %d",uMsg,wParam);
keyDelegate(this, cast(KeyEvent*)&gApp.event);
} else if (uMsg <= WM_MOUSELAST && uMsg >= WM_MOUSEFIRST) {
MouseEvent ev;
mouseDelegate(this, cast(MouseEvent*)&gApp.event);
} else {
switch (uMsg) {
case WM_PAINT:
version (LOG) log.writefln(" got canvas paint");
repaintNow();
break;
case WM_DESTROY:
version (LOG) log.writefln(" got canvas destroy");
hasPeer = NO_PEER;
default:
version (LOG) log.writefln(" got canvas default");
break;
}
}
return doDefault;
}
}
extern(Windows) int MinWinCanvasProc(HWND hWnd, uint uMsg, WPARAM wParam, LPARAM lParam) {
version(LOG) log.printf("in minwin canvas proc hwnd %p\n",hWnd);
bool doDefault = true;
Canvas canvas = cast(Canvas)peerToWindowChild(hWnd);
version(LOG) log.printf("in minwin canvas proc canvas %p\n",canvas);
if (canvas !is null)
doDefault = canvas.WindowProc(hWnd,uMsg,wParam,lParam) != 0;
if (doDefault)
return DefWindowProcA(hWnd, uMsg, wParam, lParam);
else
return 0;
}
static this() {
HINSTANCE hInst = GetModuleHandleA(null);
WNDCLASSA wc;
wc.lpszClassName = "MinWinCanvas";
wc.style = CS_OWNDC | CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = &MinWinCanvasProc;
wc.hInstance = hInst;
wc.hCursor = LoadCursorA(cast(HINSTANCE) null, IDC_ARROW);
wc.hbrBackground = null;
// wc.hbrBackground = cast(HBRUSH) (COLOR_WINDOW + 1); // not +1 for default
wc.lpszMenuName = null;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
RegisterClassA(&wc);
}
} else version (GTK) {
private import minwin.gtk;
class Canvas : WindowChild {
this(Component parent, char[] name = "") {
PeerForAdd parentp = parent.getPeerForAdd();
this.name = name;
peer = gtk_drawing_area_new();
gtk_widget_add_events(peer,GdkEventMask.GDK_KEY_PRESS_MASK |
GdkEventMask.GDK_KEY_RELEASE_MASK |
GdkEventMask.GDK_EXPOSURE_MASK |
GdkEventMask.GDK_POINTER_MOTION_MASK |
GdkEventMask.GDK_BUTTON_PRESS_MASK |
GdkEventMask.GDK_BUTTON_RELEASE_MASK);
g_signal_connect_data(peer,"expose-event",
cast(GCallback)&mw_canvas_expose_callback,
cast(gpointer)this,
null,GConnectFlags.G_CONNECT_AFTER);
g_signal_connect_data(peer,"key-press-event",
cast(GCallback)&mw_canvas_key_callback,
cast(gpointer)this,
null,GConnectFlags.G_CONNECT_AFTER);
g_signal_connect_data(peer,"key-release-event",
cast(GCallback)&mw_canvas_key_callback,
cast(gpointer)this,
null,GConnectFlags.G_CONNECT_AFTER);
g_signal_connect_data(peer,"motion-notify-event",
cast(GCallback)&mw_can_mouse_callback,
cast(gpointer)this,
null,GConnectFlags.G_CONNECT_AFTER);
g_signal_connect_data(peer,"button-press-event",
cast(GCallback)&mw_can_button_callback,
cast(gpointer)this,
null,GConnectFlags.G_CONNECT_AFTER);
g_signal_connect_data(peer,"button-release-event",
cast(GCallback)&mw_can_button_callback,
cast(gpointer)this,
null,GConnectFlags.G_CONNECT_AFTER);
GtkObject* obj = cast(GtkObject*)peer;
obj.flags |= GtkWidgetFlags.GTK_CAN_FOCUS;
gtk_container_add(cast(GtkContainer*)parentp,peer);
setWindowChildPeer(this,peer,OWNS_PEER);
parent.addChild(this);
gtk_widget_realize(peer);
visible = true;
}
mixin CommonEventSourceImpl!();
void backgroundColor(Color c) {
GdkWindow* win = (cast(GtkWidget*)peer).window;
GdkColor nc;
// look up GdkColor for rgb values in c using rgb colormap
gdk_window_set_background(win,&nc);
}
GContext getGContext(Event* paintEvent) {
GContext gc = newGContext();
// GtkWidget* widget = cast(GtkWidget*)peer;
GdkWindow* gwin = peer.window;
sysAssert(gwin !is null,"Canvas peer has no window",0);
// version(LOG) log.printf(" CANVAS DRAWABLE is %x\n",gwin);
gc.drawable = cast(GdkDrawable*)gwin;
gc.layout = gtk_widget_create_pango_layout(peer,"");
gc.peer = gdk_gc_new(gc.drawable);
gc.hasPeer = OWNS_PEER;
gc.paintEvent = cast(GdkEventExpose*)paintEvent;
return gc;
}
// share with Window modulo peer vs content
Image getCompatibleImage(int width, int height) {
GtkWidget* widget = peer;
GdkDrawable* gwin = cast(GdkDrawable*)widget.window;
// ImagePeer bm = gdk_pixbuf_new(GdkColorspace.GDK_COLORSPACE_RGB,
// false,8,width,height);
int depth = gdk_drawable_get_depth(gwin);
ImagePeer bm = gdk_pixmap_new(gwin,width,height,depth);
version(LOG) log.writefln("create bitmap %x",cast(int)bm);
Image res = new Image(bm);
res.width = width;
res.height = height;
res.hasPeer = OWNS_PEER;
return res;
}
// share with Window modulo peer vs content
Image loadCompatibleImage(char[] imageKey, char[] fmt = "bmp") {
GtkWidget* widget = peer;
GdkDrawable* gwin = cast(GdkDrawable*)widget.window;
GError* err;
char[] fname = imageKey;
if (gApp.resourcePath.length > 0) {
fname = gApp.resourcePath ~ "/" ~ fname;
}
fname = fname ~ "." ~ fmt;
GdkPixbuf* pbuf = gdk_pixbuf_new_from_file(toStringz(fname),&err);
int width = gdk_pixbuf_get_width(pbuf);
int height = gdk_pixbuf_get_height(pbuf);
int depth = gdk_drawable_get_depth(gwin);
GdkColormap* cmap = gdk_drawable_get_colormap(gwin);
ImagePeer bm = gdk_pixmap_new(gwin,width,height,depth);
gdk_pixbuf_render_pixmap_and_mask_for_colormap(pbuf,cmap,&bm,null,0);
Image res = new Image(bm);
res.width = width;
res.height = height;
res.hasPeer = OWNS_PEER;
return res;
}
mixin SharedCanvasImpl!();
mixin WindowChildImpl!();
}
extern (C) gboolean mw_canvas_expose_callback(GtkWidget* w,
GdkEventExpose* event,
gpointer ud) {
Canvas c = cast(Canvas) ud;
if (c) {
c.repaintNow(cast(Event*)event);
}
return false;
}
extern (C) gboolean mw_canvas_key_callback(GtkWidget* w,
GdkEventKey* event,
gpointer ud) {
Canvas c = cast(Canvas) ud;
if (c && !c.keyDelegate.isEmpty) {
c.keyDelegate(c,cast(KeyEvent*)event);
}
return false;
}
extern (C) gboolean mw_can_mouse_callback(GtkWidget* w,
GdkEventMotion* event,
gpointer ud) {
Canvas c = cast(Canvas) ud;
if (c && !c.mouseDelegate.isEmpty) {
// TODO this should be made into a GdkEventButton
c.mouseDelegate(c,cast(MouseEvent*)event);
}
return false;
}
extern (C) gboolean mw_can_button_callback(GtkWidget* w,
GdkEventButton* event,
gpointer ud) {
Canvas c = cast(Canvas) ud;
if (c && !c.mouseDelegate.isEmpty) {
// TODO this should be made into a GdkEventButton
c.mouseDelegate(c,cast(MouseEvent*)event);
}
return false;
}
}