toaruos/lib/panel_windowlist.c

234 lines
6.4 KiB
C

/**
* @brief Panel window list widget
*/
#include <toaru/yutani.h>
#include <toaru/graphics.h>
#include <toaru/menu.h>
#include <toaru/text.h>
#include <toaru/panel.h>
#include <toaru/icon_cache.h>
#define GRADIENT_HEIGHT 24
#define GRADIENT_AT(y) premultiply(rgba(72, 167, 255, ((24-(y))*160)/24))
#define MAX_TEXT_WIDTH 180
#define MIN_TEXT_WIDTH 50
#define TOTAL_CELL_WIDTH (title_width)
static struct MenuList * window_menu;
static int title_width = 0;
static yutani_wid_t _window_menu_wid = 0;
static void _window_menu_start_move(struct MenuEntry * self) {
if (!_window_menu_wid)
return;
yutani_focus_window(yctx, _window_menu_wid);
yutani_window_drag_start_wid(yctx, _window_menu_wid);
}
static void _window_menu_start_maximize(struct MenuEntry * self) {
if (!_window_menu_wid)
return;
yutani_special_request_wid(yctx, _window_menu_wid, YUTANI_SPECIAL_REQUEST_MAXIMIZE);
yutani_focus_window(yctx, _window_menu_wid);
}
static void _window_menu_close(struct MenuEntry * self) {
if (!_window_menu_wid)
return;
yutani_focus_window(yctx, _window_menu_wid);
yutani_special_request_wid(yctx, _window_menu_wid, YUTANI_SPECIAL_REQUEST_PLEASE_CLOSE);
}
static void window_show_menu(yutani_wid_t wid, int x) {
if (window_menu->window) return;
_window_menu_wid = wid;
panel_menu_show_at(window_menu, x);
}
static int widget_draw_windowlist(struct PanelWidget * this, gfx_context_t * ctx) {
if (!window_list || window_list->length == 0) {
title_width = 0;
} else if (this->width <= 0) {
title_width = 28;
} else {
title_width = this->width / window_list->length;
if (title_width > MAX_TEXT_WIDTH) {
title_width = MAX_TEXT_WIDTH;
}
if (title_width < MIN_TEXT_WIDTH) {
title_width = 28;
}
}
int i = 0, j = 0;
if (window_list) {
foreach(node, window_list) {
struct window_ad * ad = node->value;
int w = title_width;
if (i + w > this->width) {
break;
}
/* Hilight the focused window */
if (ad->flags & 1) {
/* This is the focused window */
for (int y = 0; y < GRADIENT_HEIGHT; ++y) {
for (int x = i; x < i + w; ++x) {
GFX(ctx, x, y) = alpha_blend_rgba(GFX(ctx, x, y), GRADIENT_AT(y));
}
}
}
if (title_width >= MIN_TEXT_WIDTH) {
/* Ellipsifiy the title */
char * s = ellipsify(ad->name, 14, font, title_width - 4, NULL);
sprite_t * icon = icon_get_48(ad->icon);
gfx_context_t * subctx = init_graphics_subregion(ctx, i, 0, w, ctx->height);
draw_sprite_scaled_alpha(subctx, icon, w - 48 - 2, 0, 48, 48, (ad->flags & 1) ? 1.0 : 0.7);
tt_draw_string_shadow(subctx, font, s, 14, 2, TEXT_Y_OFFSET, (j == focused_app) ? HILIGHT_COLOR : (ad->flags & 1) ? FOCUS_COLOR : TEXT_COLOR, rgb(0,0,0), 4);
free(subctx);
free(s);
} else {
sprite_t * icon = icon_get_16(ad->icon);
gfx_context_t * subctx = init_graphics_subregion(ctx, i, 0, w, ctx->height);
draw_sprite_scaled(subctx, icon, 6, 6, 16, 16);
free(subctx);
}
ad->left = this->left + i;
j++;
i += w;
}
}
return 0;
}
static int widget_click_windowlist(struct PanelWidget * this, struct yutani_msg_window_mouse_event * evt) {
foreach(node, window_list) {
struct window_ad * ad = node->value;
if (evt->new_x >= ad->left && evt->new_x < ad->left + TOTAL_CELL_WIDTH) {
yutani_focus_window(yctx, ad->wid);
break;
}
}
return 0;
}
static int widget_rightclick_windowlist(struct PanelWidget * this, struct yutani_msg_window_mouse_event * evt) {
foreach(node, window_list) {
struct window_ad * ad = node->value;
if (evt->new_x >= ad->left && evt->new_x < ad->left + TOTAL_CELL_WIDTH) {
window_show_menu(ad->wid, evt->new_x);
}
}
return 0;
}
/* Update the hover-focus window */
static int set_focused(int i) {
if (focused_app != i) {
focused_app = i;
return 1;
}
return 0;
}
static int widget_move_windowlist(struct PanelWidget * this, struct yutani_msg_window_mouse_event * evt) {
int found = 0;
int i = 0;
int should_redraw = 0;
foreach(node, window_list) {
struct window_ad * ad = node->value;
if (evt->new_x >= ad->left && evt->new_x < ad->left + TOTAL_CELL_WIDTH) {
found = 1;
should_redraw |= set_focused(i);
break;
}
i++;
}
if (!found) {
should_redraw |= set_focused(-1);
}
int scroll_direction = 0;
if (evt->buttons & YUTANI_MOUSE_SCROLL_UP) scroll_direction = -1;
else if (evt->buttons & YUTANI_MOUSE_SCROLL_DOWN) scroll_direction = 1;
if (scroll_direction != 0) {
struct window_ad * last = window_list->tail ? window_list->tail->value : NULL;
int focus_next = 0;
foreach(node, window_list) {
struct window_ad * ad = node->value;
if (focus_next) {
yutani_focus_window(yctx, ad->wid);
return 1;
}
if (ad->flags & 1) {
if (scroll_direction == -1) {
yutani_focus_window(yctx, last->wid);
return 1;
}
if (scroll_direction == 1) {
focus_next = 1;
}
}
last = ad;
}
if (focus_next && window_list->head) {
struct window_ad * ad = window_list->head->value;
yutani_focus_window(yctx, ad->wid);
return 1;
}
}
return should_redraw;
}
static int widget_leave_windowlist(struct PanelWidget * this, struct yutani_msg_window_mouse_event * evt) {
this->highlighted = 0;
return set_focused(-1);
}
static int widget_onkey_windowlist(struct PanelWidget * this, struct yutani_msg_key_event * ke) {
if ((ke->event.modifiers & KEY_MOD_LEFT_ALT) &&
(ke->event.keycode == KEY_F3) &&
(ke->event.action == KEY_ACTION_DOWN)) {
foreach(node, window_list) {
struct window_ad * ad = node->value;
if (ad->flags & 1) {
window_show_menu(ad->wid, ad->left + title_width / 2);
}
}
}
return 0;
}
struct PanelWidget * widget_init_windowlist(void) {
window_menu = menu_create();
window_menu->flags |= MENU_FLAG_BUBBLE_LEFT;
menu_insert(window_menu, menu_create_normal(NULL, NULL, "Maximize", _window_menu_start_maximize));
menu_insert(window_menu, menu_create_normal(NULL, NULL, "Move", _window_menu_start_move));
menu_insert(window_menu, menu_create_separator());
menu_insert(window_menu, menu_create_normal(NULL, NULL, "Close", _window_menu_close));
/* Alt+F3 = window context menu */
yutani_key_bind(yctx, KEY_F3, KEY_MOD_LEFT_ALT, YUTANI_BIND_STEAL);
struct PanelWidget * widget = widget_new();
widget->fill = 1;
widget->draw = widget_draw_windowlist;
widget->click = widget_click_windowlist;
widget->right_click = widget_rightclick_windowlist;
widget->move = widget_move_windowlist;
widget->leave = widget_leave_windowlist;
widget->onkey = widget_onkey_windowlist;
list_insert(widgets_enabled, widget);
return widget;
}