commit a8d438e533db40481c75ddd952a14f14b6f68c94
parent 8a3b7084765b5ce72d0e55adf283e737a0049e03
Author: lumidify <nobody@lumidify.org>
Date:   Thu,  5 Jan 2017 10:24:48 +0100
Turn the basic framework into a single .c and .h file pair
Diffstat:
| M | Makefile |  |  | 2 | +- | 
| D | common.c |  |  | 208 | ------------------------------------------------------------------------------- | 
| D | common.h |  |  | 187 | ------------------------------------------------------------------------------- | 
| M | ltk.c |  |  | 328 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ | 
| M | ltk.h |  |  | 246 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- | 
| D | theme.c |  |  | 64 | ---------------------------------------------------------------- | 
| D | theme.h |  |  | 51 | --------------------------------------------------- | 
| D | window.c |  |  | 153 | ------------------------------------------------------------------------------- | 
| D | window.h |  |  | 54 | ------------------------------------------------------ | 
9 files changed, 571 insertions(+), 722 deletions(-)
diff --git a/Makefile b/Makefile
@@ -1,7 +1,7 @@
 LIBS = -lX11 -lm -ldl
 STD = -std=c89
 FLAGS = -g -w -Wall -Werror -Wextra -pedantic
-CFILES = ltk.c cJSON.c common.c grid.c window.c theme.c button.c test1.c
+CFILES = ltk.c cJSON.c grid.c button.c test1.c
 
 all: test1.c
 	gcc $(STD) $(FLAGS) $(LIBS) $(CFILES) -o test
