WIP: UI: Status Bar Keymaps When Idle #121059

Draft
Harley Acheson wants to merge 8 commits from Harley/blender:IdleKeymaps into main

When changing the target branch, be careful to rebase the branch in your fork to match. See documentation.
8 changed files with 142 additions and 240 deletions

View File

@ -4067,7 +4067,8 @@ static uiBut *ui_def_but(uiBlock *block,
float max,
const char *tip)
{
BLI_assert(width >= 0 && height >= 0);
/* Allow negative separators. */
BLI_assert(width >= 0 && height >= 0 || (type == UI_BTYPE_SEPR));
if (type & UI_BUT_POIN_TYPES) { /* a pointer is required */
if (poin == nullptr) {

View File

@ -79,6 +79,7 @@
#include "ED_object.hh"
#include "ED_render.hh"
#include "ED_screen.hh"
#include "ED_screen_types.hh"
#include "ED_undo.hh"
#include "IMB_imbuf.hh"
@ -6307,6 +6308,60 @@ void uiTemplateReportsBanner(uiLayout *layout, bContext *C)
UI_block_emboss_set(block, previous_emboss);
}
static wmKeyMapItem *keymapItem(bContext *C,
wmWindowManager *wm,
wmWindow *win,
ScrArea *area,
ARegion *region,
short type,
short val,
uint8_t modifier)
{
wmEvent test_event = *win->eventstate;
test_event.type = type;
test_event.val = val;
test_event.modifier = modifier;
test_event.flag = (eWM_EventFlag)0;
ListBase *handlers[] = {
&region->handlers,
&area->handlers,
&win->handlers,
};
for (int handler_index = 0; handler_index < ARRAY_SIZE(handlers); handler_index++) {
if (!handlers[handler_index]) {
continue;
}
wmKeyMapItem *kmi = WM_event_match_keymap_item_from_handlers(
C, wm, win, handlers[handler_index], &test_event);
if (kmi && !(kmi->flag & KMI_INACTIVE)) {
return kmi;
}
}
return nullptr;
}
static bool uiTemplateInputStatusAzone(uiLayout *layout, AZone *az, ARegion *region)
{
if (az->type == AZONE_AREA) {
uiItemL(layout, IFACE_("Split (within area)"), ICON_MOUSE_LMB_DRAG);
uiItemL(layout, IFACE_("Join (out of area)"), ICON_MOUSE_LMB_DRAG);
uiItemL(layout, "", ICON_EVENT_SHIFT);
uiItemL(layout, IFACE_("New window"), ICON_MOUSE_LMB_DRAG);
uiItemL(layout, "", ICON_EVENT_CTRL);
uiItemL(layout, IFACE_("Swap areas"), ICON_MOUSE_LMB_DRAG);
return true;
}
if (az->type == AZONE_REGION) {
uiItemL(layout,
(region->visible) ? IFACE_("Resize region") : IFACE_("Show hidden region"),
ICON_MOUSE_LMB_DRAG);
return true;
}
return false;
}
void uiTemplateInputStatus(uiLayout *layout, bContext *C)
{
wmWindow *win = CTX_wm_window(C);
@ -6323,32 +6378,90 @@ void uiTemplateInputStatus(uiLayout *layout, bContext *C)
}
/* Otherwise should cursor keymap status. */
for (int i = 0; i < 3; i++) {
uiLayout *box = uiLayoutRow(layout, false);
uiLayout *col = uiLayoutColumn(box, false);
uiLayout *row = uiLayoutRow(col, true);
uiLayoutSetAlignment(row, UI_LAYOUT_ALIGN_LEFT);
const char *msg = CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT,
WM_window_cursor_keymap_status_get(win, i, 0));
const char *msg_drag = CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT,
WM_window_cursor_keymap_status_get(win, i, 1));
uiLayout *row = uiLayoutRow(layout, true);
bScreen *screen = CTX_wm_screen(C);
ARegion *region = screen->active_region;
ScrArea *area = nullptr;
if (msg || (msg_drag == nullptr)) {
/* Icon and text separately are closer together with aligned layout. */
uiItemL(row, "", (ICON_MOUSE_LMB + i));
uiItemL(row, msg ? msg : "", ICON_NONE);
if (region == nullptr) {
/* Could be over an action zone. Separate PR? */
LISTBASE_FOREACH (ScrArea *, area_iter, &screen->areabase) {
LISTBASE_FOREACH (AZone *, az, &area_iter->actionzones) {
if (BLI_rcti_isect_pt_v(&az->rect, win->eventstate->xy)) {
region = az->region;
area = area_iter;
if (uiTemplateInputStatusAzone(row, az, region)) {
return;
}
break;
}
}
}
if (msg_drag) {
uiItemL(row, "", (ICON_MOUSE_LMB_DRAG + i));
uiItemL(row, msg_drag, ICON_NONE);
}
/* Use trick with empty string to keep icons in same position. */
row = uiLayoutRow(col, false);
uiItemL(row, " ", ICON_NONE);
}
if (!region) {
/* Could be a separate PR to deal with edges. */
uiItemL(row, "Resize", ICON_MOUSE_LMB_DRAG);
uiItemL(row, "Options", ICON_MOUSE_RMB);
return;
}
if (!area) {
area = ED_area_find_under_cursor(C, SPACE_TYPE_ANY, win->eventstate->xy);
}
if (area == nullptr) {
return;
}
/* Fallback to window for these region types. */
if (ELEM(region->regiontype, RGN_TYPE_TOOLS, RGN_TYPE_TOOL_PROPS)) {
region = BKE_area_find_region_type(area, RGN_TYPE_WINDOW);
}
ScrArea *save_area = CTX_wm_area(C);
ARegion *save_region = CTX_wm_region(C);
CTX_wm_window_set(C, win);
CTX_wm_area_set(C, area);
CTX_wm_region_set(C, region);
wmKeyMapItem *kmi;
wmWindowManager *wm = CTX_wm_manager(C);
blender::Set<std::string> op_names;
uint8_t modifiers[] = {0, KM_SHIFT, KM_CTRL, KM_ALT, KM_OSKEY, KM_SHIFT | KM_CTRL};
for (int mod_index = 0; mod_index < ARRAY_SIZE(modifiers); mod_index++) {
for (short button = LEFTMOUSE; button <= RIGHTMOUSE; button++) {
for (short action = KM_PRESS; action <= KM_CLICK_DRAG; action++) {
kmi = keymapItem(C, wm, win, area, region, button, action, modifiers[mod_index]);
if (kmi) {
wmOperatorType *ot = WM_operatortype_find(kmi->idname, false);
const std::string operator_name = WM_operatortype_name(ot, kmi->ptr);
std::string name = (ot) ? operator_name : kmi->idname;
if (op_names.add(name)) {
int icon_mod[4];
int icon = UI_icon_from_keymap_item(kmi, icon_mod);
for (int j = 0; j < ARRAY_SIZE(icon_mod) && icon_mod[j]; j++) {
uiItemL(row, "", icon_mod[j]);
}
if (icon >= ICON_MOUSE_LMB && icon <= ICON_MOUSE_RMB) {
uiItemS_ex(row, -0.5f);
}
uiItemL(row, "", icon);
if (icon >= ICON_MOUSE_LMB && icon <= ICON_MOUSE_RMB) {
uiItemS_ex(row, -0.7f);
}
uiItemL(row, name.c_str(), ICON_NONE);
uiItemS_ex(row, 0.7f);
}
}
}
}
}
CTX_wm_area_set(C, save_area);
CTX_wm_region_set(C, save_region);
}
void uiTemplateStatusInfo(uiLayout *layout, bContext *C)

View File

@ -973,6 +973,10 @@ void ED_screen_set_active_region(bContext *C, wmWindow *win, const int xy[2])
screen->active_region = nullptr;
}
if (region_prev != screen->active_region || !screen->active_region) {
WM_window_status_area_tag_redraw(win);
}
/* Check for redraw headers. */
if (region_prev != screen->active_region) {

View File

@ -397,8 +397,7 @@ typedef struct wmWindow {
/** Custom drawing callbacks. */
ListBase drawcalls;
/** Private runtime info to show text in the status bar. */
void *cursor_keymap_status;
void *_pad2;
/**
* The time when the key is pressed in milliseconds (see #GHOST_GetEventTime).

View File

@ -1740,11 +1740,6 @@ void WM_event_tablet_data_default_set(wmTabletData *tablet_data);
*/
wmEvent *WM_event_add_simulate(wmWindow *win, const wmEvent *event_to_add);
const char *WM_window_cursor_keymap_status_get(const wmWindow *win,
int button_index,
int type_index);
void WM_window_cursor_keymap_status_refresh(bContext *C, wmWindow *win);
void WM_window_status_area_tag_redraw(wmWindow *win);
/**
* Similar to #BKE_screen_area_map_find_area_xy and related functions,

View File

@ -171,7 +171,6 @@ static void window_manager_blend_read_data(BlendDataReader *reader, ID *id)
win->eventstate = nullptr;
win->eventstate_prev_press_time_ms = 0;
win->event_last_handled = nullptr;
win->cursor_keymap_status = nullptr;
#if defined(WIN32) || defined(__APPLE__)
win->ime_data = nullptr;
win->ime_data_is_composing = false;

View File

@ -726,14 +726,6 @@ void wm_event_do_notifiers(bContext *C)
RE_FreeUnusedGPUResources();
/* Status bar. */
if (wm->winactive) {
wmWindow *win = wm->winactive;
CTX_wm_window_set(C, win);
WM_window_cursor_keymap_status_refresh(C, win);
CTX_wm_window_set(C, nullptr);
}
/* Auto-run warning. */
wm_test_autorun_warning(C);
/* Deprecation warning. */
@ -6142,52 +6134,6 @@ bool WM_event_match(const wmEvent *winevent, const wmKeyMapItem *kmi)
* so only perform this on changing tools, space types, pressing different modifier keys... etc.
* \{ */
/** State storage to detect changes between calls to refresh the information. */
struct CursorKeymapInfo_State {
uint8_t modifier;
short space_type;
short region_type;
/** Never use, just compare memory for changes. */
bToolRef tref;
};
struct CursorKeymapInfo {
/**
* 0: Mouse button index.
* 1: Event type (click/press, drag).
* 2: Text.
*/
char text[3][2][128];
wmEvent state_event;
CursorKeymapInfo_State state;
};
static void wm_event_cursor_store(CursorKeymapInfo_State *state,
const wmEvent *event,
short space_type,
short region_type,
const bToolRef *tref)
{
state->modifier = event->modifier;
state->space_type = space_type;
state->region_type = region_type;
state->tref = tref ? *tref : bToolRef{};
}
const char *WM_window_cursor_keymap_status_get(const wmWindow *win,
int button_index,
int type_index)
{
if (win->cursor_keymap_status != nullptr) {
CursorKeymapInfo *cd = static_cast<CursorKeymapInfo *>(win->cursor_keymap_status);
const char *msg = cd->text[button_index][type_index];
if (*msg) {
return msg;
}
}
return nullptr;
}
ScrArea *WM_window_status_area_find(wmWindow *win, bScreen *screen)
{
if (screen->state == SCREENFULL) {
@ -6212,157 +6158,6 @@ void WM_window_status_area_tag_redraw(wmWindow *win)
}
}
void WM_window_cursor_keymap_status_refresh(bContext *C, wmWindow *win)
{
bScreen *screen = WM_window_get_active_screen(win);
ScrArea *area_statusbar = WM_window_status_area_find(win, screen);
if (area_statusbar == nullptr) {
MEM_SAFE_FREE(win->cursor_keymap_status);
return;
}
CursorKeymapInfo *cd;
if (UNLIKELY(win->cursor_keymap_status == nullptr)) {
win->cursor_keymap_status = MEM_callocN(sizeof(CursorKeymapInfo), __func__);
}
cd = static_cast<CursorKeymapInfo *>(win->cursor_keymap_status);
/* Detect unchanged state (early exit). */
if (memcmp(&cd->state_event, win->eventstate, sizeof(wmEvent)) == 0) {
return;
}
/* Now perform more comprehensive check,
* still keep this fast since it happens on mouse-move. */
CursorKeymapInfo cd_prev = *((CursorKeymapInfo *)win->cursor_keymap_status);
cd->state_event = *win->eventstate;
/* Find active region and associated area. */
ARegion *region = screen->active_region;
if (region == nullptr) {
return;
}
ScrArea *area = nullptr;
ED_screen_areas_iter (win, screen, area_iter) {
if (BLI_findindex(&area_iter->regionbase, region) != -1) {
area = area_iter;
break;
}
}
if (area == nullptr) {
return;
}
/* Keep as-is. */
if (ELEM(area->spacetype, SPACE_STATUSBAR, SPACE_TOPBAR)) {
return;
}
if (ELEM(region->regiontype,
RGN_TYPE_HEADER,
RGN_TYPE_TOOL_HEADER,
RGN_TYPE_FOOTER,
RGN_TYPE_ASSET_SHELF_HEADER,
RGN_TYPE_TEMPORARY,
RGN_TYPE_HUD))
{
return;
}
/* Fallback to window. */
if (ELEM(region->regiontype, RGN_TYPE_TOOLS, RGN_TYPE_TOOL_PROPS)) {
region = BKE_area_find_region_type(area, RGN_TYPE_WINDOW);
}
/* Detect changes to the state. */
{
bToolRef *tref = nullptr;
if ((region->regiontype == RGN_TYPE_WINDOW) &&
((1 << area->spacetype) & WM_TOOLSYSTEM_SPACE_MASK))
{
const Scene *scene = WM_window_get_active_scene(win);
ViewLayer *view_layer = WM_window_get_active_view_layer(win);
WorkSpace *workspace = WM_window_get_active_workspace(win);
bToolKey tkey{};
tkey.space_type = area->spacetype;
tkey.mode = WM_toolsystem_mode_from_spacetype(scene, view_layer, area, area->spacetype);
tref = WM_toolsystem_ref_find(workspace, &tkey);
}
wm_event_cursor_store(&cd->state, win->eventstate, area->spacetype, region->regiontype, tref);
if (memcmp(&cd->state, &cd_prev.state, sizeof(cd->state)) == 0) {
return;
}
}
/* Changed context found, detect changes to key-map and refresh the status bar. */
const struct {
int button_index;
int type_index; /* 0: press or click, 1: drag. */
int event_type;
int event_value;
} event_data[] = {
{0, 0, LEFTMOUSE, KM_PRESS},
{0, 0, LEFTMOUSE, KM_CLICK},
{0, 0, LEFTMOUSE, KM_CLICK_DRAG},
{1, 0, MIDDLEMOUSE, KM_PRESS},
{1, 0, MIDDLEMOUSE, KM_CLICK},
{1, 0, MIDDLEMOUSE, KM_CLICK_DRAG},
{2, 0, RIGHTMOUSE, KM_PRESS},
{2, 0, RIGHTMOUSE, KM_CLICK},
{2, 0, RIGHTMOUSE, KM_CLICK_DRAG},
};
for (int button_index = 0; button_index < 3; button_index++) {
cd->text[button_index][0][0] = '\0';
cd->text[button_index][1][0] = '\0';
}
CTX_wm_window_set(C, win);
CTX_wm_area_set(C, area);
CTX_wm_region_set(C, region);
ListBase *handlers[] = {
&region->handlers,
&area->handlers,
&win->handlers,
};
wmWindowManager *wm = CTX_wm_manager(C);
for (int data_index = 0; data_index < ARRAY_SIZE(event_data); data_index++) {
const int button_index = event_data[data_index].button_index;
const int type_index = event_data[data_index].type_index;
if (cd->text[button_index][type_index][0] != 0) {
continue;
}
wmEvent test_event = *win->eventstate;
test_event.type = event_data[data_index].event_type;
test_event.val = event_data[data_index].event_value;
test_event.flag = (eWM_EventFlag)0;
wm_eventemulation(&test_event, true);
wmKeyMapItem *kmi = nullptr;
for (int handler_index = 0; handler_index < ARRAY_SIZE(handlers); handler_index++) {
kmi = WM_event_match_keymap_item_from_handlers(
C, wm, win, handlers[handler_index], &test_event);
if (kmi) {
break;
}
}
if (kmi) {
wmOperatorType *ot = WM_operatortype_find(kmi->idname, false);
const std::string operator_name = WM_operatortype_name(ot, kmi->ptr);
const char *name = (ot) ? operator_name.c_str() : kmi->idname;
STRNCPY(cd->text[button_index][type_index], name);
}
}
if (memcmp(&cd_prev.text, &cd->text, sizeof(cd_prev.text)) != 0) {
ED_area_tag_redraw(area_statusbar);
}
CTX_wm_window_set(C, nullptr);
}
/** \} */
/* -------------------------------------------------------------------- */

View File

@ -276,10 +276,6 @@ void wm_window_free(bContext *C, wmWindowManager *wm, wmWindow *win)
WM_event_consecutive_data_free(win);
}
if (win->cursor_keymap_status) {
MEM_freeN(win->cursor_keymap_status);
}
WM_gestures_free_all(win);
wm_event_free_all(win);