diff --git a/GNUmakefile b/GNUmakefile index 03345624..4e845414 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -23,6 +23,7 @@ baseHFILES = \ $(osHFILES) baseCFILES = \ + areaevents.c \ control.c \ menu.c \ ptrarray.c \ diff --git a/winarea_d2d/events.c b/areaevents.c similarity index 99% rename from winarea_d2d/events.c rename to areaevents.c index f2749f19..0f42bc6f 100644 --- a/winarea_d2d/events.c +++ b/areaevents.c @@ -1,6 +1,4 @@ // 29 march 2014 -// TODO remove -#include #include "ui.h" #include "uipriv.h" diff --git a/gtkarea/area.h b/gtkarea/area.h index f75d874d..e7b366df 100644 --- a/gtkarea/area.h +++ b/gtkarea/area.h @@ -7,24 +7,6 @@ #include #include -#define areaWidgetType (areaWidget_get_type()) -#define areaWidget(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), areaWidgetType, areaWidget)) -#define isAreaWidget(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), areaWidgetType)) -#define areaWidgetClass(class) (G_TYPE_CHECK_CLASS_CAST((class), areaWidgetType, areaWidgetClass)) -#define isAreaWidgetClass(class) (G_TYPE_CHECK_CLASS_TYPE((class), areaWidget)) -#define getAreaWidgetClass(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), areaWidgetType, areaWidgetClass)) - -typedef struct areaWidget areaWidget; -typedef struct areaWidgetClass areaWidgetClass; - -struct areaWidget { - GtkDrawingArea parent_instance; - struct areaPrivate *priv; -}; - -struct areaWidgetClass { - GtkDrawingAreaClass parent_class; -}; extern GType areaWidget_get_type(void); @@ -34,4 +16,3 @@ extern GType areaWidget_get_type(void); extern GtkWidget *newArea(uiAreaHandler *ah); extern void areaUpdateScroll(GtkWidget *area); -extern uiDrawContext *newContext(cairo_t *); diff --git a/gtkarea/events.c b/gtkarea/events.c deleted file mode 100644 index 6544b396..00000000 --- a/gtkarea/events.c +++ /dev/null @@ -1,170 +0,0 @@ -// 29 march 2014 -// TODO remove -#include -#include -#include "ui.h" -#include "uipriv.h" - -/* -Windows and GTK+ have a limit of 2 and 3 clicks, respectively, natively supported. Fortunately, we can simulate the double/triple-click behavior to build higher-order clicks. We can use the same algorithm Windows uses on both: - http://blogs.msdn.com/b/oldnewthing/archive/2004/10/18/243925.aspx -For GTK+, we pull the double-click time and double-click distance, which work the same as the equivalents on Windows (so the distance is in all directions), from the GtkSettings system. - -On GTK+ this will also allow us to discard the GDK_BUTTON_2PRESS and GDK_BUTTON_3PRESS events, so the button press stream will be just like on other platforms. - -Thanks to mclasen, garnacho_, halfline, and tristan in irc.gimp.net/#gtk+. -*/ - -// x, y, xdist, ydist, and c.rect must have the same units -// so must time, maxTime, and c.prevTime -uintmax_t clickCounterClick(clickCounter *c, uintmax_t button, intmax_t x, intmax_t y, uintptr_t time, uintptr_t maxTime, intmax_t xdist, intmax_t ydist) -{ - // different button than before? if so, don't count - if (button != c->curButton) - c->count = 0; - - // (x, y) in the allowed region for a double-click? if not, don't count - if (x < c->rectX0) - c->count = 0; - if (y < c->rectY0) - c->count = 0; - if (x >= c->rectX1) - c->count = 0; - if (y >= c->rectY1) - c->count = 0; - - // too slow? if so, don't count - // note the below expression; time > (c.prevTime + maxTime) can overflow! - if ((time - c->prevTime) > maxTime) // too slow; don't count - c->count = 0; - - c->count++; // if either of the above ifs happened, this will make the click count 1; otherwise it will make the click count 2, 3, 4, 5, ... - - // now we need to update the internal structures for the next test - c->curButton = button; - c->prevTime = time; - c->rectX0 = x - xdist; - c->rectY0 = y - ydist; - c->rectX1 = x + xdist; - c->rectY1 = y + ydist; - - return c->count; -} - -void clickCounterReset(clickCounter *c) -{ - c->curButton = 0; - c->rectX0 = 0; - c->rectY0 = 0; - c->rectX1 = 0; - c->rectY1 = 0; - c->prevTime = 0; - c->count = 0; -} - -/* -For position independence across international keyboard layouts, typewriter keys are read using scancodes (which are always set 1). -Windows provides the scancodes directly in the LPARAM. -GTK+ provides the scancodes directly from the underlying window system via GdkEventKey.hardware_keycode. -On X11, this is scancode + 8 (because X11 keyboard codes have a range of [8,255]). -Wayland is guaranteed to give the same result (thanks ebassi in irc.gimp.net/#gtk+). -On Linux, where evdev is used instead of polling scancodes directly from the keyboard, evdev's typewriter section key code constants are the same as scancodes anyway, so the rules above apply. -Typewriter section scancodes are the same across international keyboards with some exceptions that have been accounted for (see KeyEvent's documentation); see http://www.quadibloc.com/comp/scan.htm for details. -Non-typewriter keys can be handled safely using constants provided by the respective backend API. - -Because GTK+ keysyms may or may not obey Num Lock, we also handle the 0-9 and . keys on the numeric keypad with scancodes (they match too). -*/ - -// use uintptr_t to be safe; the size of the scancode/hardware key code field on each platform is different -static const struct { - uintptr_t scancode; - char equiv; -} scancodeKeys[] = { - { 0x02, '1' }, - { 0x03, '2' }, - { 0x04, '3' }, - { 0x05, '4' }, - { 0x06, '5' }, - { 0x07, '6' }, - { 0x08, '7' }, - { 0x09, '8' }, - { 0x0A, '9' }, - { 0x0B, '0' }, - { 0x0C, '-' }, - { 0x0D, '=' }, - { 0x0E, '\b' }, - { 0x0F, '\t' }, - { 0x10, 'q' }, - { 0x11, 'w' }, - { 0x12, 'e' }, - { 0x13, 'r' }, - { 0x14, 't' }, - { 0x15, 'y' }, - { 0x16, 'u' }, - { 0x17, 'i' }, - { 0x18, 'o' }, - { 0x19, 'p' }, - { 0x1A, '[' }, - { 0x1B, ']' }, - { 0x1C, '\n' }, - { 0x1E, 'a' }, - { 0x1F, 's' }, - { 0x20, 'd' }, - { 0x21, 'f' }, - { 0x22, 'g' }, - { 0x23, 'h' }, - { 0x24, 'j' }, - { 0x25, 'k' }, - { 0x26, 'l' }, - { 0x27, ';' }, - { 0x28, '\'' }, - { 0x29, '`' }, - { 0x2B, '\\' }, - { 0x2C, 'z' }, - { 0x2D, 'x' }, - { 0x2E, 'c' }, - { 0x2F, 'v' }, - { 0x30, 'b' }, - { 0x31, 'n' }, - { 0x32, 'm' }, - { 0x33, ',' }, - { 0x34, '.' }, - { 0x35, '/' }, - { 0x39, ' ' }, - { 0xFFFF, 0 }, -}; - -static const struct { - uintptr_t scancode; - uiExtKey equiv; -} scancodeExtKeys[] = { - { 0x47, uiExtKeyN7 }, - { 0x48, uiExtKeyN8 }, - { 0x49, uiExtKeyN9 }, - { 0x4B, uiExtKeyN4 }, - { 0x4C, uiExtKeyN5 }, - { 0x4D, uiExtKeyN6 }, - { 0x4F, uiExtKeyN1 }, - { 0x50, uiExtKeyN2 }, - { 0x51, uiExtKeyN3 }, - { 0x52, uiExtKeyN0 }, - { 0x53, uiExtKeyNDot }, - { 0xFFFF, 0 }, -}; - -int fromScancode(uintptr_t scancode, uiAreaKeyEvent *ke) -{ - int i; - - for (i = 0; scancodeKeys[i].scancode != 0xFFFF; i++) - if (scancodeKeys[i].scancode == scancode) { - ke->Key = scancodeKeys[i].equiv; - return 1; - } - for (i = 0; scancodeExtKeys[i].scancode != 0xFFFF; i++) - if (scancodeExtKeys[i].scancode == scancode) { - ke->ExtKey = scancodeExtKeys[i].equiv; - return 1; - } - return 0; -} diff --git a/gtkarea/uipriv.h b/gtkarea/uipriv.h deleted file mode 100644 index e519c93f..00000000 --- a/gtkarea/uipriv.h +++ /dev/null @@ -1,16 +0,0 @@ -typedef struct clickCounter clickCounter; -// you should call Reset() to zero-initialize a new instance -// it doesn't matter that all the non-count fields are zero: the first click will fail the curButton test straightaway, so it'll return 1 and set the rest of the structure accordingly -struct clickCounter { - uintmax_t curButton; - intmax_t rectX0; - intmax_t rectY0; - intmax_t rectX1; - intmax_t rectY1; - uintptr_t prevTime; - uintmax_t count; -}; -extern uintmax_t clickCounterClick(clickCounter *, uintmax_t, intmax_t, intmax_t, uintptr_t, uintptr_t, intmax_t, intmax_t); -extern void clickCounterReset(clickCounter *); - -extern int fromScancode(uintptr_t, uiAreaKeyEvent *); diff --git a/ui.h b/ui.h index 2cd60577..ff4c9274 100644 --- a/ui.h +++ b/ui.h @@ -265,7 +265,8 @@ struct uiAreaHandler { }; _UI_EXTERN uintmax_t uiMenuItemType(void); -#define uiMenuItem(this) ((uiMenuItem *) uiIsA((this), uiMenuItemType(), 1)) +#define uiArea(this) ((uiArea *) uiIsA((this), uiAreaType(), 1)) +_UI_EXTERN void uiAreaUpdateScroll(uiArea *a); _UI_EXTERN uiArea *uiNewArea(uiAreaHandler *ah); struct uiAreaDrawParams { diff --git a/uipriv.h b/uipriv.h index 830e70f8..e01a9331 100644 --- a/uipriv.h +++ b/uipriv.h @@ -42,3 +42,20 @@ extern int shouldQuit(void); // types.c extern void uninitTypes(void); extern uiTyped *newTyped(uintmax_t type); + +// areaevents.c +typedef struct clickCounter clickCounter; +// you should call Reset() to zero-initialize a new instance +// it doesn't matter that all the non-count fields are zero: the first click will fail the curButton test straightaway, so it'll return 1 and set the rest of the structure accordingly +struct clickCounter { + uintmax_t curButton; + intmax_t rectX0; + intmax_t rectY0; + intmax_t rectX1; + intmax_t rectY1; + uintptr_t prevTime; + uintmax_t count; +}; +extern uintmax_t clickCounterClick(clickCounter *, uintmax_t, intmax_t, intmax_t, uintptr_t, uintptr_t, intmax_t, intmax_t); +extern void clickCounterReset(clickCounter *); +extern int fromScancode(uintptr_t, uiAreaKeyEvent *); diff --git a/unix/GNUmakeinc.mk b/unix/GNUmakeinc.mk index 84e1c188..17a98754 100644 --- a/unix/GNUmakeinc.mk +++ b/unix/GNUmakeinc.mk @@ -2,6 +2,7 @@ osCFILES = \ unix/alloc.c \ + unix/area.c \ unix/box.c \ unix/button.c \ unix/checkbox.c \ @@ -9,6 +10,7 @@ osCFILES = \ unix/combobox.c \ unix/control.c \ unix/datetimepicker.c \ + unix/draw.c \ unix/entry.c \ unix/group.c \ unix/label.c \ diff --git a/gtkarea/area.c b/unix/area.c similarity index 92% rename from gtkarea/area.c rename to unix/area.c index a16c26be..b8abce23 100644 --- a/gtkarea/area.c +++ b/unix/area.c @@ -1,5 +1,31 @@ // 4 september 2015 -#include "area.h" +#include "uipriv_unix.h" + +#define areaWidgetType (areaWidget_get_type()) +#define areaWidget(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), areaWidgetType, areaWidget)) +#define isAreaWidget(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), areaWidgetType)) +#define areaWidgetClass(class) (G_TYPE_CHECK_CLASS_CAST((class), areaWidgetType, areaWidgetClass)) +#define isAreaWidgetClass(class) (G_TYPE_CHECK_CLASS_TYPE((class), areaWidget)) +#define getAreaWidgetClass(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), areaWidgetType, areaWidgetClass)) + +typedef struct areaWidget areaWidget; +typedef struct areaWidgetClass areaWidgetClass; + +struct areaWidget { + GtkDrawingArea parent_instance; + struct areaPrivate *priv; +}; + +struct areaWidgetClass { + GtkDrawingAreaClass parent_class; +}; + +struct uiArea { + uiUnixControl c; + GtkWidget *widget; + GtkDrawingArea *drawingArea; + areaWidget *area; +}; struct areaPrivate { uiArea *a; @@ -527,14 +553,33 @@ static void areaWidget_scrollable_init(GtkScrollable *iface) // no need to do anything; the interface only has properties } -GtkWidget *newArea(uiAreaHandler *ah) +// control implementation + +uiUnixDefineControl( + uiArea, // type name + uiAreaType // type function +) + +void uiAreaUpdateScroll(uiArea *a) { - return GTK_WIDGET(g_object_new(areaWidgetType, - "area-handler", ah, - NULL)); + updateScroll(a->area); } -void areaUpdateScroll(GtkWidget *area) +uiArea *uiNewArea(uiAreaHandler *ah) { - updateScroll(areaWidget(area)); + uiArea *a; + + a = (uiArea *) uiNewControl(uiAreaType()); + + a->widget = GTK_WIDGET(g_object_new(areaWidgetType, + "area-handler", ah, + NULL)); + a->drawingArea = GTK_DRAWING_AREA(a->widget); + a->area = areaWidget(a->widget); + + // TODO wrap in scrolled window + + uiUnixFinishNewControl(a, uiArea); + + return a; } diff --git a/gtkarea/draw.c b/unix/draw.c similarity index 99% rename from gtkarea/draw.c rename to unix/draw.c index 5bb85301..9938c0ff 100644 --- a/gtkarea/draw.c +++ b/unix/draw.c @@ -1,5 +1,5 @@ // 6 september 2015 -#include "area.h" +#include "uipriv_unix.h" struct uiDrawPath { GArray *pieces; diff --git a/unix/uipriv_unix.h b/unix/uipriv_unix.h index 937b1b62..ffaaae72 100644 --- a/unix/uipriv_unix.h +++ b/unix/uipriv_unix.h @@ -37,5 +37,8 @@ extern void childSetFlag(struct child *c, int flag); extern GtkWidget *childBox(struct child *c); extern void childSetMargined(struct child *c, int margined); +// draw.c +extern uiDrawContext *newContext(cairo_t *); + // TODO #define uiControlQueueResize(...) diff --git a/winarea_d2d/uipriv.h b/winarea_d2d/uipriv.h deleted file mode 100644 index e519c93f..00000000 --- a/winarea_d2d/uipriv.h +++ /dev/null @@ -1,16 +0,0 @@ -typedef struct clickCounter clickCounter; -// you should call Reset() to zero-initialize a new instance -// it doesn't matter that all the non-count fields are zero: the first click will fail the curButton test straightaway, so it'll return 1 and set the rest of the structure accordingly -struct clickCounter { - uintmax_t curButton; - intmax_t rectX0; - intmax_t rectY0; - intmax_t rectX1; - intmax_t rectY1; - uintptr_t prevTime; - uintmax_t count; -}; -extern uintmax_t clickCounterClick(clickCounter *, uintmax_t, intmax_t, intmax_t, uintptr_t, uintptr_t, intmax_t, intmax_t); -extern void clickCounterReset(clickCounter *); - -extern int fromScancode(uintptr_t, uiAreaKeyEvent *);