Skip to content

Create a native widget for Shoes

Cecil edited this page Jan 13, 2016 · 9 revisions

This is harder to do than Creating a Class for Shoes in C which describes adding an 'svghandle' method to canvas and a native class that tries to be a canvas. This wiki note will describe creating a brand new native widget. You'll want to read the note/failure above because wiring in .c and .h and macros will be very similar.

We are going to start from the bottom which is shoes_native_create_surface which is just a rectangular section of pixels in Gtk/Cocoa that can be moved, updated and respond to mouse events and key strokes in that windows. Shoes still has remnants of the VLC widget so we have a decent chance this will work.

VLC was removed with this commit https://github.com/shoes/shoes/commit/5271cb6af2927481869de18221abcb19fac50cfb#diff-d79c549efc22f563775c25100e5b0c9f so you might want to review how it was implemented as a native widget.

Since this is a native widget, we are going to add code to native.h gtk.c and cocoa.m as well as canvas(.c and .h) and ruby(.c and .h)

Svg will be an rectangular widget. It will respond to many common Shoes element commands and support a style or two where it makes sense. We'll try to do most of the work in svg.c rather that litter up canvas.c and ruby.c

In canvas.h we add

typedef struct {
  VALUE parent;
  VALUE attr;
  shoes_place place;
  SHOES_CONTROL_REF ref;
  int init;
  RsvgHandle *handle;
  RsvgDimensionData svgdim;
  char  *subid;  // null for everything. 
  SHOES_SLOT_OS *slot;
} shoes_svg;

We'll also create a native/gtksvg.c which will implement the shoes_native_svg_XXX functions. Smarter people would add the code to ruby.c native/cocoa.m will also have shoes_native_svg methods declare; Those are declared in native.h so lets add these to native.h

SHOES_SURFACE_REF shoes_native_svg(shoes_canvas *, VALUE, shoes_place *);
void shoes_native_svg_position(SHOES_SURFACE_REF, shoes_place *,
    VALUE, shoes_canvas *, shoes_place *);
void shoes_native_svg_hide(SHOES_SURFACE_REF);
void shoes_native_svg_show(SHOES_SURFACE_REF);
void shoes_native_svg_remove(shoes_canvas *, SHOES_SURFACE_REF);

The convention is that shoes_native_svg() is the C/Obj-C function that will create the widget. It will be called from shoes_svg_new() which will be called from ruby.c when the user's Shoes script creates the svg widget.

Then in gtk.c and cocoa.m there are shoes_native_svg() which do whatever is required to create the thing and get events delivered to it. The convention is not always adhered to. Sometimes you find them in app.c

We'll need to update the make/*/env.rb files to compile gtksvg.c and then we'll need to write gtksvg.c to match - we'll end up writing much more code of course. For now I'm just getting things to compile and link together. You won't be suprised at the code in gtkvsg.c at this point - it's just the shoes_native_surface_functions cloned out of gtk.c and renamed.

To test where it can even create and svg object we'll need a Shoes script to call svg and we'll gdb step thru it starting a shoes_canvas_svg. every thing allocs oK but running gets a draw method missing from shoes and a 'drawing failure' message from Gtk. We haven't wired the up gtk signals so that doesn't surprise me. We need to add some more declares and code. In canvas.h, the svg section looks like

VALUE shoes_svg_new(VALUE, VALUE, VALUE, VALUE);
VALUE shoes_svg_alloc(VALUE);
VALUE shoes_svg_draw(VALUE, VALUE, VALUE);
VALUE shoes_svg_show(VALUE);
VALUE shoes_svg_hide(VALUE);
VALUE shoes_svg_get_top(VALUE);
VALUE shoes_svg_get_left(VALUE);
VALUE shoes_svg_get_width(VALUE);
VALUE shoes_svg_get_height(VALUE);
VALUE shoes_svg_remove(VALUE);

and of course we need those new functions in svg.c (which will call shoes_native_svg_xxx equivalents if needed)

We also need to tell the ruby.c code that we have a new class

  cSvg   = rb_define_class_under(cTypes, "Svg", rb_cObject);
  rb_define_alloc_func(cSvg, shoes_svg_alloc);
  rb_define_method(cSvg, "draw", CASTHOOK(shoes_svg_draw), 2);
  rb_define_method(cSvg, "render", CASTHOOK(shoes_svg_render), -1);
  rb_define_method(cSvg, "full_width", CASTHOOK(shoes_svg_get_width), 0);
  rb_define_method(cSvg, "full_height", CASTHOOK(shoes_svg_get_height), 0);

We can't pretend we are cNative aka control widget - (I tried). We need to define and implement some of the methods that you would find in a Shoes Image. Draw is critical because Shoes calls it internally for repainting. mysvg.render or mysvg.render subid is what you'd use a in Shoes script.

We're going to end up creating a bunch of c functions in svg.c and declared in canvas.h and Ruby-ized in ruby.c This note is not going to describe all the internals of those functions and they will have been added to, modified and removed since this note was written.

WE PROMISE to NOT document draw in Shoes manual without big bold letters "Won't Work" in case anybody thinks they can override or monkey patch it. We will smirk and mock those attempts.

painting, sizing and layout

We need to get the svg widget/image into the Shoes draw/repaint/sizing mechanism. I'm sure Shoes4 was started because no one wanted to deal with this in 'C'. It can be a deep dive and probably won't be well documented here.

Rsvg is a bit weird. It has it own methods to call when drawing with Cairo. They call it render_to_cairo but Shoes may want to call it in paint or draw or repaint. Since both Gtk and Cocoa use Cairo we can do our repaint/scale it the same place for both platforms so we've put code in shoes_svg_paint_svg() in svg.c It's not going to be the last time you'll be confused about when things are named native and aren't in those files.

[Dec 1, 2015] API

Rethink. We want an svgimage object (basically a rsvghandle wrapper) that we can load from a file (or a string). We need Shoes methods that can query it (has_subid, full_width, full_height, sub width and sub height That way the shoes script writer can compute the display widget dimensions (pixels, aspect ratio) they like want, before they add their image/layer object a Shoes slot. layout with a slot.svg(svgobj, w, h)

So Shoes

parsing args and styles for our Svg widget

Now we need to work on the shoes_svg_draw () function in svg.c. It is
called many many times from Shoes (in ways mysterious to Shoes).

Clone this wiki locally