diff --git a/common.c b/common.c
@@ -1,208 +0,0 @@
-/*
- * This file is part of the Lumidify ToolKit (LTK)
- * Copyright (c) 2016, 2017 Lumidify Productions <lumidify@openmailbox.org>
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in all
- * copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-
-#include "ltk.h"
-
-char *ltk_read_file(const char *path)
-{
-    FILE *f;
-    long len;
-    char *file_contents;
-    f = fopen(path, "rb");
-    fseek(f, 0, SEEK_END);
-    len = ftell(f);
-    fseek(f, 0, SEEK_SET);
-    file_contents = malloc(len + 1);
-    fread(file_contents, 1, len, f);
-    file_contents[len] = '\0';
-    fclose(f);
-
-    return file_contents;
-}
-
-int ltk_collide_rect(LtkRect rect, int x, int y)
-{
-    return (rect.x <= x && (rect.x + rect.w) >= x && rect.y <= y && (rect.y + rect.h) >= y);
-}
-
-void ltk_remove_active_widget(void *widget)
-{
-    if (!widget) return;
-    LtkWidget *parent = widget;
-    LtkWidget *child;
-    while (parent->active_widget)
-    {
-        child = parent->active_widget;
-        child->state = LTK_NORMAL;
-        child->draw(child);
-        parent->active_widget = NULL;
-        parent = child;
-    }
-}
-
-void ltk_change_active_widget_state(void *widget, LtkWidgetState state)
-{
-    if (!widget) return;
-    LtkWidget *ptr = widget;
-    while (ptr = ptr->active_widget)
-    {
-        ptr->state = state;
-        ptr->draw(ptr);
-    }
-}
-
-void ltk_remove_hover_widget(void *widget)
-{
-    if (!widget) return;
-    LtkWidget *parent = widget;
-    LtkWidget *child;
-    while (parent->hover_widget)
-    {
-        child = parent->hover_widget;
-        child->state = child->state == LTK_HOVERACTIVE ? LTK_ACTIVE : LTK_NORMAL;
-        child->draw(child);
-        parent->hover_widget = NULL;
-        parent = child;
-    }
-}
-
-LtkWidget ltk_create_widget(LtkWindow *window, void (*draw)(void *), void (*destroy)(void *), int needs_redraw)
-{
-    LtkWidget widget;
-    widget.window = window;
-    widget.active_widget = NULL;
-    widget.hover_widget = NULL;
-    widget.parent = NULL;
-
-    widget.key_press = NULL;
-    widget.key_release = NULL;
-    widget.mouse_press = NULL;
-    widget.mouse_release = NULL;
-    widget.motion_notify = NULL;
-
-    widget.resize = NULL;
-    widget.draw = draw;
-    widget.destroy = destroy;
-
-    widget.needs_redraw = needs_redraw;
-    widget.state = LTK_NORMAL;
-    widget.row = 0;
-    widget.rect.x = 0;
-    widget.rect.y = 0;
-    widget.rect.w = 100;
-    widget.rect.h = 100;
-
-    widget.row = NULL;
-    widget.column = NULL;
-    widget.row_span = NULL;
-    widget.column_span = NULL;
-    widget.sticky = NULL;
-
-    return widget;
-}
-
-void ltk_mouse_press_event(void *widget, XEvent event)
-{
-    LtkWidget *ptr = widget;
-    if (!ptr || ptr->state == LTK_DISABLED) return;
-    if (event.xbutton.button == 1)
-    {
-        LtkWidget *parent = ptr->parent;
-        if (parent)
-        {
-            ltk_remove_active_widget(parent);
-            parent->active_widget = ptr;
-        }
-        ptr->state = LTK_PRESSED;
-        if (ptr->needs_redraw) ptr->draw(ptr);
-    }
-    if (ptr->mouse_press)
-    {
-        ptr->mouse_press(ptr, event);
-    }
-}
-
-void ltk_mouse_release_event(void *widget, XEvent event)
-{
-    LtkWidget *ptr = widget;
-    if (!ptr || ptr->state == LTK_DISABLED) return;
-    if (ptr->state == LTK_PRESSED)
-    {
-        ptr->state = LTK_HOVERACTIVE;
-        if (ptr->needs_redraw) ptr->draw(ptr);
-    }
-    if (ptr->mouse_release)
-    {
-        ptr->mouse_release(ptr, event);
-    }
-}
-
-void ltk_motion_notify_event(void *widget, XEvent event)
-{
-    LtkWidget *ptr = widget;
-    if (ptr && (ptr->state == LTK_NORMAL || ptr->state == LTK_ACTIVE) &&
-        (event.xmotion.state & Button1Mask) != Button1Mask)
-    {
-        ptr->state = ptr->state == LTK_ACTIVE ? LTK_HOVERACTIVE : LTK_HOVER;
-        LtkWidget *parent = ptr->parent;
-        if (parent)
-        {
-            ltk_remove_hover_widget(parent);
-            parent->hover_widget = ptr;
-        }
-        if (ptr->needs_redraw) ptr->draw(ptr);
-    }
-    if (ptr->motion_notify)
-    {
-        ptr->motion_notify(ptr, event);
-    }
-}
-
-void ltk_handle_event(XEvent event)
-{
-    LtkWindow *window;
-    LtkWidget *root_widget;
-    HASH_FIND_INT(ltk_global->window_hash, &event.xany.window, window);
-    if (!window) return;
-    root_widget = window->root_widget;
-    switch (event.type)
-    {
-    case KeyPress:
-        break;
-    case KeyRelease:
-        break;
-    case ButtonPress:
-        if (root_widget) ltk_mouse_press_event(root_widget, event);
-        break;
-    case ButtonRelease:
-        if (root_widget) ltk_mouse_release_event(root_widget, event);
-        break;
-    case MotionNotify:
-        if (root_widget) ltk_motion_notify_event(root_widget, event);
-        break;
-    default:
-        /* FIXME: users should be able to register other events like closing the window */
-        if (window->other_event)
-            window->other_event(window, event);
-    }
-}
diff --git a/common.h b/common.h
@@ -1,187 +0,0 @@
-/*
- * This file is part of the Lumidify ToolKit (LTK)
- * Copyright (c) 2016, 2017 Lumidify Productions <lumidify@openmailbox.org>
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in all
- * copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-
-#ifndef _LTK_COMMON_H_
-#define _LTK_COMMON_H_
-
-typedef struct LtkWidget LtkWidget;
-typedef struct LtkWindow LtkWindow;
-
-/*
- * Struct to represent a rectangle.
- */
-typedef struct
-{
-    int x;
-    int y;
-    int w;
-    int h;
-} LtkRect;
-
-typedef enum {
-    LTK_STICKY_LEFT = 1 << 0,
-    LTK_STICKY_RIGHT = 1 << 1,
-    LTK_STICKY_TOP = 1 << 2,
-    LTK_STICKY_BOTTOM = 1 << 3
-} LtkStickyMask;
-
-/*
- * An enumeration of all widget states.
- */
-typedef enum
-{
-    LTK_NORMAL = 0,
-    LTK_HOVER = 1,
-    LTK_PRESSED = 2,
-    LTK_ACTIVE = 3,
-    LTK_HOVERACTIVE = 4,
-    LTK_DISABLED = 5
-} LtkWidgetState;
-
-/*
- * A struct to contain all basic widget information.
- * First element of every widget so the widget can
- * be cast to LtkWidget.
- */
-typedef struct LtkWidget
-{
-    /* The window the widget will be displayed on */
-    LtkWindow *window;
-    /* For container widgets; the widget that is currently active */
-    struct LtkWidget *active_widget;
-    /* For container widgets; the widget that is currently highlighted */
-    struct LtkWidget *hover_widget;
-    /* Parent widget */
-    struct LtkWidget *parent;
-
-    /* Called on KeyPress events */
-    void (*key_press)(void *, XEvent event);
-    /* Called on KeyRelease events */
-    void (*key_release)(void *, XEvent event);
-    /* Called on ButtonPress events */
-    void (*mouse_press)(void *, XEvent event);
-    /* Called on ButtonRelease event */
-    void (*mouse_release)(void *, XEvent event);
-    /* Called on MotionNotify events */
-    void (*motion_notify)(void *, XEvent event);
-
-    /* Function to update the widget after its LtkRect has been modified */
-    void (*resize)(void *);
-    /* Function to draw the widget */
-    void (*draw)(void *);
-    /* Function to destroy the widget */
-    void (*destroy)(void *);
-
-    /* Position and size of the widget */
-    LtkRect rect;
-    /* Row of widget if gridded */
-    unsigned int row;
-    /* Column of widget if gridded */
-    unsigned int column;
-    /* Row span of widget if gridded */
-    unsigned int row_span;
-    /* Column span of widget if gridded */
-    unsigned int column_span;
-    /* Specifies if the widget needs to be redrawn after a state change */
-    int needs_redraw : 1;
-    /* State of the widget */
-    LtkWidgetState state : 3;
-    /* Similar to sticky in tk */
-    unsigned short sticky : 4;
-} LtkWidget;
-
-
-/*
- * Check if a rectangle collides with a point.
- * rect: The rectangle.
- * x: The x coordinate of the point.
- * y: The y coordinate of the point.
- */
-int ltk_collide_rect(LtkRect rect, int x, int y);
-
-/*
- * Read a file and return a null-terminated string with the contents.
- * path: The path to the file.
- */
-char *ltk_read_file(const char *path);
-
-/*
- * Recursively set the state of all active_widgets in 'widget' to 'state'.
- */
-void ltk_change_active_widget_state(void *widget, LtkWidgetState state);
-
-/*
- * Recursively set the state of all active_widgets in 'widget' to LTK_NORMAL,
- * redraw the widgets, and remove the references to them from their parents.
- */
-void ltk_remove_active_widget(void *widget);
-
-/*
- * Recursively set the state of all hover_widgets in 'widget' to LTK_NORMAL or
- * LTK_ACTIVE, redraw the widgets, and remove the references to them from their parents.
- */
-void ltk_remove_hover_widget(void *widget);
-
-/*
- * Create a widget.
- * window: The window the widget is to be shown on.
- * draw: The function used to draw the widget.
- * destroy: The function used to destroy the widget.
- * needs_redraw: Flag to indicate if the widget needs to be
- *               be redrawn when it is resized.
- * Returns: The new LtkWidget.
- */
-LtkWidget ltk_create_widget(LtkWindow *window, void (*draw)(void *), void (*destroy)(void *), int needs_redraw);
-
-/*
- * Handles mouse press events for all widgets and calls
- * specific widget handlers if set.
- * widget: Pointer to the widget the mouse is currrently on.
- * event: The event to be handled.
- */
-void ltk_mouse_press_event(void *widget, XEvent event);
-
-/*
- * Handles mouse release events for all widgets and calls
- * specific widget handlers if set.
- * widget: Pointer to the widget the mouse is currrently on.
- * event: The event to be handled.
- */
-void ltk_mouse_release_event(void *widget, XEvent event);
-
-/*
- * Handles mouse motion events for all widgets and calls
- * specific widget handlers if set.
- * widget: Pointer to the widget the mouse is currrently on.
- * event: The event to be handled.
- */
-void ltk_motion_notify_event(void *widget, XEvent event);
-
-/*
- * Handles all events and dispatches them to their
- * respective handlers.
- * event: The event to be handled.
- */
-void ltk_handle_event(XEvent event);
-
-#endif
diff --git a/ltk.c b/ltk.c
@@ -91,3 +91,330 @@ void ltk_mainloop(void)
         */
     }
 }
