diff --git a/userspace/gui/basic/draw.c b/userspace/gui/basic/draw.c deleted file mode 100644 index 86f041c5..00000000 --- a/userspace/gui/basic/draw.c +++ /dev/null @@ -1,483 +0,0 @@ -/* This file is part of ToaruOS and is released under the terms - * of the NCSA / University of Illinois License - see LICENSE.md - * Copyright (C) 2013-2014 Kevin Lange - */ - -/* - * draw - * - * Windowed graphical drawing tool. - * Simple painting application. - * - * This is also the playground for the work-in-progress - * ToaruToolKit GUI toolkit. - */ -#include - -#include "lib/yutani.h" -#include "gui/ttk/ttk.h" -#include "lib/list.h" - -/* XXX TOOLKIT FUNCTIONS */ - -gfx_context_t * ctx; -yutani_window_t * wina; -yutani_t * yctx; - -/* Active TTK window XXX */ -static yutani_window_t * ttk_window = NULL; - -/* TTK Window's objects XXX */ -static list_t * ttk_objects = NULL; - -#define TTK_BUTTON_TYPE 0x00000001 -#define TTK_RAW_SURFACE_TYPE 0x00000002 - -#define TTK_BUTTON_STATE_NORMAL 0 -#define TTK_BUTTON_STATE_DOWN 1 - -cairo_surface_t * internal_surface; - -/* - * Core TTK GUI object - */ -typedef struct { - uint32_t type; /* Object type indicator (for introspection) */ - int32_t x; /* Coordinates */ - int32_t y; - int32_t width; /* Sizes */ - int32_t height; - void (*render_func)(void *, cairo_t * cr); /* (Internal) function to render the object */ - void (*click_callback)(void *, struct yutani_msg_window_mouse_event *); /* Callback function for clicking */ -} ttk_object; - -/* TTK Button */ -typedef struct { - ttk_object _super; /* Parent type (Object -> Button) */ - char * title; /* Button text */ - uint32_t fill_color; /* Fill color */ - uint32_t fore_color; /* Text color */ - int button_state; -} ttk_button; - -typedef struct { - ttk_object _super; - gfx_context_t * surface; -} ttk_raw_surface; - -void ttk_render_button(void * s, cairo_t * cr) { - ttk_object * self = (ttk_object *)s; - - if (((ttk_button *)self)->button_state == TTK_BUTTON_STATE_DOWN) { - _ttk_draw_button_select(cr, self->x, self->y, self->width, self->height, ((ttk_button *)self)->title); - } else { - _ttk_draw_button(cr, self->x, self->y, self->width, self->height, ((ttk_button *)self)->title); - } -#if 0 - /* Fill the button */ - for (uint16_t y = self->y + 1; y < self->y + self->height; y++) { - draw_line(ctx, self->x, self->x + self->width, y, y, ((ttk_button *)self)->fill_color); - } - /* Then draw the border */ - uint32_t border_color = rgb(0,0,0); - draw_line(ctx, self->x, self->x + self->width, self->y, self->y, border_color); - draw_line(ctx, self->x, self->x, self->y, self->y + self->height, border_color); - draw_line(ctx, self->x + self->width, self->x + self->width, self->y, self->y + self->height, border_color); - draw_line(ctx, self->x, self->x + self->width, self->y + self->height, self->y + self->height, border_color); - /* button-specific stuff */ - uint32_t w = draw_string_width(((ttk_button * )self)->title); - uint16_t offset = (self->width - w) / 2; - draw_string(ctx, self->x + offset, self->y + self->height - 3, ((ttk_button *)self)->fore_color, ((ttk_button * )self)->title); -#endif -} - -void ttk_render_raw_surface(void * s, cairo_t * cr) { - ttk_object * self = (ttk_object *)s; - - gfx_context_t * surface = ((ttk_raw_surface *)self)->surface; - - { - cairo_save(cr); - int stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, surface->width); - cairo_surface_t * internal_surface = cairo_image_surface_create_for_data(surface->backbuffer, CAIRO_FORMAT_ARGB32, surface->width, surface->height, stride); - - cairo_set_source_surface(cr, internal_surface, self->x, self->y); - cairo_paint(cr); - - cairo_surface_destroy(internal_surface); - cairo_restore(cr); - } -} - -ttk_button * ttk_button_new(char * title, void (*callback)(void *, struct yutani_msg_window_mouse_event *)) { - ttk_button * out = malloc(sizeof(ttk_button)); - out->title = title; - out->fill_color = rgb(100,100,100); - out->button_state = TTK_BUTTON_STATE_NORMAL; - - /* Standard */ - ttk_object * obj = (ttk_object *)out; - obj->click_callback = callback; - obj->render_func = ttk_render_button; - obj->type = TTK_BUTTON_TYPE; - obj->x = 0; - obj->y = 0; - obj->width = 20; - obj->height = 20; - - list_insert(ttk_objects, obj); - return out; -} - -ttk_raw_surface * ttk_raw_surface_new(int width, int height) { - ttk_raw_surface * out = malloc(sizeof(ttk_raw_surface)); - - ttk_object * obj = (ttk_object *)out; - - out->surface = malloc(sizeof(gfx_context_t)); - out->surface->width = width; - out->surface->height = height; - out->surface->depth = 32; - out->surface->buffer = malloc(sizeof(uint32_t) * width * height); - out->surface->backbuffer = out->surface->buffer; - - draw_fill(out->surface, rgb(255,255,255)); - - obj->width = width; - obj->height = height; - obj->x = decor_left_width + 4; - obj->y = decor_top_height + 4; - - obj->click_callback = NULL; - obj->type = TTK_RAW_SURFACE_TYPE; - - obj->render_func = ttk_render_raw_surface; - - list_insert(ttk_objects, obj); - return out; -} - -/* - * Reposition a TTK object - */ -void ttk_position(ttk_object * obj, int x, int y, int width, int height) { - obj->x = x; - obj->y = y; - obj->width = width; - obj->height = height; -} - -int ttk_within(ttk_object * obj, struct yutani_msg_window_mouse_event * evt) { - if (evt->new_x >= obj->x && evt->new_x < obj->x + obj->width && - evt->new_y >= obj->y && evt->new_y < obj->y + obj->height) { - return 1; - } - return 0; -} - -void ttk_check_click(struct yutani_msg_window_mouse_event * evt) { - if (evt->command == YUTANI_MOUSE_EVENT_CLICK) { - foreach(node, ttk_objects) { - ttk_object * obj = (ttk_object *)node->value; - if (ttk_within(obj, evt)) { - if (obj->click_callback) { - obj->click_callback(obj, evt); - } - } - } - } else if (evt->command == YUTANI_MOUSE_EVENT_DOWN) { - fprintf(stderr, "Mouse down: %d, %d\n", evt->new_x, evt->new_y); - } -} - -void ttk_render() { - /* XXX */ - ttk_window_t _window; - ttk_window_t * window = &_window; - - window->core_context = ctx; - window->core_window = ttk_window; - window->width = ctx->width; // - decor_width(); - window->height = ctx->height; //- decor_height(); - window->off_x = 0; //decor_left_width; - window->off_y = 0; //decor_top_height; - window->title = "Draw!"; - - draw_fill(ctx, rgb(TTK_BACKGROUND_DEFAULT)); - ttk_redraw_borders(window); - - { - int stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, window->core_window->width); - cairo_surface_t * core_surface = cairo_image_surface_create_for_data(window->core_context->backbuffer, CAIRO_FORMAT_ARGB32, window->core_window->width, window->core_window->height, stride); - cairo_t * cr_main = cairo_create(core_surface); - - /* TODO move this surface to a ttk_frame_t or something; GUIs man, go look at some Qt or GTK APIs! */ - cairo_surface_t * internal_surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, window->width, window->height); - cairo_t * cr = cairo_create(internal_surface); - - foreach(node, ttk_objects) { - ttk_object * obj = (ttk_object *)node->value; - if (obj->render_func) { - obj->render_func(obj, cr); - } - } - - /* Paint the window's internal surface onto the backbuffer */ - cairo_set_source_surface(cr_main, internal_surface, (double)window->off_x, (double)window->off_y); - cairo_paint(cr_main); - cairo_surface_flush(internal_surface); - cairo_destroy(cr); - cairo_surface_destroy(internal_surface); - - /* In theory, we don't actually want to destroy much of any of this; maybe the cairo_t */ - cairo_surface_flush(core_surface); - cairo_destroy(cr_main); - cairo_surface_destroy(core_surface); - } - - flip(window->core_context); - yutani_flip(yctx, wina); -} - -void setup_ttk(yutani_window_t * window) { - ttk_window = window; - ttk_objects = list_create(); - init_shmemfonts(); -} - -uint32_t drawing_color = 0; -uint16_t quit = 0; - -ttk_button * button_red; -ttk_button * button_green; -ttk_button * button_blue; - -ttk_button * button_thick; -ttk_button * button_thin; -ttk_raw_surface * drawing_surface; -int thick = 0; - -static void set_color(void * button, struct yutani_msg_window_mouse_event * event) { - ttk_button * self = (ttk_button *)button; - - if (button_blue != self) button_blue->button_state = TTK_BUTTON_STATE_NORMAL; - if (button_red != self) button_red->button_state = TTK_BUTTON_STATE_NORMAL; - if (button_green != self) button_green->button_state = TTK_BUTTON_STATE_NORMAL; - - self->button_state = TTK_BUTTON_STATE_DOWN; - drawing_color = self->fill_color; - - ttk_render(); -} - -static void set_thickness_thick(void * button, struct yutani_msg_window_mouse_event * event) { -#if 0 - button_thick->fill_color = rgb(127,127,127); - button_thick->fore_color = rgb(255,255,255); - button_thin->fill_color = rgb(40,40,40); - button_thin->fore_color = rgb(255,255,255); -#endif - button_thick->button_state = TTK_BUTTON_STATE_DOWN; - button_thin->button_state = TTK_BUTTON_STATE_NORMAL; - thick = 1; - ttk_render(); -} - -static void set_thickness_thin(void * button, struct yutani_msg_window_mouse_event * event) { -#if 0 - button_thin->fill_color = rgb(127,127,127); - button_thin->fore_color = rgb(255,255,255); - button_thick->fill_color = rgb(40,40,40); - button_thick->fore_color = rgb(255,255,255); -#endif - button_thin->button_state = TTK_BUTTON_STATE_DOWN; - button_thick->button_state = TTK_BUTTON_STATE_NORMAL; - thick = 0; - ttk_render(); -} - -static void resize_finish(int width, int height) { - yutani_window_resize_accept(yctx, wina, width, height); - reinit_graphics_yutani(ctx, wina); - ttk_render(); - yutani_window_resize_done(yctx, wina); - yutani_flip(yctx, wina); -} - -static void resize_button(void * button, struct yutani_msg_window_mouse_event * event) { - yutani_window_resize(yctx, wina, 600, 600); - yutani_msg_t * m = yutani_wait_for(yctx, YUTANI_MSG_RESIZE_OFFER); - struct yutani_msg_window_resize * wr = (void*)m->data; - resize_finish(wr->width, wr->height); - free(m); -} - -void keep_drawing(struct yutani_msg_window_mouse_event * mouse) { - double thickness = thick ? 2.0 : 0.5;; - - int old_x = mouse->old_x - ((ttk_object *)drawing_surface)->x; - int old_y = mouse->old_y - ((ttk_object *)drawing_surface)->y; - - int new_x = mouse->new_x - ((ttk_object *)drawing_surface)->x; - int new_y = mouse->new_y - ((ttk_object *)drawing_surface)->y; - - { - cairo_t * cr = cairo_create(internal_surface); - - cairo_set_source_rgb(cr, _RED(drawing_color) / 255.0, _GRE(drawing_color) / 255.0, _BLU(drawing_color) / 255.0); - cairo_set_line_width(cr, thickness); - - cairo_move_to(cr, old_x, old_y); - cairo_line_to(cr, new_x, new_y); - - cairo_stroke(cr); - - cairo_destroy(cr); - } - - { - int stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, wina->width); - cairo_surface_t * core_surface = cairo_image_surface_create_for_data(ctx->backbuffer, CAIRO_FORMAT_ARGB32, wina->width, wina->height, stride); - cairo_t * cr = cairo_create(core_surface); - - cairo_rectangle(cr, ((ttk_object*)drawing_surface)->x, ((ttk_object*)drawing_surface)->y, ((ttk_object*)drawing_surface)->width, ((ttk_object*)drawing_surface)->height); - cairo_clip(cr); - - cairo_set_source_rgb(cr, _RED(drawing_color) / 255.0, _GRE(drawing_color) / 255.0, _BLU(drawing_color) / 255.0); - cairo_set_line_width(cr, thickness); - - cairo_move_to(cr, old_x + ((ttk_object*)drawing_surface)->x, old_y + ((ttk_object*)drawing_surface)->y); - cairo_line_to(cr, new_x + ((ttk_object*)drawing_surface)->x, new_y + ((ttk_object*)drawing_surface)->y); - - cairo_stroke(cr); - cairo_destroy(cr); - cairo_surface_destroy(core_surface); - - flip(ctx); - } - -} - -int main (int argc, char ** argv) { - int left = 30; - int top = 30; - - int width = 450; - int height = 450; - - yctx = yutani_init(); - - init_decorations(); - - /* Do something with a window */ - wina = yutani_window_create(yctx, width + decor_width(), height+decor_height()); - yutani_window_move(yctx, wina, 100, 100); - - ctx = init_graphics_yutani_double_buffer(wina); - draw_fill(ctx, rgba(0,0,0,0)); - - yutani_window_advertise_icon(yctx, wina, "Draw!", "applications-painting"); - - setup_ttk(wina); - - - button_blue = ttk_button_new("Blue", set_color); - ttk_position((ttk_object *)button_blue, decor_left_width + 3, decor_top_height + 3, 100, 20); - button_blue->fill_color = rgb(0,0,255); - button_blue->fore_color = rgb(255,255,255); - - button_green = ttk_button_new("Green", set_color); - ttk_position((ttk_object *)button_green, decor_left_width + 106, decor_top_height + 3, 100, 20); - button_green->fill_color = rgb(0,255,0); - button_green->fore_color = rgb(0,0,0); - - button_red = ttk_button_new("Red", set_color); - ttk_position((ttk_object *)button_red, decor_left_width + 209, decor_top_height + 3, 100, 20); - button_red->fill_color = rgb(255,0,0); - button_red->fore_color = rgb(255,255,255); - - button_thick = ttk_button_new("Thick", set_thickness_thick); - ttk_position((ttk_object *)button_thick, decor_left_width + 312, decor_top_height + 3, 50, 20); - button_thick->fill_color = rgb(40,40,40); - button_thick->fore_color = rgb(255,255,255); - - button_thin = ttk_button_new("Thin", set_thickness_thin); - ttk_position((ttk_object *)button_thin, decor_left_width + 362, decor_top_height + 3, 50, 20); - button_thin->fill_color = rgb(127,127,127); - button_thin->fore_color = rgb(255,255,255); - - ttk_button * button_resize = ttk_button_new("*", resize_button); - ttk_position((ttk_object *)button_resize, decor_left_width + 410, decor_top_height + 3, 20, 20); - button_resize->fill_color = rgb(127,127,127); - button_resize->fore_color = rgb(255,255,255); - - drawing_surface = ttk_raw_surface_new(width - 30, height - 70); - ((ttk_object *)drawing_surface)->y += 19; - - int stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, drawing_surface->surface->width); - internal_surface = cairo_image_surface_create_for_data(drawing_surface->surface->backbuffer, CAIRO_FORMAT_ARGB32, drawing_surface->surface->width, drawing_surface->surface->height, stride); - - drawing_color = rgb(255,0,0); - - ttk_render(); - - while (!quit) { - yutani_msg_t * m = yutani_poll(yctx); - if (m) { - switch (m->type) { - case YUTANI_MSG_KEY_EVENT: - { - struct yutani_msg_key_event * ke = (void*)m->data; - if (ke->event.action == KEY_ACTION_DOWN && ke->event.keycode == 'q') { - quit = 1; - break; - } - } - break; - case YUTANI_MSG_WINDOW_FOCUS_CHANGE: - { - struct yutani_msg_window_focus_change * wf = (void*)m->data; - yutani_window_t * win = hashmap_get(yctx->windows, (void*)wf->wid); - if (win) { - win->focused = wf->focused; - ttk_render(); - } - } - break; - case YUTANI_MSG_WINDOW_MOUSE_EVENT: - { - struct yutani_msg_window_mouse_event * me = (void*)m->data; - if (decor_handle_event(yctx, m) == DECOR_CLOSE) { - quit = 1; - break; - } - if (me->command == YUTANI_MOUSE_EVENT_DRAG && me->buttons & YUTANI_MOUSE_BUTTON_LEFT) { - keep_drawing(me); - yutani_flip(yctx, wina); - } else if (me->command == YUTANI_MOUSE_EVENT_RAISE) { - ttk_render(); - } else { - ttk_check_click(me); - } - } - break; - case YUTANI_MSG_RESIZE_OFFER: - { - struct yutani_msg_window_resize * wr = (void*)m->data; - resize_finish(wr->width, wr->height); - } - break; - case YUTANI_MSG_SESSION_END: - quit = 1; - break; - default: - break; - } - free(m); - } - } - - yutani_close(yctx, wina); - - return 0; -} diff --git a/userspace/gui/ttk/lib/ttk.c b/userspace/gui/ttk/lib/ttk.c deleted file mode 100644 index df028baa..00000000 --- a/userspace/gui/ttk/lib/ttk.c +++ /dev/null @@ -1,471 +0,0 @@ -/* This file is part of ToaruOS and is released under the terms - * of the NCSA / University of Illinois License - see LICENSE.md - * Copyright (C) 2013-2014 Kevin Lange - */ -/* - * - * Toolkit Demo and Development Application - * - */ -#include -#include -#include - -#include - -#include "lib/decorations.h" - -#include "../ttk.h" - -/* TTK {{{ */ - -static yutani_t * yctx; -/* XXX ttk_app_t ? */ -static hashmap_t * ttk_wids_to_windows; - -void cairo_rounded_rectangle(cairo_t * cr, double x, double y, double width, double height, double radius) { - double degrees = M_PI / 180.0; - - cairo_new_sub_path(cr); - cairo_arc (cr, x + width - radius, y + radius, radius, -90 * degrees, 0 * degrees); - cairo_arc (cr, x + width - radius, y + height - radius, radius, 0 * degrees, 90 * degrees); - cairo_arc (cr, x + radius, y + height - radius, radius, 90 * degrees, 180 * degrees); - cairo_arc (cr, x + radius, y + radius, radius, 180 * degrees, 270 * degrees); - cairo_close_path(cr); -} - - -void ttk_redraw_borders(ttk_window_t * window) { - render_decorations(window->core_window, window->core_context, window->title); -} - -void _ttk_draw_button(cairo_t * cr, int x, int y, int width, int height, char * title) { - cairo_save(cr); - - cairo_set_line_cap(cr, CAIRO_LINE_CAP_ROUND); - cairo_set_line_join(cr, CAIRO_LINE_JOIN_ROUND); - - cairo_rounded_rectangle(cr, 2 + x, 2 + y, width - 4, height - 4, 2.0); - cairo_set_source_rgba(cr, 44.0/255.0, 71.0/255.0, 91.0/255.0, 29.0/255.0); - cairo_set_line_width(cr, 4); - cairo_stroke(cr); - - cairo_rounded_rectangle(cr, 2 + x, 2 + y, width - 4, height - 4, 2.0); - cairo_set_source_rgba(cr, 158.0/255.0, 169.0/255.0, 177.0/255.0, 1.0); - cairo_set_line_width(cr, 2); - cairo_stroke(cr); - - { - cairo_pattern_t * pat = cairo_pattern_create_linear(2 + x, 2 + y, 2 + x, 2 + y + height - 4); - cairo_pattern_add_color_stop_rgba(pat, 0, 1, 1, 1, 1); - cairo_pattern_add_color_stop_rgba(pat, 1, 241.0/255.0, 241.0/255.0, 244.0/255.0, 1); - cairo_rounded_rectangle(cr, 2 + x, 2 + y, width - 4, height - 4, 2.0); - cairo_set_source(cr, pat); - cairo_fill(cr); - cairo_pattern_destroy(pat); - } - - { - cairo_pattern_t * pat = cairo_pattern_create_linear(3 + x, 3 + y, 3 + x, 3 + y + height - 4); - cairo_pattern_add_color_stop_rgba(pat, 0, 252.0/255.0, 252.0/255.0, 254.0/255.0, 1); - cairo_pattern_add_color_stop_rgba(pat, 1, 223.0/255.0, 225.0/255.0, 230.0/255.0, 1); - cairo_rounded_rectangle(cr, 3 + x, 3 + y, width - 5, height - 5, 2.0); - cairo_set_source(cr, pat); - cairo_fill(cr); - cairo_pattern_destroy(pat); - } - - { - cairo_surface_t * surface = cairo_get_target(cr); - gfx_context_t fake_context = { - .width = cairo_image_surface_get_width(surface), - .height = cairo_image_surface_get_height(surface), - .depth = 32, - .buffer = NULL, - .backbuffer = cairo_image_surface_get_data(surface) - }; - - set_font_face(FONT_SANS_SERIF); - set_font_size(13); - - int str_width = draw_string_width(title); - draw_string(&fake_context, (width - str_width) / 2 + x, y + (height) / 2 + 4, rgb(49,49,49), title); - } - - cairo_restore(cr); -} - -void _ttk_draw_button_hover(cairo_t * cr, int x, int y, int width, int height, char * title) { - cairo_save(cr); - - cairo_set_line_cap(cr, CAIRO_LINE_CAP_ROUND); - cairo_set_line_join(cr, CAIRO_LINE_JOIN_ROUND); - - cairo_rounded_rectangle(cr, 2 + x, 2 + y, width - 4, height - 4, 2.0); - cairo_set_source_rgba(cr, 44.0/255.0, 71.0/255.0, 91.0/255.0, 29.0/255.0); - cairo_set_line_width(cr, 4); - cairo_stroke(cr); - - cairo_rounded_rectangle(cr, 2 + x, 2 + y, width - 4, height - 4, 2.0); - cairo_set_source_rgba(cr, 158.0/255.0, 169.0/255.0, 177.0/255.0, 1.0); - cairo_set_line_width(cr, 2); - cairo_stroke(cr); - - { - cairo_pattern_t * pat = cairo_pattern_create_linear(2 + x, 2 + y, 2 + x, 2 + y + height - 4); - cairo_pattern_add_color_stop_rgba(pat, 0, 1, 1, 1, 1); - cairo_pattern_add_color_stop_rgba(pat, 1, 229.0/255.0, 229.0/255.0, 246.0/255.0, 1); - cairo_rounded_rectangle(cr, 2 + x, 2 + y, width - 4, height - 4, 2.0); - cairo_set_source(cr, pat); - cairo_fill(cr); - cairo_pattern_destroy(pat); - } - - { - cairo_pattern_t * pat = cairo_pattern_create_linear(3 + x, 3 + y, 3 + x, 3 + y + height - 4); - cairo_pattern_add_color_stop_rgba(pat, 0, 252.0/255.0, 252.0/255.0, 254.0/255.0, 1); - cairo_pattern_add_color_stop_rgba(pat, 1, 212.0/255.0, 223.0/255.0, 251.0/255.0, 1); - cairo_rounded_rectangle(cr, 3 + x, 3 + y, width - 5, height - 5, 2.0); - cairo_set_source(cr, pat); - cairo_fill(cr); - cairo_pattern_destroy(pat); - } - - { - cairo_surface_t * surface = cairo_get_target(cr); - gfx_context_t fake_context = { - .width = cairo_image_surface_get_width(surface), - .height = cairo_image_surface_get_height(surface), - .depth = 32, - .buffer = NULL, - .backbuffer = cairo_image_surface_get_data(surface) - }; - - set_font_face(FONT_SANS_SERIF); - set_font_size(13); - - int str_width = draw_string_width(title); - draw_string(&fake_context, (width - str_width) / 2 + x, y + (height) / 2 + 4, rgb(49,49,49), title); - } - - cairo_restore(cr); - -} - -void _ttk_draw_button_select(cairo_t * cr, int x, int y, int width, int height, char * title) { - cairo_save(cr); - - cairo_set_line_cap(cr, CAIRO_LINE_CAP_ROUND); - cairo_set_line_join(cr, CAIRO_LINE_JOIN_ROUND); - - cairo_rounded_rectangle(cr, 2 + x, 2 + y, width - 4, height - 4, 2.0); - cairo_set_source_rgba(cr, 134.0/255.0, 173.0/255.0, 201.0/255.0, 1.0); - cairo_set_line_width(cr, 2); - cairo_stroke(cr); - - cairo_rounded_rectangle(cr, 2 + x, 2 + y, width - 4, height - 4, 2.0); - cairo_set_source_rgba(cr, 202.0/255.0, 211.0/255.0, 232.0/255.0, 1.0); - cairo_fill(cr); - - { - cairo_surface_t * surface = cairo_get_target(cr); - gfx_context_t fake_context = { - .width = cairo_image_surface_get_width(surface), - .height = cairo_image_surface_get_height(surface), - .depth = 32, - .buffer = NULL, - .backbuffer = cairo_image_surface_get_data(surface) - }; - - set_font_face(FONT_SANS_SERIF); - set_font_size(13); - - int str_width = draw_string_width(title); - draw_string(&fake_context, (width - str_width) / 2 + x, y + (height) / 2 + 4, rgb(49,49,49), title); - } - - cairo_restore(cr); - -} - -void _ttk_draw_button_disabled(cairo_t * cr, int x, int y, int width, int height, char * title) { - cairo_save(cr); - - cairo_set_line_cap(cr, CAIRO_LINE_CAP_ROUND); - cairo_set_line_join(cr, CAIRO_LINE_JOIN_ROUND); - - cairo_rounded_rectangle(cr, 2 + x, 2 + y, width - 4, height - 4, 2.0); - cairo_set_source_rgba(cr, 44.0/255.0, 71.0/255.0, 91.0/255.0, 29.0/255.0); - cairo_set_line_width(cr, 4); - cairo_stroke(cr); - - cairo_rounded_rectangle(cr, 2 + x, 2 + y, width - 4, height - 4, 2.0); - cairo_set_source_rgba(cr, 152.0/255.0, 152.0/255.0, 152.0/255.0, 1.0); - cairo_set_line_width(cr, 2); - cairo_stroke(cr); - - { - cairo_pattern_t * pat = cairo_pattern_create_linear(2 + x, 2 + y, 2 + x, 2 + y + height - 4); - cairo_pattern_add_color_stop_rgba(pat, 0, 229.0/255.0, 229.0/255.0, 229.0/255.0, 1); - cairo_pattern_add_color_stop_rgba(pat, 1, 178.0/255.0, 178.0/255.0, 178.0/255.0, 1); - cairo_rounded_rectangle(cr, 2 + x, 2 + y, width - 4, height - 4, 2.0); - cairo_set_source(cr, pat); - cairo_fill(cr); - cairo_pattern_destroy(pat); - } - - { - cairo_pattern_t * pat = cairo_pattern_create_linear(3 + x, 3 + y, 3 + x, 3 + y + height - 4); - cairo_pattern_add_color_stop_rgba(pat, 0, 210.0/255.0, 210.0/255.0, 210.0/255.0, 1); - cairo_pattern_add_color_stop_rgba(pat, 1, 165.0/255.0, 166.0/255.0, 170.0/255.0, 1); - cairo_rounded_rectangle(cr, 3 + x, 3 + y, width - 5, height - 5, 2.0); - cairo_set_source(cr, pat); - cairo_fill(cr); - cairo_pattern_destroy(pat); - } - - { - cairo_surface_t * surface = cairo_get_target(cr); - gfx_context_t fake_context = { - .width = cairo_image_surface_get_width(surface), - .height = cairo_image_surface_get_height(surface), - .depth = 32, - .buffer = NULL, - .backbuffer = cairo_image_surface_get_data(surface) - }; - - set_font_face(FONT_SANS_SERIF); - set_font_size(13); - - int str_width = draw_string_width(title); - draw_string(&fake_context, (width - str_width) / 2 + x, y + (height) / 2 + 4, rgb(100,100,100), title); - } - - cairo_restore(cr); -} - -#define TTK_MENU_HEIGHT 24 - -void _ttk_draw_menu(cairo_t * cr, int x, int y, int width) { - cairo_save(cr); - - int height = TTK_MENU_HEIGHT; - cairo_set_source_rgba(cr, 59.0/255.0, 59.0/255.0, 59.0/255.0, 1); - cairo_rectangle(cr, x, y, width, height); - cairo_fill(cr); - - { - cairo_surface_t * surface = cairo_get_target(cr); - gfx_context_t fake_context = { - .width = cairo_image_surface_get_width(surface), - .height = cairo_image_surface_get_height(surface), - .depth = 32, - .buffer = NULL, - .backbuffer = cairo_image_surface_get_data(surface) - }; - - set_font_face(FONT_SANS_SERIF); - set_font_size(13); - - draw_string(&fake_context, x + 8, y + height - 6, rgb(248,248,248), "File"); - } - - - cairo_restore(cr); -} - -void ttk_window_draw(ttk_window_t * window) { - draw_fill(window->core_context, rgb(TTK_BACKGROUND_DEFAULT)); - ttk_redraw_borders(window); - - /* TODO actual drawing */ - { - /* TODO move these surfaces into the ttk_window_t object */ - int stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, window->core_window->width); - cairo_surface_t * core_surface = cairo_image_surface_create_for_data(window->core_context->backbuffer, CAIRO_FORMAT_ARGB32, window->core_window->width, window->core_window->height, stride); - cairo_t * cr_main = cairo_create(core_surface); - - /* TODO move this surface to a ttk_frame_t or something; GUIs man, go look at some Qt or GTK APIs! */ - cairo_surface_t * internal_surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, window->width, window->height); - cairo_t * cr = cairo_create(internal_surface); - - _ttk_draw_menu(cr, 0, 0, window->width); - - _ttk_draw_button(cr, 4, TTK_MENU_HEIGHT + 4, window->width - 8, 40, "Regular Button"); - - _ttk_draw_button(cr, 4, TTK_MENU_HEIGHT + 48 + 4, (window->width / 2) - 8, 40, "Regular Button"); - _ttk_draw_button_hover(cr, 4 + (window->width / 2), TTK_MENU_HEIGHT + 48 + 4, (window->width / 2) - 8, 40, "Hover Button"); - - _ttk_draw_button_select(cr, 4, TTK_MENU_HEIGHT + 2 * 48 + 4, (window->width / 2) - 8, 40, "Selected"); - _ttk_draw_button_disabled(cr, 4 + (window->width / 2), TTK_MENU_HEIGHT + 2 * 48 + 4, (window->width / 2) - 8, 40, "Disabled Button"); - - _ttk_draw_button(cr, 4, TTK_MENU_HEIGHT + 3 * 48 + 4, window->width - 8, window->height - (3 * 48) - TTK_MENU_HEIGHT - 8, "Regular Button"); - - /* Paint the window's internal surface onto the backbuffer */ - cairo_set_source_surface(cr_main, internal_surface, (double)window->off_x, (double)window->off_y); - cairo_paint(cr_main); - cairo_surface_flush(internal_surface); - cairo_destroy(cr); - cairo_surface_destroy(internal_surface); - - /* In theory, we don't actually want to destroy much of any of this; maybe the cairo_t */ - cairo_surface_flush(core_surface); - cairo_destroy(cr_main); - cairo_surface_destroy(core_surface); - } - - flip(window->core_context); - yutani_flip(yctx, window->core_window); -} - -void ttk_move_callback(ttk_window_t * window_ttk, int x, int y) { - if (!window_ttk) { - return; - } - - /* XXX Do we want to do things with the old position? */ - - window_ttk->x = x; - window_ttk->y = y; - -} - -void ttk_resize_callback(ttk_window_t * window_ttk, int width, int height) { - if (!window_ttk) { - fprintf(stderr, "[ttk] received window callback for a window not registered with TTK, ignoring.\n"); - return; - } - - yutani_window_resize_accept(yctx, window_ttk->core_window, width, height); - - /* Update window size */ - window_ttk->width = width - decor_width(); - window_ttk->height = height - decor_height(); - - /* Reinitialize graphics context */ - reinit_graphics_yutani(window_ttk->core_context, window_ttk->core_window); - - ttk_window_draw(window_ttk); - - yutani_window_resize_done(yctx, window_ttk->core_window); - yutani_flip(yctx, window_ttk->core_window); -} - -void ttk_focus_callback(ttk_window_t * window_ttk, int focused) { - if (!window_ttk) { - fprintf(stderr, "[ttk] received window callback for a window not registered with TTK, ignoring.\n"); - return; - } - - window_ttk->core_window->focused = focused; - ttk_window_draw(window_ttk); -} - - -void ttk_initialize() { - /* Connect to the windowing server */ - yctx = yutani_init(); - - /* Initialize the decoration library */ - init_decorations(); - - ttk_wids_to_windows = hashmap_create_int(5); /* wids are ints, ergo... */ -} - -ttk_window_t * ttk_window_new(char * title, uint16_t width, uint16_t height) { - ttk_window_t * new_win = malloc(sizeof(ttk_window_t)); - new_win->title = strdup(title); - new_win->width = width; - new_win->height = height; - new_win->off_x = decor_left_width; - new_win->off_y = decor_top_height; - - new_win->core_window = yutani_window_create(yctx, new_win->width + decor_width(), new_win->height + decor_height()); - yutani_window_move(yctx, new_win->core_window, TTK_DEFAULT_X, TTK_DEFAULT_Y); - assert(new_win->core_window && "Oh dear, I've failed to allocate a new window from the server. This is terrible."); - - /* XXX icon; also need to do this if we change the title later */ - yutani_window_advertise(yctx, new_win->core_window, new_win->title); - - new_win->core_context = init_graphics_yutani_double_buffer(new_win->core_window); - draw_fill(new_win->core_context, rgb(TTK_BACKGROUND_DEFAULT)); - - ttk_window_draw(new_win); - - hashmap_set(ttk_wids_to_windows, (void*)new_win->core_window->wid, new_win); -} - -void ttk_quit() { - list_t * windows = hashmap_values(ttk_wids_to_windows); - - foreach(node, windows) { - ttk_window_t * win = node->value; - yutani_close(yctx, win->core_window); - } - - list_free(windows); - free(windows); - hashmap_free(ttk_wids_to_windows); - free(ttk_wids_to_windows); -} - -int ttk_run(ttk_window_t * window) { - while (1) { - yutani_msg_t * m = yutani_poll(yctx); - if (m) { - switch (m->type) { - case YUTANI_MSG_KEY_EVENT: - { - struct yutani_msg_key_event * ke = (void*)m->data; - if (ke->event.action == KEY_ACTION_DOWN && ke->event.keycode == 'q') { - goto done; - } - } - break; - case YUTANI_MSG_WINDOW_FOCUS_CHANGE: - { - struct yutani_msg_window_focus_change * fc = (void*)m->data; - ttk_focus_callback(hashmap_get(ttk_wids_to_windows, (void*)fc->wid), fc->focused); - } - break; - case YUTANI_MSG_RESIZE_OFFER: - { - struct yutani_msg_window_resize * wr = (void*)m->data; - ttk_resize_callback(hashmap_get(ttk_wids_to_windows, (void*)wr->wid), wr->width, wr->height); - } - break; - case YUTANI_MSG_WINDOW_MOVE: - { - struct yutani_msg_window_move * wm = (void*)m->data; - ttk_move_callback(hashmap_get(ttk_wids_to_windows, (void*)wm->wid), wm->x, wm->y); - } - break; - case YUTANI_MSG_WINDOW_MOUSE_EVENT: - { - struct yutani_msg_window_mouse_event * me = (void*)m->data; - if (decor_handle_event(yctx, m) == DECOR_CLOSE) { - goto done; - } - if (me->command == YUTANI_MOUSE_EVENT_DOWN && me->buttons & YUTANI_MOUSE_BUTTON_LEFT) { - ttk_window_t * win = hashmap_get(ttk_wids_to_windows, (void*)me->wid); - if (win) { - /* Do something */ - } - } - } - break; - case YUTANI_MSG_SESSION_END: - goto done; - default: - break; - } - free(m); - } - } - -done: - ttk_quit(); - return 0; -} - -/* }}} end TTK */ diff --git a/userspace/gui/ttk/ttk-demo.c b/userspace/gui/ttk/ttk-demo.c deleted file mode 100644 index f891beae..00000000 --- a/userspace/gui/ttk/ttk-demo.c +++ /dev/null @@ -1,21 +0,0 @@ -/* This file is part of ToaruOS and is released under the terms - * of the NCSA / University of Illinois License - see LICENSE.md - * Copyright (C) 2013 Kevin Lange - */ -/* - * - * Toolkit Demo and Development Application - * - */ -#include -#include - -#include "gui/ttk/ttk.h" - -int main (int argc, char ** argv) { - - ttk_initialize(); - ttk_window_t * main_window = ttk_window_new("TTK Demo", 500, 500); - - return ttk_run(main_window); -} diff --git a/userspace/gui/ttk/ttk.h b/userspace/gui/ttk/ttk.h deleted file mode 100644 index a3aaeec3..00000000 --- a/userspace/gui/ttk/ttk.h +++ /dev/null @@ -1,50 +0,0 @@ -#ifndef _TTK_H -#define _TTK_H - -#include -#include -#include -#include -#include -#include FT_FREETYPE_H -#include FT_CACHE_H - -#include "lib/yutani.h" -#include "lib/graphics.h" -#include "lib/decorations.h" -#include "lib/shmemfonts.h" -#include "lib/hashmap.h" - -typedef struct ttk_window { - yutani_window_t * core_window; - gfx_context_t * core_context; - char * title; - cairo_surface_t * cairo_surface; - uint16_t width; /* internal space */ - uint16_t height; - uint16_t off_x; /* decor_left_width */ - uint16_t off_y; /* decor_top_height */ - - int32_t x; - int32_t y; -} ttk_window_t; - -#define TTK_BACKGROUND_DEFAULT 204,204,204 -#define TTK_DEFAULT_X 300 -#define TTK_DEFAULT_Y 300 - -void cairo_rounded_rectangle(cairo_t * cr, double x, double y, double width, double height, double radius); -void ttk_redraw_borders(ttk_window_t * window); -void _ttk_draw_button(cairo_t * cr, int x, int y, int width, int height, char * title); -void _ttk_draw_button_hover(cairo_t * cr, int x, int y, int width, int height, char * title); -void _ttk_draw_button_select(cairo_t * cr, int x, int y, int width, int height, char * title); -void _ttk_draw_button_disabled(cairo_t * cr, int x, int y, int width, int height, char * title); -void _ttk_draw_menu(cairo_t * cr, int x, int y, int width); -void ttk_window_draw(ttk_window_t * window); -void ttk_initialize(); -ttk_window_t * ttk_window_new(char * title, uint16_t width, uint16_t height); -void ttk_quit(); -int ttk_run(ttk_window_t * window); - - -#endif diff --git a/userspace/py/bin/painting.py b/userspace/py/bin/painting.py new file mode 100755 index 00000000..e62735e0 --- /dev/null +++ b/userspace/py/bin/painting.py @@ -0,0 +1,260 @@ +#!/usr/bin/python3 +""" +Painting +""" +import os +import math +import stat +import sys +import subprocess + +import cairo + +import yutani +import text_region +import toaru_fonts +import toaru_package + +from color_picker import ColorPickerWindow + +from menu_bar import MenuBarWidget, MenuEntryAction, MenuEntrySubmenu, MenuEntryDivider, MenuWindow +from icon_cache import get_icon + +version = "0.1.0" +_description = f"ToaruPaint {version}\n© 2017 Kevin Lange\n\nDraw stuff, maybe.\n\nhttp://github.com/klange/toaruos" + +class PaintingWindow(yutani.Window): + + base_width = 600 + base_height = 600 + + def __init__(self, decorator): + super(PaintingWindow, self).__init__(self.base_width + decorator.width(), self.base_height + decorator.height(), title="ToaruPaint", icon="applications-painting", doublebuffer=True) + self.move(100,100) + self.x = 100 + self.y = 100 + self.decorator = decorator + self.picker = None + self.last_color = (0,0,0) + + def exit_app(action): + menus = [x for x in self.menus.values()] + for x in menus: + x.definitely_close() + self.close() + sys.exit(0) + + def about_window(action): + subprocess.Popen(["about-applet.py","About ToaruPaint","applications-painting","/usr/share/icons/48/applications-painting.png",_description]) + + def help_browser(action): + subprocess.Popen(["help-browser.py","painting.trt"]) + + def close_picker(): + self.last_color = self.picker.color + self.picker = None + + def select_color(action): + if self.picker: + return + else: + self.picker = ColorPickerWindow(self.decorator, close_picker) + self.picker.draw() + + menus = [ + ("File", [ + MenuEntryAction("Exit","exit",exit_app,None), + ]), + ("Tools", [ + MenuEntryAction("Color",None,select_color,None), + ]), + ("Help", [ + MenuEntryAction("Contents","help",help_browser,None), + MenuEntryDivider(), + MenuEntryAction("About ToaruPaint","star",about_window,None), + ]), + ] + + self.menubar = MenuBarWidget(self,menus) + + self.menus = {} + self.hovered_menu = None + + self.buf = yutani.GraphicsBuffer(500,500) + self.surface = self.buf.get_cairo_surface() + self.draw_ctx = cairo.Context(self.surface) + self.draw_ctx.rectangle(0,0,self.surface.get_width(),self.surface.get_height()) + self.draw_ctx.set_source_rgb(1,1,1) + self.draw_ctx.fill() + + self.hilighted = None + self.was_drawing = False + self.line_width = 1.0 + self.curs_x = None + self.curs_y = None + + def color(self): + if self.picker: + return self.picker.color + else: + return self.last_color + + def draw(self): + surface = self.get_cairo_surface() + + WIDTH, HEIGHT = self.width - self.decorator.width(), self.height - self.decorator.height() + + ctx = cairo.Context(surface) + ctx.translate(self.decorator.left_width(), self.decorator.top_height()) + ctx.rectangle(0,0,WIDTH,HEIGHT) + ctx.set_source_rgb(0.5,0.5,0.5) + ctx.fill() + + ctx.save() + ctx.translate(0,self.menubar.height) + ctx.set_source_surface(self.surface,0,0) + ctx.paint() + + if not self.curs_x is None: + ctx.arc(self.curs_x,self.curs_y,self.line_width/2,0,2*math.pi) + ctx.set_line_width(0.5) + ctx.set_source_rgba(0,0,0,0.7) + ctx.stroke() + ctx.arc(self.curs_x,self.curs_y,self.line_width/2-0.5,0,2*math.pi) + ctx.set_line_width(0.5) + ctx.set_source_rgba(1,1,1,0.7) + ctx.stroke() + + ctx.restore() + + self.menubar.draw(ctx,0,0,WIDTH) + + self.decorator.render(self) + self.flip() + + def finish_resize(self, msg): + """Accept a resize.""" + if msg.width < 120 or msg.height < 120: + self.resize_offer(max(msg.width,120),max(msg.height,120)) + return + self.resize_accept(msg.width, msg.height) + self.reinit() + self.draw() + self.resize_done() + self.flip() + + def mouse_event(self, msg): + if d.handle_event(msg) == yutani.Decor.EVENT_CLOSE: + window.close() + sys.exit(0) + x,y = msg.new_x - self.decorator.left_width(), msg.new_y - self.decorator.top_height() + w,h = self.width - self.decorator.width(), self.height - self.decorator.height() + + if not self.was_drawing: + self.curs_x = None + self.curs_y = None + if x >= 0 and x < w and y >= 0 and y < self.menubar.height: + self.menubar.mouse_event(msg, x, y) + return + + if x < 0 or x >= w or y < 0 or y >= h: + return + + if x >= 0 and x < w and y >= self.menubar.height and y < h: + if msg.buttons & yutani.MouseButton.BUTTON_RIGHT: + if not self.menus: + menu_entries = [ + MenuEntryAction("Up",None,self.go_up,None), + ] + menu = MenuWindow(menu_entries,(self.x+msg.new_x,self.y+msg.new_y),root=self) + return + + if y < 0: return + + if msg.buttons & yutani.MouseButton.SCROLL_UP: + self.line_width *= 1.2 + elif msg.buttons & yutani.MouseButton.SCROLL_DOWN: + self.line_width /= 1.2 + + redraw = False + + if not (msg.buttons & yutani.MouseButton.BUTTON_LEFT): + self.was_drawing = False + + if msg.command == yutani.MouseEvent.DRAG and msg.buttons & yutani.MouseButton.BUTTON_LEFT: + self.was_drawing = True + self.draw_ctx.set_line_cap(cairo.LINE_CAP_ROUND) + self.draw_ctx.set_line_join(cairo.LINE_JOIN_ROUND) + self.draw_ctx.set_source_rgb(*self.color()) + self.draw_ctx.set_line_width(self.line_width) + self.draw_ctx.move_to(0.5+msg.old_x - self.decorator.left_width(), 0.5+msg.old_y - self.decorator.top_height() - self.menubar.height); + self.draw_ctx.line_to(0.5+msg.new_x - self.decorator.left_width(), 0.5+msg.new_y - self.decorator.top_height() - self.menubar.height); + self.draw_ctx.stroke() + + + self.curs_x = 0.5+msg.new_x - self.decorator.left_width() + self.curs_y = 0.5+msg.new_y - self.decorator.top_height() - self.menubar.height + self.draw() + + def keyboard_event(self, msg): + if msg.event.action != yutani.KeyAction.ACTION_DOWN: + return # Ignore anything that isn't a key down. + if msg.event.key == b"q": + self.close() + sys.exit(0) + +if __name__ == '__main__': + yutani.Yutani() + d = yutani.Decor() + + window = PaintingWindow(d) + window.draw() + + while 1: + # Poll for events. + msg = yutani.yutani_ctx.poll() + if msg.type == yutani.Message.MSG_SESSION_END: + window.close() + break + elif msg.type == yutani.Message.MSG_KEY_EVENT: + if msg.wid == window.wid: + window.keyboard_event(msg) + elif msg.wid in window.menus: + window.menus[msg.wid].keyboard_event(msg) + elif msg.type == yutani.Message.MSG_WINDOW_FOCUS_CHANGE: + if msg.wid == window.wid: + if msg.focused == 0 and window.menus: + window.focused = 1 + else: + window.focused = msg.focused + window.draw() + elif msg.wid in window.menus and msg.focused == 0: + window.menus[msg.wid].leave_menu() + if not window.menus and window.focused: + window.focused = 0 + window.draw() + elif window.picker and msg.wid == window.picker.wid: + window.picker.focused = msg.focused + window.picker.draw() + elif msg.type == yutani.Message.MSG_RESIZE_OFFER: + if msg.wid == window.wid: + window.finish_resize(msg) + elif window.picker and msg.wid == window.picker.wid: + window.picker.resize_finish(msg) + elif msg.type == yutani.Message.MSG_WINDOW_MOVE: + if msg.wid == window.wid: + window.x = msg.x + window.y = msg.y + elif msg.type == yutani.Message.MSG_WINDOW_MOUSE_EVENT: + if msg.wid == window.wid: + window.mouse_event(msg) + elif msg.wid in window.menus: + m = window.menus[msg.wid] + if msg.new_x >= 0 and msg.new_x < m.width and msg.new_y >= 0 and msg.new_y < m.height: + window.hovered_menu = m + elif window.hovered_menu == m: + window.hovered_menu = None + m.mouse_action(msg) + elif window.picker and msg.wid == window.picker.wid: + window.picker.mouse_event(msg) + diff --git a/userspace/py/bin/panel.py b/userspace/py/bin/panel.py index d3131d35..d88e3dc0 100755 --- a/userspace/py/bin/panel.py +++ b/userspace/py/bin/panel.py @@ -339,7 +339,7 @@ class ApplicationsMenuWidget(BaseWidget): ] games.extend(self.extra('games')) graphics = [ - MenuEntryAction("Draw!","applications-painting",launch_app,"draw"), + MenuEntryAction("ToaruPaint","applications-painting",launch_app,"painting.py"), ] graphics.extend(self.extra('graphics')) settings = [ diff --git a/userspace/py/lib/color_picker.py b/userspace/py/lib/color_picker.py new file mode 100755 index 00000000..f2eafba3 --- /dev/null +++ b/userspace/py/lib/color_picker.py @@ -0,0 +1,240 @@ +#!/usr/bin/python3 +""" +Color picker +""" +import colorsys +import math +import sys + +import cairo + +import yutani + +def s(p1,p2,p3): + return (p1[0] - p3[0]) * (p2[1] - p3[1]) - (p2[0] - p3[0]) * (p1[1] - p3[1]) + +def dist(a,b): + return math.sqrt((a[0]-b[0])**2+(a[1]-b[1])**2) + +def closest_point(pt, v1, v2): + ap = (pt[0] - v1[0]),(pt[1] - v1[1]) + ab = (v2[0] - v1[0]),(v2[1] - v1[1]) + ab2 = ab[0]**2 + ab[1]**2 + ap_ab = ap[0]*ab[0] + ap[1]*ab[1] + t = ap_ab / ab2 + return (v1[0] + ab[0] * t, v1[1] + ab[1] * t) + +def point_inside(pt, v1, v2, v3): + a = s(pt,v1,v2) + b = s(pt,v2,v3) + c = s(pt,v3,v1) + return (a < 0) == (b < 0) == (c < 0) + +class ColorPickerWindow(yutani.Window): + + base_width = 200 + + def __init__(self, decorator, close_callback): + super(ColorPickerWindow,self).__init__(self.base_width + decorator.width(), self.base_width + decorator.height(), title="Color Picker", doublebuffer=True) + self.move(100,100) + self.decorator = decorator + self.close_callback = close_callback + + self.hue = math.radians(240) + self.angle = -self.hue + self.pat = cairo.MeshGradient() + self.pat.begin_patch() + self.pat.move_to(-1.0,0.0) + self.pat.curve_to(-1.0,0.3886666666666667,-0.7746666666666667,0.742,-0.4226666666666667,0.906) + self.pat.curve_to(-0.12666666666666668,1.044,0.2173333333333333,1.0293333333333334,0.5,0.866) + self.pat.curve_to(0.8093333333333331,0.6873333333333332,1.0,0.3573333333333333,1.0,0.0) + self.pat.set_corner_color_rgb (0, 0, 1, 1) + self.pat.set_corner_color_rgb (1, 0, 0, 1) + self.pat.set_corner_color_rgb (2, 1, 0, 1) + self.pat.set_corner_color_rgb (3, 1, 0, 0) + self.pat.end_patch() + self.pat.begin_patch() + self.pat.move_to(-1.0,0.0) + self.pat.curve_to(-1.0,-0.3886666666666667,-0.7746666666666667,-0.742,-0.4226666666666667,-0.906) + self.pat.curve_to(-0.12666666666666668,-1.044,0.2173333333333333,-1.0293333333333334,0.5,-0.866) + self.pat.curve_to(0.8093333333333331,-0.6873333333333332,1.0,-0.3573333333333333,1.0,0.0) + self.pat.set_corner_color_rgb (0, 0, 1, 1) + self.pat.set_corner_color_rgb (1, 0, 1, 0) + self.pat.set_corner_color_rgb (2, 1, 1, 0) + self.pat.set_corner_color_rgb (3, 1, 0, 0) + self.pat.end_patch() + + self.dit_th = 0.0 + self.dit_r = 0.0 + self.color = (0,0,0) + + self.v1,self.v2,self.v3 = (0,0),(0,0),(0,0) + + self.down_in_circle = False + self.down_in_triangle = False + + def draw(self): + surface = self.get_cairo_surface() + + WIDTH, HEIGHT = self.width - self.decorator.width(), self.height - self.decorator.height() + + ctx = cairo.Context(surface) + ctx.translate(self.decorator.left_width(),self.decorator.top_height()) + ctx.rectangle(0,0,WIDTH,HEIGHT) + ctx.set_source_rgb(204/255,204/255,204/255) + ctx.fill() + ctx.scale (WIDTH/2.0, HEIGHT/2.0) + ctx.translate(1,1) + + # Draw the concentric circles for the hue selection. + ctx.arc(0.0,0.0,1.0,0,2*math.pi) + ctx.set_fill_rule(cairo.FILL_RULE_EVEN_ODD) + ctx.arc(0.0,0.0,0.8,0,2*math.pi) + ctx.set_source (self.pat) + ctx.fill() + + self.v1 = math.cos(self.angle+4*math.pi/3)*0.8,math.sin(self.angle+4*math.pi/3)*0.8 + self.v2 = math.cos(self.angle+6*math.pi/3)*0.8,math.sin(self.angle+6*math.pi/3)*0.8 + self.v3 = math.cos(self.angle+8*math.pi/3)*0.8,math.sin(self.angle+8*math.pi/3)*0.8 + + # Temporary pattern for inner triangle. + pat2 = cairo.MeshGradient() + pat2.begin_patch() + pat2.move_to(*self.v3) + pat2.line_to(*self.v1) + pat2.line_to(*self.v2) + pat2.line_to(*self.v3) + pat2.set_corner_color_rgb (0, 1, 1, 1) + pat2.set_corner_color_rgb (1, 0, 0, 0) + pat2.set_corner_color_rgb (2, *colorsys.hsv_to_rgb(self.hue/(2*math.pi),1.0,1.0)) + pat2.set_corner_color_rgb (3, 1, 1, 1) + pat2.end_patch() + + ctx.move_to(*self.v3) + ctx.line_to(*self.v1) + ctx.line_to(*self.v2) + ctx.line_to(*self.v3) + ctx.set_source (pat2) + ctx.fill() + + ctx.set_line_width(0.04) + ctx.move_to(*self.v2) + ctx.line_to(self.v2[0]/0.8,self.v2[1]/0.8) + ctx.set_source_rgb(0,0,0) + ctx.stroke() + + dit_x,dit_y = math.cos(math.radians(self.dit_th)+self.angle)*self.dit_r,math.sin(math.radians(self.dit_th)+self.angle)*self.dit_r + + ctx.set_line_width(0.02) + ctx.arc(dit_x,dit_y,0.05,0,2*math.pi) + ctx.set_source_rgb(0,0,0) + ctx.stroke() + + dit = dit_x,dit_y + sat_p = closest_point(dit,self.v1,self.v2) + exp_p = closest_point(dit,self.v1,self.v3) + m_exp = closest_point(self.v2,self.v1,self.v3) + m_sat = closest_point(self.v3,self.v1,self.v2) + white_amount = dist(sat_p,dit)/dist(m_sat,self.v3) + mix_amount = dist(exp_p,dit)/dist(m_exp,self.v2) + r,g,b = colorsys.hsv_to_rgb(self.hue/(2*math.pi),1.0,1.0) + _r = white_amount * 1.0 + mix_amount * r + _g = white_amount * 1.0 + mix_amount * g + _b = white_amount * 1.0 + mix_amount * b + self.color = (_r,_g,_b) + ctx.arc(-0.85,-0.85,0.1,0,2*math.pi) + ctx.set_source_rgb(_r,_g,_b) + ctx.fill() + + self.decorator.render(self) + self.flip() + + def finish_resize(self,msg): + """Accept a resize.""" + WIDTH, HEIGHT = msg.width - d.width(), msg.height - d.height() + if WIDTH != HEIGHT: + self.resize_offer(WIDTH+d.width(),WIDTH+d.height()) + return + self.resize_accept(msg.width, msg.height) + self.reinit() + self.draw() + self.resize_done() + self.flip() + + def mouse_event(self,msg): + if self.decorator.handle_event(msg) == yutani.Decor.EVENT_CLOSE: + # Close the window when the 'X' button is clicked. + self.close() + self.close_callback() + if msg.buttons & yutani.MouseButton.BUTTON_LEFT: + WIDTH, HEIGHT = self.width - self.decorator.width(), self.height - self.decorator.height() + x = msg.new_x - self.decorator.left_width() - WIDTH/2 + y = msg.new_y - self.decorator.top_height() - HEIGHT/2 + r = math.sqrt(x*x + y*y)/(WIDTH/2) + a = 0 + if x > 0 and y > 0: + a = math.degrees(math.atan(y/x)) + if y == 0 and x < 0: + a = 180 + if x == 0 and y > 0: + a = 90 + if x == 0 and y < 0: + a = 90*3 + if x < 0 and y > 0: + a = 180 + math.degrees(math.atan(y/x)) + if x < 0 and y < 0: + a = 180 + math.degrees(math.atan(y/x)) + if x > 0 and y < 0: + a = 360 + math.degrees(math.atan(y/x)) + if msg.command == yutani.MouseEvent.DOWN: + self.down_in_circle = False + self.down_in_triangle = False + if r < 0.8: + self.down_in_triangle = True + elif r <= 1.0: + self.down_in_circle = True + if self.down_in_triangle: + _x,_y = x/WIDTH*2, y/HEIGHT*2 + if point_inside((_x,_y),self.v1,self.v2,self.v3): + self.dit_th = a - math.degrees(self.angle) + self.dit_r = r + self.draw() + if self.down_in_circle: + self.hue = math.radians(360-a) + self.angle = -self.hue + self.draw() + if msg.command == yutani.MouseEvent.RAISE or msg.command == yutani.MouseEvent.CLICK: + self.down_in_circle = False + self.down_in_triangel = False + +if __name__ == '__main__': + # Connect to the server. + yutani.Yutani() + d = yutani.Decor() + + def on_close(): + sys.exit(0) + + w = ColorPickerWindow(d,on_close) + w.draw() + + while 1: + # Poll for events. + msg = yutani.yutani_ctx.poll() + if msg.type == yutani.Message.MSG_SESSION_END: + w.close() + sys.exit(0) + elif msg.type == yutani.Message.MSG_KEY_EVENT: + if msg.event.key == b'q': + w.close() + sys.exit(0) + elif msg.type == yutani.Message.MSG_WINDOW_FOCUS_CHANGE: + if msg.wid == w.wid: + w.focused = msg.focused + w.draw() + elif msg.type == yutani.Message.MSG_RESIZE_OFFER: + if msg.wid == w.wid: + w.resize_finish(msg) + elif msg.type == yutani.Message.MSG_WINDOW_MOUSE_EVENT: + if msg.wid == w.wid: + w.mouse_event(msg) diff --git a/util/auto-dep.py b/util/auto-dep.py index 446e6885..7daf8b24 100755 --- a/util/auto-dep.py +++ b/util/auto-dep.py @@ -49,7 +49,6 @@ class Classifier(object): # Yutani Libraries '"lib/yutani.h"': (None, '-ltoaru-yutani', ['"lib/kbd.h"', '"lib/list.h"', '"lib/pex.h"', '"lib/graphics.h"', '"lib/hashmap.h"']), '"lib/decorations.h"': (None, '-ltoaru-decorations', ['"lib/shmemfonts.h"', '"lib/graphics.h"', '"lib/yutani.h"','"lib/dlfcn.h"']), - '"gui/ttk/ttk.h"': (None, '-ltoaru-ttk', ['"lib/decorations.h"', '"lib/hashmap.h"', '', '']), '"gui/terminal/lib/termemu.h"': (None, '-ltoaru-termemu', ['"lib/graphics.h"']), }