Merged the GTK+ uiArea implementation with its backend. Now we just have to move the test program.

This commit is contained in:
Pietro Gagliardi 2015-10-08 18:24:09 -04:00
parent cb58ced9d9
commit ef04c18856
12 changed files with 78 additions and 232 deletions

View File

@ -23,6 +23,7 @@ baseHFILES = \
$(osHFILES) $(osHFILES)
baseCFILES = \ baseCFILES = \
areaevents.c \
control.c \ control.c \
menu.c \ menu.c \
ptrarray.c \ ptrarray.c \

View File

@ -1,6 +1,4 @@
// 29 march 2014 // 29 march 2014
// TODO remove
#include <stdint.h>
#include "ui.h" #include "ui.h"
#include "uipriv.h" #include "uipriv.h"

View File

@ -7,24 +7,6 @@
#include <stdint.h> #include <stdint.h>
#include <stddef.h> #include <stddef.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;
};
extern GType areaWidget_get_type(void); extern GType areaWidget_get_type(void);
@ -34,4 +16,3 @@ extern GType areaWidget_get_type(void);
extern GtkWidget *newArea(uiAreaHandler *ah); extern GtkWidget *newArea(uiAreaHandler *ah);
extern void areaUpdateScroll(GtkWidget *area); extern void areaUpdateScroll(GtkWidget *area);
extern uiDrawContext *newContext(cairo_t *);

View File

@ -1,170 +0,0 @@
// 29 march 2014
// TODO remove
#include <stdint.h>
#include <stddef.h>
#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;
}

View File

@ -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 *);

3
ui.h
View File

@ -265,7 +265,8 @@ struct uiAreaHandler {
}; };
_UI_EXTERN uintmax_t uiMenuItemType(void); _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); _UI_EXTERN uiArea *uiNewArea(uiAreaHandler *ah);
struct uiAreaDrawParams { struct uiAreaDrawParams {

View File

@ -42,3 +42,20 @@ extern int shouldQuit(void);
// types.c // types.c
extern void uninitTypes(void); extern void uninitTypes(void);
extern uiTyped *newTyped(uintmax_t type); 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 *);

View File

@ -2,6 +2,7 @@
osCFILES = \ osCFILES = \
unix/alloc.c \ unix/alloc.c \
unix/area.c \
unix/box.c \ unix/box.c \
unix/button.c \ unix/button.c \
unix/checkbox.c \ unix/checkbox.c \
@ -9,6 +10,7 @@ osCFILES = \
unix/combobox.c \ unix/combobox.c \
unix/control.c \ unix/control.c \
unix/datetimepicker.c \ unix/datetimepicker.c \
unix/draw.c \
unix/entry.c \ unix/entry.c \
unix/group.c \ unix/group.c \
unix/label.c \ unix/label.c \

View File

@ -1,5 +1,31 @@
// 4 september 2015 // 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 { struct areaPrivate {
uiArea *a; uiArea *a;
@ -527,14 +553,33 @@ static void areaWidget_scrollable_init(GtkScrollable *iface)
// no need to do anything; the interface only has properties // 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, updateScroll(a->area);
"area-handler", ah,
NULL));
} }
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;
} }

View File

@ -1,5 +1,5 @@
// 6 september 2015 // 6 september 2015
#include "area.h" #include "uipriv_unix.h"
struct uiDrawPath { struct uiDrawPath {
GArray *pieces; GArray *pieces;

View File

@ -37,5 +37,8 @@ extern void childSetFlag(struct child *c, int flag);
extern GtkWidget *childBox(struct child *c); extern GtkWidget *childBox(struct child *c);
extern void childSetMargined(struct child *c, int margined); extern void childSetMargined(struct child *c, int margined);
// draw.c
extern uiDrawContext *newContext(cairo_t *);
// TODO // TODO
#define uiControlQueueResize(...) #define uiControlQueueResize(...)

View File

@ -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 *);