+
+LtkWindowTheme *ltk_parse_window_theme(cJSON *window_json)
+{
+    LtkWindowTheme *window_theme = malloc(sizeof(LtkWindowTheme));
+    if (!window_theme) ltk_fatal("No memory for new LtkWindowTheme\n");
+    cJSON *border_width = cJSON_GetObjectItem(window_json, "border-width");
+    cJSON *fg = cJSON_GetObjectItem(window_json, "foreground");
+    cJSON *bg = cJSON_GetObjectItem(window_json, "background");
+    window_theme->border_width = border_width ? border_width->valueint : 0;
+    window_theme->fg = ltk_create_xcolor(fg->valuestring);
+    window_theme->bg = ltk_create_xcolor(bg->valuestring);
+
+    return window_theme;
+}
+
+void ltk_redraw_window(LtkWindow *window)
+{
+    LtkWidget *ptr;
+    if (!window)
+    {
+        return;
+    }
+    if (!window->root_widget)
+    {
+        return;
+    }
+    ptr = window->root_widget;
+    ptr->draw(ptr);
+}
+
+LtkWindow *ltk_create_window(const char *title, int x, int y, unsigned int w, unsigned int h)
+{
+    LtkWindow *window = malloc(sizeof(LtkWindow));
+    if (!window) ltk_fatal("Not enough memory left for window!\n");
+    LtkWindowTheme *wtheme = ltk_global->theme->window; /* For convenience */
+    Display *display = ltk_global->display; /* For convenience */
+    window->xwindow = XCreateSimpleWindow(display, DefaultRootWindow(display), x, y, w, h, wtheme->border_width, wtheme->fg.pixel, wtheme->bg.pixel);
+    window->gc = XCreateGC(display, window->xwindow, 0, 0);
+    XSetForeground(display, window->gc, wtheme->fg.pixel);
+    XSetBackground(display, window->gc, wtheme->bg.pixel);
+    XSetStandardProperties(display, window->xwindow, title, NULL, None, NULL, 0, NULL);
+    XSetWMProtocols(display, window->xwindow, <k_global->wm_delete_msg, 1);
+    window->root_widget = NULL;
+
+    window->other_event = <k_window_other_event;
+
+    window->rect.w = 0;
+    window->rect.h = 0;
+    window->rect.x = 0;
+    window->rect.y = 0;
+
+    XClearWindow(display, window->xwindow);
+    XMapRaised(display, window->xwindow);
+    XSelectInput(display, window->xwindow, ExposureMask|KeyPressMask|KeyReleaseMask|ButtonPressMask|ButtonReleaseMask|StructureNotifyMask|PointerMotionMask);
+
+    HASH_ADD_INT(ltk_global->window_hash, xwindow, window);
+
+    return window;
+}
+
+void ltk_remove_window(LtkWindow *window)
+{
+    ltk_destroy_window(window);
+    if (!ltk_global->window_hash) ltk_quit();
+}
+
+void ltk_destroy_window(LtkWindow *window)
+{
+    HASH_DEL(ltk_global->window_hash, window);
+    LtkWidget *ptr = window->root_widget;
+    if (ptr) ptr->destroy(ptr);
+    XDestroyWindow(ltk_global->display, window->xwindow);
+    free(window);
+}
+
+void ltk_window_other_event(void *widget, XEvent event)
+{
+    LtkWindow *window = widget;
+    LtkWidget *ptr = window->root_widget;
+    if (event.type == ConfigureNotify)
+    {
+        unsigned int w, h;
+        w = event.xconfigure.width;
+        h = event.xconfigure.height;
+        if (ptr && ptr->resize && (window->rect.w != w || window->rect.h != h))
+        {
+            window->rect.w = w;
+            window->rect.h = h;
+            ptr->rect.w = w;
+            ptr->rect.h = h;
+            ptr->resize(ptr);
+            ltk_redraw_window(window);
+        }
+    }
+    if (event.type == Expose && event.xexpose.count == 0)
+    {
+        ltk_redraw_window(window);
+    }
+    if (event.type == ClientMessage && event.xclient.data.l[0] == ltk_global->wm_delete_msg)
+    {
+        ltk_remove_window(window);
+    }
+}
+
+LtkTheme *ltk_load_theme(const char *path)
+{
+    char *file_contents = ltk_read_file(path);
+
+    cJSON *json = cJSON_Parse(file_contents);
+    if (!json)
+    {
+        printf("Theme error before: [%s]\n", cJSON_GetErrorPtr());
+        return NULL;
+    }
+    cJSON *button_json = cJSON_GetObjectItem(json, "button");
+    if (!button_json)
+    {
+        printf("Theme error before: [%s]\n", cJSON_GetErrorPtr());
+        return NULL;
+    }
+    cJSON *window_json = cJSON_GetObjectItem(json, "window");
+    if (!window_json)
+    {
+        printf("Theme error before: [%s]\n", cJSON_GetErrorPtr());
+        return NULL;
+    }
+
+    LtkTheme *theme = malloc(sizeof(LtkTheme));
+    theme->button = ltk_parse_button_theme(button_json);
+    theme->window = ltk_parse_window_theme(window_json);
+
+    free(file_contents);
+    cJSON_Delete(json);
+
+    return theme;
+}
+
+void ltk_destroy_theme(LtkTheme *theme)
+{
+    free(theme->button);
+    free(theme->window);
+    free(theme);
+}
+
+char *ltk_read_file(const char *path)
+{
+    FILE *f;
+    long len;
+    char *file_contents;
+    f = fopen(path, "rb");
+    fseek(f, 0, SEEK_END);
+    len = ftell(f);
+    fseek(f, 0, SEEK_SET);
+    file_contents = malloc(len + 1);
+    fread(file_contents, 1, len, f);
+    file_contents[len] = '\0';
+    fclose(f);
+
+    return file_contents;
+}
+
+int ltk_collide_rect(LtkRect rect, int x, int y)
+{
+    return (rect.x <= x && (rect.x + rect.w) >= x && rect.y <= y && (rect.y + rect.h) >= y);
+}
+
+void ltk_remove_active_widget(void *widget)
+{
+    if (!widget) return;
+    LtkWidget *parent = widget;
+    LtkWidget *child;
+    while (parent->active_widget)
+    {
+        child = parent->active_widget;
+        child->state = LTK_NORMAL;
+        child->draw(child);
+        parent->active_widget = NULL;
+        parent = child;
+    }
+}
+
+void ltk_change_active_widget_state(void *widget, LtkWidgetState state)
+{
+    if (!widget) return;
+    LtkWidget *ptr = widget;
+    while (ptr = ptr->active_widget)
+    {
+        ptr->state = state;
+        ptr->draw(ptr);
+    }
+}
+
+void ltk_remove_hover_widget(void *widget)
+{
+    if (!widget) return;
+    LtkWidget *parent = widget;
+    LtkWidget *child;
+    while (parent->hover_widget)
+    {
+        child = parent->hover_widget;
+        child->state = child->state == LTK_HOVERACTIVE ? LTK_ACTIVE : LTK_NORMAL;
+        child->draw(child);
+        parent->hover_widget = NULL;
+        parent = child;
+    }
+}
+
+LtkWidget ltk_create_widget(LtkWindow *window, void (*draw)(void *), void (*destroy)(void *), int needs_redraw)
+{
+    LtkWidget widget;
+    widget.window = window;
+    widget.active_widget = NULL;
+    widget.hover_widget = NULL;
+    widget.parent = NULL;
+
+    widget.key_press = NULL;
+    widget.key_release = NULL;
+    widget.mouse_press = NULL;
+    widget.mouse_release = NULL;
+    widget.motion_notify = NULL;
+
+    widget.resize = NULL;
+    widget.draw = draw;
+    widget.destroy = destroy;
+
+    widget.needs_redraw = needs_redraw;
+    widget.state = LTK_NORMAL;
+    widget.row = 0;
+    widget.rect.x = 0;
+    widget.rect.y = 0;
+    widget.rect.w = 100;
+    widget.rect.h = 100;
+
+    widget.row = NULL;
+    widget.column = NULL;
+    widget.row_span = NULL;
+    widget.column_span = NULL;
+    widget.sticky = NULL;
+
+    return widget;
+}
+
+void ltk_mouse_press_event(void *widget, XEvent event)
+{
+    LtkWidget *ptr = widget;
+    if (!ptr || ptr->state == LTK_DISABLED) return;
+    if (event.xbutton.button == 1)
+    {
+        LtkWidget *parent = ptr->parent;
+        if (parent)
+        {
+            ltk_remove_active_widget(parent);
+            parent->active_widget = ptr;
+        }
+        ptr->state = LTK_PRESSED;
+        if (ptr->needs_redraw) ptr->draw(ptr);
+    }
+    if (ptr->mouse_press)
+    {
+        ptr->mouse_press(ptr, event);
+    }
+}
+
+void ltk_mouse_release_event(void *widget, XEvent event)
+{
+    LtkWidget *ptr = widget;
+    if (!ptr || ptr->state == LTK_DISABLED) return;
+    if (ptr->state == LTK_PRESSED)
+    {
+        ptr->state = LTK_HOVERACTIVE;
+        if (ptr->needs_redraw) ptr->draw(ptr);
+    }
+    if (ptr->mouse_release)
+    {
+        ptr->mouse_release(ptr, event);
+    }
+}
+
+void ltk_motion_notify_event(void *widget, XEvent event)
+{
+    LtkWidget *ptr = widget;
+    if (ptr && (ptr->state == LTK_NORMAL || ptr->state == LTK_ACTIVE) &&
+        (event.xmotion.state & Button1Mask) != Button1Mask)
+    {
+        ptr->state = ptr->state == LTK_ACTIVE ? LTK_HOVERACTIVE : LTK_HOVER;
+        LtkWidget *parent = ptr->parent;
+        if (parent)
+        {
+            ltk_remove_hover_widget(parent);
+            parent->hover_widget = ptr;
+        }
+        if (ptr->needs_redraw) ptr->draw(ptr);
+    }
+    if (ptr->motion_notify)
+    {
+        ptr->motion_notify(ptr, event);
+    }
+}
+
+void ltk_handle_event(XEvent event)
+{
+    LtkWindow *window;
+    LtkWidget *root_widget;
+    HASH_FIND_INT(ltk_global->window_hash, &event.xany.window, window);
+    if (!window) return;
+    root_widget = window->root_widget;
+    switch (event.type)
+    {
+    case KeyPress:
+        break;
+    case KeyRelease:
+        break;
+    case ButtonPress:
+        if (root_widget) ltk_mouse_press_event(root_widget, event);
+        break;
+    case ButtonRelease:
+        if (root_widget) ltk_mouse_release_event(root_widget, event);
+        break;
+    case MotionNotify:
+        if (root_widget) ltk_motion_notify_event(root_widget, event);
+        break;
+    default:
+        /* FIXME: users should be able to register other events like closing the window */
+        if (window->other_event)
+            window->other_event(window, event);
+    }
+}+
\ No newline at end of file
diff --git a/ltk.h b/ltk.h
@@ -30,11 +30,134 @@
 #include <X11/Xutil.h>
 #include "cJSON.h"
 #include "uthash.h"
