/** * @brief Panel window list widget */ #include #include #include #include #include #include #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; /* Update the hover-focus window */ static void set_focused(int i) { if (focused_app != i) { focused_app = i; redraw(); } } 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; } static int widget_move_windowlist(struct PanelWidget * this, struct yutani_msg_window_mouse_event * evt) { int found = 0; int i = 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; set_focused(i); break; } i++; } if (!found) { 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 1; } static int widget_leave_windowlist(struct PanelWidget * this, struct yutani_msg_window_mouse_event * evt) { this->highlighted = 0; set_focused(-1); return 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; }