Replace Draw, kill C TTK

This commit is contained in:
Kevin Lange 2017-01-15 23:30:32 +09:00
parent 2d35aebc03
commit dbe7a93865
8 changed files with 501 additions and 1027 deletions

View File

@ -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 <stdlib.h>
#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;
}

View File

@ -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 <stdlib.h>
#include <assert.h>
#include <math.h>
#include <cairo.h>
#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 */

View File

@ -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 <stdlib.h>
#include <assert.h>
#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);
}

View File

@ -1,50 +0,0 @@
#ifndef _TTK_H
#define _TTK_H
#include <stdlib.h>
#include <assert.h>
#include <math.h>
#include <cairo.h>
#include <ft2build.h>
#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

260
userspace/py/bin/painting.py Executable file
View File

@ -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"<b>ToaruPaint {version}</b>\n© 2017 Kevin Lange\n\nDraw stuff, maybe.\n\n<color 0x0000FF>http://github.com/klange/toaruos</color>"
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)

View File

@ -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 = [

240
userspace/py/lib/color_picker.py Executable file
View File

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

View File

@ -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"', '<cairo.h>', '<math.h>']),
'"gui/terminal/lib/termemu.h"': (None, '-ltoaru-termemu', ['"lib/graphics.h"']),
}