-#include "common.h"
-#include "window.h"
-#include "theme.h"
-#include "grid.h"
+
+/*
+ * Struct to represent a rectangle.
+ */
+typedef struct
+{
+    int x;
+    int y;
+    int w;
+    int h;
+} LtkRect;
+
+typedef enum {
+    LTK_STICKY_LEFT = 1 << 0,
+    LTK_STICKY_RIGHT = 1 << 1,
+    LTK_STICKY_TOP = 1 << 2,
+    LTK_STICKY_BOTTOM = 1 << 3
+} LtkStickyMask;
+
+/*
+ * An enumeration of all widget states.
+ */
+typedef enum
+{
+    LTK_NORMAL = 0,
+    LTK_HOVER = 1,
+    LTK_PRESSED = 2,
+    LTK_ACTIVE = 3,
+    LTK_HOVERACTIVE = 4,
+    LTK_DISABLED = 5
+} LtkWidgetState;
+
+typedef struct LtkWindow LtkWindow;
+
+/*
+ * A struct to contain all basic widget information.
+ * First element of every widget so the widget can
+ * be cast to LtkWidget.
+ */
+typedef struct LtkWidget
+{
+    /* The window the widget will be displayed on */
+    LtkWindow *window;
+    /* For container widgets; the widget that is currently active */
+    struct LtkWidget *active_widget;
+    /* For container widgets; the widget that is currently highlighted */
+    struct LtkWidget *hover_widget;
+    /* Parent widget */
+    struct LtkWidget *parent;
+
+    /* Called on KeyPress events */
+    void (*key_press)(void *, XEvent event);
+    /* Called on KeyRelease events */
+    void (*key_release)(void *, XEvent event);
+    /* Called on ButtonPress events */
+    void (*mouse_press)(void *, XEvent event);
+    /* Called on ButtonRelease event */
+    void (*mouse_release)(void *, XEvent event);
+    /* Called on MotionNotify events */
+    void (*motion_notify)(void *, XEvent event);
+
+    /* Function to update the widget after its LtkRect has been modified */
+    void (*resize)(void *);
+    /* Function to draw the widget */
+    void (*draw)(void *);
+    /* Function to destroy the widget */
+    void (*destroy)(void *);
+
+    /* Position and size of the widget */
+    LtkRect rect;
+    /* Row of widget if gridded */
+    unsigned int row;
+    /* Column of widget if gridded */
+    unsigned int column;
+    /* Row span of widget if gridded */
+    unsigned int row_span;
+    /* Column span of widget if gridded */
+    unsigned int column_span;
+    /* Specifies if the widget needs to be redrawn after a state change */
+    int needs_redraw : 1;
+    /* State of the widget */
+    LtkWidgetState state : 3;
+    /* Similar to sticky in tk */
+    unsigned short sticky : 4;
+} LtkWidget;
+
+/*
+ * Struct to represent a window.
+ */
+typedef struct LtkWindow
+{
+    Window xwindow;
+    GC gc;
+    void *root_widget;
+    void (*other_event)(void *, XEvent event);
+    LtkRect rect;
+    UT_hash_handle hh;
+} LtkWindow;
+
+/*
+ * Struct to represent the border width,
+ * foreground color, and background color
+ * of a window.
+ */
+typedef struct LtkWindowTheme
+{
+    int border_width;
+    XColor fg;
+    XColor bg;
+} LtkWindowTheme;
+
 #include "button.h"
