Mercurial > projects > doodle
view canvas.d @ 9:66b47e122b31
Checkpoint
author | "David Bryant <bagnose@gmail.com>" |
---|---|
date | Sat, 11 Jul 2009 22:06:09 +0930 |
parents | 936feb16eed4 |
children | 71ca82e0eb76 |
line wrap: on
line source
module canvas; import std.stdio; import std.math; import cairo.Context; import cairo.Surface; import gdk.Drawable; import gtk.Widget; import gtk.Toolbar; import gtk.Table; import gtk.HRuler; import gtk.VRuler; import gtk.Range; import gtk.HScrollbar; import gtk.VScrollbar; import gtk.DrawingArea; import gtk.Adjustment; import tk.misc; import tk.geometry; import tk.types; import tk.events; import tk.gtk_support; private import gtkc.gtk; import icanvas; import cairo_support; // x and y run right and up respectively class Canvas : Table, ICanvas { static this() { ORIGIN = Point(0.0, 0.0); INITIAL_PAGE_SIZE = Vector(210.0, 297.0); // A4 } this(ICanvasEventHandler event_handler) { super(3, 3, 0); mEventHandler = event_handler; const double PPI = 120.0; const double MM_PER_INCH = 25.4; mZoom = 0.25 * PPI / MM_PER_INCH; mPageLeftBottom = ORIGIN; mPageRightTop = ORIGIN + INITIAL_PAGE_SIZE; mViewCentre = ORIGIN + INITIAL_PAGE_SIZE / 2.0; mCanvasLeftBottom = mPageLeftBottom - 3.0 * INITIAL_PAGE_SIZE; mCanvasRightTop = mPageRightTop + 3.0 * INITIAL_PAGE_SIZE; mHRuler = new HRuler(); attach(mHRuler, 1, 2, 0, 1, AttachOptions.FILL | AttachOptions.EXPAND, AttachOptions.SHRINK, 0, 0); mHRuler.setMetric(GtkMetricType.CENTIMETERS); mVRuler = new VRuler(); attach(mVRuler, 0, 1, 1, 2, AttachOptions.SHRINK, AttachOptions.FILL | AttachOptions.EXPAND, 0, 0); mVRuler.setMetric(GtkMetricType.CENTIMETERS); mDrawingArea = new DrawingArea(); mDrawingArea.addOnRealize(&onRealize); mDrawingArea.addOnConfigure(&onConfigure); mDrawingArea.addOnExpose(&onExpose); mDrawingArea.addOnButtonPress(&onButtonPress); mDrawingArea.addOnButtonRelease(&onButtonRelease); mDrawingArea.addOnKeyPress(&onKeyEvent); mDrawingArea.addOnKeyRelease(&onKeyEvent); mDrawingArea.addOnMotionNotify(&onMotionNotify); mDrawingArea.addOnScroll(&onScroll); attach(mDrawingArea, 1, 2, 1, 2, AttachOptions.FILL | AttachOptions.EXPAND, AttachOptions.FILL | AttachOptions.EXPAND, 0, 0); // value, lower, upper, step-inc, page-inc, page-size // Give the adjustments dummy values until we receive a configure mHAdjustment = new Adjustment(0.25, 0.0, 1.0, 0.2, 0.5, 0.5); mHAdjustment.addOnValueChanged(&onValueChanged); mHScrollbar = new HScrollbar(mHAdjustment); attach(mHScrollbar, 1, 2, 2, 3, AttachOptions.FILL | AttachOptions.EXPAND, AttachOptions.SHRINK, 0, 0); mVAdjustment = new Adjustment(0.0, 0.0, 1.0, 0.2, 0.5, 0.5); mVAdjustment.addOnValueChanged(&onValueChanged); mVScrollbar = new VScrollbar(mVAdjustment); attach(mVScrollbar, 2, 3, 1, 2, AttachOptions.SHRINK, AttachOptions.FILL | AttachOptions.EXPAND, 0, 0); } override void rel_zoom(Point screen_datum, double factor) { // Work out pixel distance from current centre to datum, // Do the zoom, then work out the new centre that keeps the // pixel distance the same Point old_model_datum = screen_to_model(screen_datum); Vector pixel_distance = model_to_screen(old_model_datum - mViewCentre); mZoom = clamp_zoom(factor * mZoom); mViewCentre = old_model_datum - screen_to_model(pixel_distance); Point new_model_datum = screen_to_model(screen_datum); update_adjustments(); //update_rulers(new_model_datum); update_rulers(); queueDraw(); } override void rel_pan(Vector screen_displacement) { mViewCentre = mViewCentre + screen_to_model(screen_displacement); update_adjustments(); update_rulers(); queueDraw(); } private { void onRealize(Widget widget) { assert(widget is mDrawingArea); //writefln("Got realize\n"); } bool onConfigure(GdkEventConfigure * event, Widget widget) { assert(widget is mDrawingArea); mViewSize = Vector(cast(double)event.width, cast(double)event.height); update_adjustments(); update_rulers(); return true; } bool onExpose(GdkEventExpose * event, Widget widget) { assert(widget is mDrawingArea); Drawable dr = mDrawingArea.getWindow(); int width, height; dr.getSize(width, height); //writefln("Got expose %dx%d\n", width, height); scope cr = new Context(dr); if (event) { cr.rectangle(event.area.x, event.area.y, event.area.width, event.area.height); cr.clip(); } // Make the window light grey cr.setSourceRgba(1.0, 1.0, 1.0, 0.7); if (event) { cr.rectangle(event.area.x, event.area.y, event.area.width, event.area.height); } else { cr.rectangle(0.0, 0.0, width, height); } cr.fill(); { // Make the paper white, with a border Point screen_page_left_bottom = model_to_screen(mPageLeftBottom); Point screen_page_right_top = model_to_screen(mPageRightTop); cr.setSourceRgba(1.0, 1.0, 1.0, 1.0); rectangle(cr, screen_page_left_bottom, screen_page_right_top); cr.fill(); cr.setSourceRgba(0.0, 0.0, 0.0, 1.0); rectangle(cr, screen_page_left_bottom, screen_page_right_top); cr.stroke(); } return true; } bool onButtonPress(GdkEventButton * event, Widget widget) { assert(widget is mDrawingArea); //writefln("Got button event\n"); Point screen_point = Point(event.x + 0.5, event.y + 0.5); Point model_point = screen_to_model(screen_point); auto button_event = new ButtonEvent(gtk2tk_button_action(event.type), gtk2tk_button_name(event.button), screen_point, model_point, gtk2tk_mask(event.state)); mEventHandler.handle_button_press(this, button_event); return true; } bool onButtonRelease(GdkEventButton * event, Widget widget) { assert(widget is mDrawingArea); //writefln("Got button event\n"); Point screen_point = Point(event.x + 0.5, event.y + 0.5); Point model_point = screen_to_model(screen_point); auto button_event = new ButtonEvent(gtk2tk_button_action(event.type), gtk2tk_button_name(event.button), screen_point, model_point, gtk2tk_mask(event.state)); mEventHandler.handle_button_release(this, button_event); return true; } bool onKeyEvent(GdkEventKey * event, Widget widget) { assert(widget is mDrawingArea); //writefln("Got key event\n"); //auto key_event = new KeyEvent("", // mEventHandle.handle_key(key_event); return true; } bool onMotionNotify(GdkEventMotion * event, Widget widget) { assert(widget is mDrawingArea); //writefln("Got motion notify\n"); gtk_widget_event(mHRuler.getWidgetStruct(), cast(GdkEvent *)event); gtk_widget_event(mVRuler.getWidgetStruct(), cast(GdkEvent *)event); Point screen_point = Point(event.x + 0.5, event.y + 0.5); Point model_point = screen_to_model(screen_point); auto motion_event = new MotionEvent(screen_point, model_point, gtk2tk_mask(event.state)); mEventHandler.handle_motion(this, motion_event); return true; } bool onScroll(GdkEventScroll * event, Widget widget) { assert(widget is mDrawingArea); //writefln("Got scroll\n"); Point screen_point = Point(event.x + 0.5, event.y + 0.5); Point model_point = screen_to_model(screen_point); auto scroll_event = new ScrollEvent(gtk2tk_direction(event.direction), screen_point, model_point, gtk2tk_mask(event.state)); mEventHandler.handle_scroll(this, scroll_event); return true; } void onValueChanged(Adjustment adjustment) { GtkAdjustment * h_gtkAdjustment = mHAdjustment.getAdjustmentStruct(); GtkAdjustment * v_gtkAdjustment = mVAdjustment.getAdjustmentStruct(); Point view_left_bottom = Point(gtk_adjustment_get_value(h_gtkAdjustment), gtk_adjustment_get_value(v_gtkAdjustment)); //writefln("%s", view_left_bottom); Vector model_size = screen_to_model(mViewSize); mViewCentre = view_left_bottom + model_size / 2.0; update_rulers(); queueDraw(); } void update_rulers() { Vector model_size = screen_to_model(mViewSize); Point view_left_bottom = mViewCentre - model_size / 2.0; Point view_right_top = mViewCentre + model_size / 2.0; mHRuler.setRange(view_left_bottom.x, view_right_top.x, 0.0, // TODO preserve the value mZoom * 2000.0); mVRuler.setRange(view_right_top.y, view_left_bottom.y, 0.0, mZoom * 2000.0); } void update_adjustments() { Vector model_size = screen_to_model(mViewSize); Point view_left_bottom = mViewCentre - model_size / 2.0; Point view_right_top = mViewCentre + model_size / 2.0; // Adjust the canvas size if necessary mCanvasLeftBottom = min_extents(mCanvasLeftBottom, view_left_bottom); mCanvasRightTop = max_extents(mCanvasRightTop, view_right_top); Vector canvas_size = mCanvasRightTop - mCanvasLeftBottom; Vector page_size = mPageRightTop - mPageLeftBottom; // Update the adjustments GtkAdjustment * h_gtkAdjustment = mHAdjustment.getAdjustmentStruct(); GtkAdjustment * v_gtkAdjustment = mVAdjustment.getAdjustmentStruct(); gtk_adjustment_set_lower(h_gtkAdjustment, mCanvasLeftBottom.x); gtk_adjustment_set_upper(h_gtkAdjustment, mCanvasRightTop.x); gtk_adjustment_set_value(h_gtkAdjustment, view_left_bottom.x); gtk_adjustment_set_step_increment(h_gtkAdjustment, canvas_size.x / 10.0); gtk_adjustment_set_page_increment(h_gtkAdjustment, canvas_size.x / 5.0); gtk_adjustment_set_page_size(h_gtkAdjustment, model_size.x); gtk_adjustment_set_lower(v_gtkAdjustment, mCanvasLeftBottom.y); gtk_adjustment_set_upper(v_gtkAdjustment, mCanvasRightTop.y); gtk_adjustment_set_value(v_gtkAdjustment, view_left_bottom.y); gtk_adjustment_set_step_increment(v_gtkAdjustment, canvas_size.y / 10.0); gtk_adjustment_set_page_increment(v_gtkAdjustment, canvas_size.y / 5.0); gtk_adjustment_set_page_size(v_gtkAdjustment, model_size.y); mHAdjustment.changed(); mHAdjustment.valueChanged(); mVAdjustment.changed(); mVAdjustment.valueChanged(); } Point model_to_screen(Point model) { return ORIGIN + mViewSize / 2.0 + mZoom * (model - mViewCentre); } Point screen_to_model(Point screen) { return mViewCentre + (screen - mViewSize / 2.0 - ORIGIN) / mZoom; } Vector model_to_screen(Vector model) { return mZoom * model; } Vector screen_to_model(Vector screen) { return screen / mZoom; } double model_to_screen(double model) { return mZoom * model; } double screen_to_model(double screen) { return screen / mZoom; } double clamp_zoom(double zoom) { return clamp(zoom, 0.02, 50.0); } static const Point ORIGIN; static const Vector INITIAL_PAGE_SIZE; ICanvasEventHandler mEventHandler; // Model units are in millimetres // Screen units are in pixels double mZoom; // pixels-per-mm Point mViewCentre; // model: where in the model is the centre of our view Point mCanvasLeftBottom; // model: bottom left corner of canvas Point mCanvasRightTop; // model: top right corner of canvas Point mPageLeftBottom; // model: bottom left corner of page Point mPageRightTop; // model: top right corner of page Vector mViewSize; // screen: size of view window in pixels HRuler mHRuler; VRuler mVRuler; DrawingArea mDrawingArea; Adjustment mHAdjustment; HScrollbar mHScrollbar; Adjustment mVAdjustment; VScrollbar mVScrollbar; } }