274 lines
12 KiB
D
274 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;
|
||
|
}
|
||
|
}
|