+#include "grid.h"
+
+/*
+ * Struct to contain all styles needed by LTK.
+ */
+typedef struct
+{
+    LtkWindowTheme *window;
+    LtkButtonTheme *button;
+} LtkTheme;
+
+/*
+ * Load a theme from a JSON file.
+ * path: The path to the file.
+ */
+LtkTheme *ltk_load_theme(const char *path);
 
 /*
  * Struct to contain all global information.
@@ -80,4 +203,119 @@ XColor ltk_create_xcolor(const char *hex);
  */
 void ltk_mainloop(void);
 
+/*
+ * Create a window.
+ * title: The title of the window.
+ * x: The x position of the window.
+ * y: The y position of the window.
+ * w: The width of the window.
+ * h: The height of the window.
+ */
+LtkWindow *ltk_create_window(const char *title, int x, int y, unsigned int w, unsigned int h);
+
+/*
+ * Redraw the widgets in a window.
+ * window: The window to redraw.
+ */
+void ltk_redraw_window(LtkWindow *window);
+
+/*
+ * Destroy a window and quit the application
+ * if no windows are left.
+ * window: The window to remove.
+ */
+void ltk_remove_window(LtkWindow *window);
+
+/*
+ * Destroy a window, freeing its memory.
+ * window: The window to destroy.
+ */
+void ltk_destroy_window(LtkWindow *window);
+
+/*
+ * Handles any event other than mouse or keyboard events.
+ * widget: Pointer to the window.
+ * event: The XEvent to be handled.
+ */
+void ltk_window_other_event(void *widget, XEvent event);
+
+/*
+ * Destroy an LtkTheme struct.
+ * theme: Pointer to the struct.
+ */
+void ltk_destroy_theme(LtkTheme *theme);
+
+/*
+ * Check if a rectangle collides with a point.
+ * rect: The rectangle.
+ * x: The x coordinate of the point.
+ * y: The y coordinate of the point.
+ */
+int ltk_collide_rect(LtkRect rect, int x, int y);
+
+/*
+ * Read a file and return a null-terminated string with the contents.
+ * path: The path to the file.
+ */
+char *ltk_read_file(const char *path);
+
+/*
+ * Recursively set the state of all active_widgets in 'widget' to 'state'.
+ */
+void ltk_change_active_widget_state(void *widget, LtkWidgetState state);
+
+/*
+ * Recursively set the state of all active_widgets in 'widget' to LTK_NORMAL,
+ * redraw the widgets, and remove the references to them from their parents.
+ */
+void ltk_remove_active_widget(void *widget);
+
+/*
+ * Recursively set the state of all hover_widgets in 'widget' to LTK_NORMAL or
+ * LTK_ACTIVE, redraw the widgets, and remove the references to them from their parents.
+ */
+void ltk_remove_hover_widget(void *widget);
+
+/*
+ * Create a widget.
+ * window: The window the widget is to be shown on.
+ * draw: The function used to draw the widget.
+ * destroy: The function used to destroy the widget.
+ * needs_redraw: Flag to indicate if the widget needs to be
+ *               be redrawn when it is resized.
+ * Returns: The new LtkWidget.
+ */
+LtkWidget ltk_create_widget(LtkWindow *window, void (*draw)(void *), void (*destroy)(void *), int needs_redraw);
+
+/*
+ * Handles mouse press events for all widgets and calls
+ * specific widget handlers if set.
+ * widget: Pointer to the widget the mouse is currrently on.
+ * event: The event to be handled.
+ */
+void ltk_mouse_press_event(void *widget, XEvent event);
+
+/*
+ * Handles mouse release events for all widgets and calls
+ * specific widget handlers if set.
+ * widget: Pointer to the widget the mouse is currrently on.
+ * event: The event to be handled.
+ */
+void ltk_mouse_release_event(void *widget, XEvent event);
+
+/*
+ * Handles mouse motion events for all widgets and calls
+ * specific widget handlers if set.
+ * widget: Pointer to the widget the mouse is currrently on.
+ * event: The event to be handled.
+ */
+void ltk_motion_notify_event(void *widget, XEvent event);
+
+/*
+ * Handles all events and dispatches them to their
+ * respective handlers.
+ * event: The event to be handled.
+ */
+void ltk_handle_event(XEvent event);
+
 #endif
diff --git a/theme.c b/theme.c
@@ -1,64 +0,0 @@
-/*
- * This file is part of the Lumidify ToolKit (LTK)
- * Copyright (c) 2016, 2017 Lumidify Productions <lumidify@openmailbox.org>
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in all
- * copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-
-#include "ltk.h"
-
-LtkTheme *ltk_load_theme(const char *path)
-{
-    char *file_contents = ltk_read_file(path);
-
-    cJSON *json = cJSON_Parse(file_contents);
-    if (!json)
-    {
-        printf("Theme error before: [%s]\n", cJSON_GetErrorPtr());
-        return NULL;
-    }
-    cJSON *button_json = cJSON_GetObjectItem(json, "button");
-    if (!button_json)
-    {
-        printf("Theme error before: [%s]\n", cJSON_GetErrorPtr());
-        return NULL;
-    }
-    cJSON *window_json = cJSON_GetObjectItem(json, "window");
-    if (!window_json)
-    {
-        printf("Theme error before: [%s]\n", cJSON_GetErrorPtr());
-        return NULL;
-    }
-
-    LtkTheme *theme = malloc(sizeof(LtkTheme));
-    theme->button = ltk_parse_button_theme(button_json);
-    theme->window = ltk_parse_window_theme(window_json);
-
-    free(file_contents);
-    cJSON_Delete(json);
-
-    return theme;
-}
-
-void ltk_destroy_theme(LtkTheme *theme)
-{
-    free(theme->button);
-    free(theme->window);
-    free(theme);
-}
diff --git a/theme.h b/theme.h
@@ -1,51 +0,0 @@
-/*
- * This file is part of the Lumidify ToolKit (LTK)
- * Copyright (c) 2016, 2017 Lumidify Productions <lumidify@openmailbox.org>
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in all
- * copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-
-#ifndef _LTK_THEME_H_
-#define _LTK_THEME_H_
-
-typedef struct LtkWindowTheme LtkWindowTheme;
-typedef struct LtkButtonTheme LtkButtonTheme;
-
-/*
- * Struct to contain all styles needed by LTK.
- */
-typedef struct
-{
-    LtkWindowTheme *window;
-    LtkButtonTheme *button;
-} LtkTheme;
-
-/*
- * Load a theme from a JSON file.
- * path: The path to the file.
- */
-LtkTheme *ltk_load_theme(const char *path);
-
-/*
- * Destroy an LtkTheme struct.
- * theme: Pointer to the struct.
- */
-void ltk_destroy_theme(LtkTheme *theme);
-
-#endif
diff --git a/window.c b/window.c
@@ -1,153 +0,0 @@
-/*
- * This file is part of the Lumidify ToolKit (LTK)
- * Copyright (c) 2016, 2017 Lumidify Productions <lumidify@openmailbox.org>
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in all
- * copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-
-#include "ltk.h"
-
-LtkWindowTheme *ltk_parse_window_theme(cJSON *window_json)
-{
-    LtkWindowTheme *window_theme = malloc(sizeof(LtkWindowTheme));
-    if (!window_theme) ltk_fatal("No memory for new LtkWindowTheme\n");
-    cJSON *border_width = cJSON_GetObjectItem(window_json, "border-width");
-    cJSON *fg = cJSON_GetObjectItem(window_json, "foreground");
-    cJSON *bg = cJSON_GetObjectItem(window_json, "background");
-    window_theme->border_width = border_width ? border_width->valueint : 0;
-    window_theme->fg = ltk_create_xcolor(fg->valuestring);
-    window_theme->bg = ltk_create_xcolor(bg->valuestring);
-
-    return window_theme;
-}
-
-void ltk_redraw_window(LtkWindow *window)
-{
-    LtkWidget *ptr;
-    if (!window)
-    {
-        return;
-    }
-    if (!window->root_widget)
-    {
-        return;
-    }
-    ptr = window->root_widget;
-    ptr->draw(ptr);
-}
-
-LtkWindow *ltk_create_window(const char *title, int x, int y, unsigned int w, unsigned int h)
-{
-    LtkWindow *window = malloc(sizeof(LtkWindow));
-    if (!window) ltk_fatal("Not enough memory left for window!\n");
-    LtkWindowTheme *wtheme = ltk_global->theme->window; /* For convenience */
-    Display *display = ltk_global->display; /* For convenience */
-    window->xwindow = XCreateSimpleWindow(display, DefaultRootWindow(display), x, y, w, h, wtheme->border_width, wtheme->fg.pixel, wtheme->bg.pixel);
-    window->gc = XCreateGC(display, window->xwindow, 0, 0);
-    XSetForeground(display, window->gc, wtheme->fg.pixel);
-    XSetBackground(display, window->gc, wtheme->bg.pixel);
-    XSetStandardProperties(display, window->xwindow, title, NULL, None, NULL, 0, NULL);
-    XSetWMProtocols(display, window->xwindow, <k_global->wm_delete_msg, 1);
-    window->root_widget = NULL;
-
-    window->other_event = <k_window_other_event;
-
-    window->rect.w = 0;
-    window->rect.h = 0;
-    window->rect.x = 0;
-    window->rect.y = 0;
-
-    XClearWindow(display, window->xwindow);
-    XMapRaised(display, window->xwindow);
-    XSelectInput(display, window->xwindow, ExposureMask|KeyPressMask|KeyReleaseMask|ButtonPressMask|ButtonReleaseMask|StructureNotifyMask|PointerMotionMask);
-
-    HASH_ADD_INT(ltk_global->window_hash, xwindow, window);
-
-    return window;
-}
-
-void ltk_remove_window(LtkWindow *window)
-{
-    ltk_destroy_window(window);
-    if (!ltk_global->window_hash) ltk_quit();
-}
-
-void ltk_destroy_window(LtkWindow *window)
-{
-    HASH_DEL(ltk_global->window_hash, window);
-    LtkWidget *ptr = window->root_widget;
-    if (ptr) ptr->destroy(ptr);
-    XDestroyWindow(ltk_global->display, window->xwindow);
-    free(window);
-}
-
-void ltk_window_other_event(void *widget, XEvent event)
-{
-    LtkWindow *window = widget;
-    LtkWidget *ptr = window->root_widget;
-    if (event.type == ConfigureNotify)
-    {
-        unsigned int w, h;
-        w = event.xconfigure.width;
-        h = event.xconfigure.height;
-        if (ptr && ptr->resize && (window->rect.w != w || window->rect.h != h))
-        {
-            window->rect.w = w;
-            window->rect.h = h;
-            ptr->rect.w = w;
-            ptr->rect.h = h;
-            ptr->resize(ptr);
-            ltk_redraw_window(window);
-        }
-    }
-    if (event.type == Expose && event.xexpose.count == 0)
-    {
-        ltk_redraw_window(window);
-    }
-    if (event.type == ClientMessage && event.xclient.data.l[0] == ltk_global->wm_delete_msg)
-    {
-        ltk_remove_window(window);
-    }
-}
-
-/*
-void ltk_resize_window(Uint32 id, int w, int h)
-{
-    LtkWindow *window;
-    LtkWidget *ptr;
-    HASH_FIND_INT(ltk_window_hash, &id, window);
-    if (!window)
-    {
-        return;
-    }
-    ptr = window->root_widget;
-    if (!ptr)
-    {
-        return;
-    }
-    ptr->rect.w = w;
-    ptr->rect.h = h;
-    ptr->resize(ptr);
-}
-
-void ltk_window_set_root_widget(LtkWindow *window, void *root_widget)
-{
-    window->root_widget = root_widget;
-}
-*/
diff --git a/window.h b/window.h
@@ -1,54 +0,0 @@
-/*
- * This file is part of the Lumidify ToolKit (LTK)
- * Copyright (c) 2016, 2017 Lumidify Productions <lumidify@openmailbox.org>
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in all
- * copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-
-#ifndef _LTK_WINDOW_H_
-#define _LTK_WINDOW_H_
-
-#include <X11/Xlib.h>
-#include "event.h"
-
-typedef struct LtkWindow
-{
-    Window xwindow;
-    GC gc;
-    void *root_widget;
-    void (*other_event)(void *, XEvent event);
-    LtkRect rect;
-    UT_hash_handle hh;
-} LtkWindow;
-
-typedef struct LtkWindowTheme
-{
-    int border_width;
-    XColor fg;
-    XColor bg;
-} LtkWindowTheme;
-
-LtkWindow *ltk_create_window(const char *title, int x, int y, unsigned int w, unsigned int h);
-void ltk_redraw_window(LtkWindow *window);
-void ltk_destroy_window(LtkWindow *window);
-void ltk_window_key_event(void *widget, XEvent event);
-void ltk_window_mouse_event(void *widget, XEvent event);
-void ltk_window_other_event(void *widget, XEvent event);
-
-#endif