UI: Optional Complex Layout for Workspace Status #120595
|
@ -17,6 +17,21 @@ struct WorkSpace;
|
|||
struct WorkSpaceInstanceHook;
|
||||
struct WorkSpaceLayout;
|
||||
|
||||
struct WorkSpaceStatusItem {
|
||||
int icon = 0;
|
||||
std::string text = {};
|
||||
float space_factor = 0.0f;
|
||||
bool inverted = false;
|
||||
};
|
||||
|
||||
namespace blender::bke {
|
||||
|
||||
struct WorkSpaceRuntime {
|
||||
blender::Vector<WorkSpaceStatusItem> status;
|
||||
};
|
||||
Harley marked this conversation as resolved
|
||||
|
||||
} // namespace blender::bke
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Create, Delete, Initialize
|
||||
* \{ */
|
||||
|
@ -153,6 +168,11 @@ bool BKE_workspace_owner_id_check(const WorkSpace *workspace, const char *owner_
|
|||
|
||||
void BKE_workspace_id_tag_all_visible(Main *bmain, int tag) ATTR_NONNULL();
|
||||
|
||||
/**
|
||||
* Empty the Workspace status items to clear the status bar.
|
||||
*/
|
||||
void BKE_workspace_status_clear(struct WorkSpace *workspace);
|
||||
|
||||
#undef GETTER_ATTRS
|
||||
#undef SETTER_ATTRS
|
||||
|
||||
|
|
|
@ -42,6 +42,8 @@ static void workspace_init_data(ID *id)
|
|||
{
|
||||
WorkSpace *workspace = (WorkSpace *)id;
|
||||
|
||||
workspace->runtime = MEM_new<blender::bke::WorkSpaceRuntime>(__func__);
|
||||
|
||||
BKE_asset_library_reference_init_default(&workspace->asset_library_ref);
|
||||
}
|
||||
|
||||
|
@ -58,7 +60,9 @@ static void workspace_free_data(ID *id)
|
|||
BKE_workspace_tool_remove(workspace, static_cast<bToolRef *>(workspace->tools.first));
|
||||
}
|
||||
|
||||
MEM_SAFE_FREE(workspace->status_text);
|
||||
BKE_workspace_status_clear(workspace);
|
||||
MEM_delete(workspace->runtime);
|
||||
|
||||
BKE_viewer_path_clear(&workspace->viewer_path);
|
||||
}
|
||||
|
||||
|
@ -115,7 +119,7 @@ static void workspace_blend_read_data(BlendDataReader *reader, ID *id)
|
|||
IDP_BlendDataRead(reader, &tref->properties);
|
||||
}
|
||||
|
||||
workspace->status_text = nullptr;
|
||||
workspace->runtime = MEM_new<blender::bke::WorkSpaceRuntime>(__func__);
|
||||
|
||||
/* Do not keep the scene reference when appending a workspace. Setting a scene for a workspace is
|
||||
* a convenience feature, but the workspace should never truly depend on scene data. */
|
||||
|
@ -633,3 +637,14 @@ bScreen *BKE_workspace_layout_screen_get(const WorkSpaceLayout *layout)
|
|||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Status
|
||||
* \{ */
|
||||
|
||||
void BKE_workspace_status_clear(WorkSpace *workspace)
|
||||
{
|
||||
workspace->runtime->status.clear_and_shrink();
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
|
|
@ -450,6 +450,19 @@ bool ED_workspace_layout_cycle(WorkSpace *workspace, short direction, bContext *
|
|||
|
||||
void ED_workspace_status_text(bContext *C, const char *str);
|
||||
|
||||
class WorkspaceStatus {
|
||||
private:
|
||||
WorkSpace *workspace_;
|
||||
wmWindowManager *wm_;
|
||||
|
||||
public:
|
||||
WorkspaceStatus(bContext *C);
|
||||
void item(const std::string text, int icon1, int icon2 = 0);
|
||||
void item_bool(const std::string text, bool inverted, int icon1, int icon2 = 0);
|
||||
void range(const std::string text, int icon1, int icon2);
|
||||
void opmodal(const std::string text, wmOperatorType *ot, int propvalue, bool inverted = false);
|
||||
};
|
||||
|
||||
void ED_workspace_do_listen(bContext *C, const wmNotifier *note);
|
||||
|
||||
/* anim */
|
||||
|
|
|
@ -362,6 +362,9 @@ enum {
|
|||
|
||||
/** Drawn in a way that indicates that the state/value is unknown. */
|
||||
UI_BUT_INDETERMINATE = 1 << 26,
|
||||
|
||||
/** Draw icon inverted to indicate a special state. */
|
||||
UI_BUT_ICON_INVERT = 1 << 27,
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -111,7 +111,8 @@ void UI_icon_draw_ex(float x,
|
|||
float desaturate,
|
||||
const uchar mono_color[4],
|
||||
bool mono_border,
|
||||
const IconTextOverlay *text_overlay);
|
||||
const IconTextOverlay *text_overlay,
|
||||
const bool inverted = false);
|
||||
|
||||
/**
|
||||
* Draw an monochrome icon into a given coordinate rectangle. The rectangle is used as-is,
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -651,13 +651,13 @@ int UI_icon_from_event_type(short event_type, short event_value)
|
|||
} while ((di = di->data.input.next));
|
||||
|
||||
if (event_type == LEFTMOUSE) {
|
||||
return ELEM(event_value, KM_CLICK, KM_PRESS) ? ICON_MOUSE_LMB : ICON_MOUSE_LMB_DRAG;
|
||||
return (event_value == KM_CLICK_DRAG) ? ICON_MOUSE_LMB_DRAG : ICON_MOUSE_LMB;
|
||||
}
|
||||
if (event_type == MIDDLEMOUSE) {
|
||||
return ELEM(event_value, KM_CLICK, KM_PRESS) ? ICON_MOUSE_MMB : ICON_MOUSE_MMB_DRAG;
|
||||
return (event_value == KM_CLICK_DRAG) ? ICON_MOUSE_MMB_DRAG : ICON_MOUSE_MMB;
|
||||
}
|
||||
if (event_type == RIGHTMOUSE) {
|
||||
return ELEM(event_value, KM_CLICK, KM_PRESS) ? ICON_MOUSE_RMB : ICON_MOUSE_RMB_DRAG;
|
||||
return (event_value == KM_CLICK_DRAG) ? ICON_MOUSE_MMB_DRAG : ICON_MOUSE_RMB;
|
||||
}
|
||||
|
||||
return ICON_NONE;
|
||||
|
@ -1985,7 +1985,8 @@ static void icon_draw_size(float x,
|
|||
const float desaturate,
|
||||
const uchar mono_rgba[4],
|
||||
const bool mono_border,
|
||||
const IconTextOverlay *text_overlay)
|
||||
const IconTextOverlay *text_overlay,
|
||||
const bool inverted = false)
|
||||
{
|
||||
bTheme *btheme = UI_GetTheme();
|
||||
const float fdraw_size = float(draw_size);
|
||||
|
@ -2059,7 +2060,7 @@ static void icon_draw_size(float x,
|
|||
else if (di->type == ICON_TYPE_EVENT) {
|
||||
const short event_type = di->data.input.event_type;
|
||||
const short event_value = di->data.input.event_value;
|
||||
icon_draw_rect_input(x, y, w, h, alpha, event_type, event_value);
|
||||
icon_draw_rect_input(x, y, w, h, alpha, event_type, event_value, inverted);
|
||||
}
|
||||
else if (di->type == ICON_TYPE_COLOR_TEXTURE) {
|
||||
/* texture image use premul alpha for correct scaling */
|
||||
|
@ -2676,7 +2677,8 @@ void UI_icon_draw_ex(float x,
|
|||
float desaturate,
|
||||
const uchar mono_color[4],
|
||||
const bool mono_border,
|
||||
const IconTextOverlay *text_overlay)
|
||||
const IconTextOverlay *text_overlay,
|
||||
const bool inverted)
|
||||
{
|
||||
const int draw_size = get_draw_size(ICON_SIZE_ICON);
|
||||
icon_draw_size(x,
|
||||
|
@ -2689,7 +2691,8 @@ void UI_icon_draw_ex(float x,
|
|||
desaturate,
|
||||
mono_color,
|
||||
mono_border,
|
||||
text_overlay);
|
||||
text_overlay,
|
||||
inverted);
|
||||
}
|
||||
|
||||
void UI_icon_draw_mono_rect(
|
||||
|
|
|
@ -38,8 +38,14 @@ static void icon_draw_rect_input_text(
|
|||
BLF_batch_draw_flush();
|
||||
}
|
||||
|
||||
void icon_draw_rect_input(
|
||||
float x, float y, int w, int h, float /*alpha*/, short event_type, short /*event_value*/)
|
||||
void icon_draw_rect_input(float x,
|
||||
float y,
|
||||
int w,
|
||||
int h,
|
||||
float /*alpha*/,
|
||||
short event_type,
|
||||
short /*event_value*/,
|
||||
bool inverted)
|
||||
{
|
||||
rctf rect{};
|
||||
rect.xmin = int(x) - U.pixelsize;
|
||||
|
@ -49,9 +55,17 @@ void icon_draw_rect_input(
|
|||
|
||||
float color[4];
|
||||
GPU_line_width(1.0f);
|
||||
UI_GetThemeColor4fv(TH_TEXT, color);
|
||||
UI_draw_roundbox_corner_set(UI_CNR_ALL);
|
||||
UI_draw_roundbox_aa(&rect, false, 3.0f * U.pixelsize, color);
|
||||
|
||||
if (inverted) {
|
||||
UI_GetThemeColor4fv(TH_TEXT, color);
|
||||
UI_draw_roundbox_aa(&rect, true, 3.0f * U.pixelsize, color);
|
||||
UI_GetThemeColor4fv(TH_BACK, color);
|
||||
}
|
||||
else {
|
||||
UI_GetThemeColor4fv(TH_TEXT, color);
|
||||
UI_draw_roundbox_aa(&rect, false, 3.0f * U.pixelsize, color);
|
||||
}
|
||||
|
||||
const enum {
|
||||
UNIX,
|
||||
|
|
|
@ -1298,8 +1298,14 @@ int ui_id_icon_get(const bContext *C, ID *id, bool big);
|
|||
|
||||
/* interface_icons_event.cc */
|
||||
|
||||
void icon_draw_rect_input(
|
||||
float x, float y, int w, int h, float alpha, short event_type, short event_value);
|
||||
void icon_draw_rect_input(float x,
|
||||
float y,
|
||||
int w,
|
||||
int h,
|
||||
float alpha,
|
||||
short event_type,
|
||||
short event_value,
|
||||
bool inverted = false);
|
||||
|
||||
/* resources.cc */
|
||||
|
||||
|
|
|
@ -1367,8 +1367,16 @@ static void widget_draw_icon(
|
|||
if (has_theme) {
|
||||
alpha *= 0.8f;
|
||||
}
|
||||
UI_icon_draw_ex(
|
||||
xs, ys, icon, aspect, alpha, 0.0f, color, has_theme, &but->icon_overlay_text);
|
||||
UI_icon_draw_ex(xs,
|
||||
ys,
|
||||
icon,
|
||||
aspect,
|
||||
alpha,
|
||||
0.0f,
|
||||
color,
|
||||
has_theme,
|
||||
&but->icon_overlay_text,
|
||||
but->drawflag & UI_BUT_ICON_INVERT);
|
||||
}
|
||||
else {
|
||||
const bTheme *btheme = UI_GetTheme();
|
||||
|
|
|
@ -67,6 +67,7 @@
|
|||
#include "BKE_scene.hh"
|
||||
#include "BKE_screen.hh"
|
||||
#include "BKE_shader_fx.h"
|
||||
#include "BKE_workspace.hh"
|
||||
|
||||
#include "BLO_readfile.hh"
|
||||
|
||||
|
@ -6313,8 +6314,19 @@ void uiTemplateInputStatus(uiLayout *layout, bContext *C)
|
|||
WorkSpace *workspace = CTX_wm_workspace(C);
|
||||
|
||||
/* Workspace status text has priority. */
|
||||
if (workspace->status_text) {
|
||||
uiItemL(layout, workspace->status_text, ICON_NONE);
|
||||
if (!workspace->runtime->status.is_empty()) {
|
||||
uiLayout *row = uiLayoutRow(layout, true);
|
||||
for (WorkSpaceStatusItem item : workspace->runtime->status) {
|
||||
if (item.space_factor != 0.0f) {
|
||||
uiItemS_ex(row, item.space_factor);
|
||||
}
|
||||
else {
|
||||
uiBut *but = uiItemL_ex(row, item.text.c_str(), item.icon, false, false);
|
||||
if (item.inverted) {
|
||||
but->drawflag |= UI_BUT_ICON_INVERT;
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -834,35 +834,138 @@ void ED_area_status_text(ScrArea *area, const char *str)
|
|||
}
|
||||
}
|
||||
|
||||
void ED_workspace_status_text(bContext *C, const char *str)
|
||||
{
|
||||
wmWindow *win = CTX_wm_window(C);
|
||||
WorkSpace *workspace = CTX_wm_workspace(C);
|
||||
/* *************************************************************** */
|
||||
|
||||
static void ed_workspace_status_item(WorkSpace *workspace,
|
||||
const std::string text,
|
||||
const int icon,
|
||||
const float space_factor = 0.0f,
|
||||
const bool inverted = false)
|
||||
{
|
||||
/* Can be nullptr when running operators in background mode. */
|
||||
if (workspace == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (str) {
|
||||
if (workspace->status_text == nullptr) {
|
||||
workspace->status_text = static_cast<char *>(MEM_mallocN(UI_MAX_DRAW_STR, "headerprint"));
|
||||
}
|
||||
BLI_strncpy(workspace->status_text, str, UI_MAX_DRAW_STR);
|
||||
}
|
||||
else {
|
||||
MEM_SAFE_FREE(workspace->status_text);
|
||||
}
|
||||
WorkSpaceStatusItem item;
|
||||
item.text = text;
|
||||
item.icon = icon;
|
||||
item.space_factor = space_factor;
|
||||
item.inverted = inverted;
|
||||
workspace->runtime->status.append(item);
|
||||
}
|
||||
|
||||
/* Redraw status bar. */
|
||||
LISTBASE_FOREACH (ScrArea *, area, &win->global_areas.areabase) {
|
||||
if (area->spacetype == SPACE_STATUSBAR) {
|
||||
ED_area_tag_redraw(area);
|
||||
break;
|
||||
static void ed_workspace_status_space(WorkSpace *workspace, float space_factor)
|
||||
{
|
||||
ed_workspace_status_item(workspace, {}, ICON_NONE, space_factor);
|
||||
}
|
||||
|
||||
WorkspaceStatus::WorkspaceStatus(bContext *C)
|
||||
{
|
||||
workspace_ = CTX_wm_workspace(C);
|
||||
Harley marked this conversation as resolved
Brecht Van Lommel
commented
This is a bit bitpicky, but I think it's better not to store pointers to context like this. Because it doesn't represent a frozen context. I suggest to put This is a bit bitpicky, but I think it's better not to store pointers to context like this. Because it doesn't represent a frozen context.
I suggest to put `ED_area_tag_redraw` in the constructor, and then store a pointer to the window manager along with the workspace.
|
||||
wm_ = CTX_wm_manager(C);
|
||||
if (workspace_) {
|
||||
BKE_workspace_status_clear(workspace_);
|
||||
}
|
||||
ED_area_tag_redraw(WM_window_status_area_find(CTX_wm_window(C), CTX_wm_screen(C)));
|
||||
}
|
||||
|
||||
/* Private helper functions to help ensure consistant spacing. */
|
||||
|
||||
#define STATUS_AFTER_TEXT 0.7f
|
||||
#define STATUS_BEFORE_TEXT 0.3f
|
||||
#define STATUS_MOUSE_ICON_BEFORE -0.5f
|
||||
#define STATUS_MOUSE_ICON_AFTER -0.7f
|
||||
|
||||
static void ed_workspace_status_text_item(WorkSpace *workspace, const std::string text)
|
||||
{
|
||||
if (!text.empty()) {
|
||||
ed_workspace_status_space(workspace, STATUS_BEFORE_TEXT);
|
||||
ed_workspace_status_item(workspace, text, ICON_NONE);
|
||||
ed_workspace_status_space(workspace, STATUS_AFTER_TEXT);
|
||||
}
|
||||
}
|
||||
|
||||
static void ed_workspace_status_mouse_item(WorkSpace *workspace, int icon, bool inverted = false)
|
||||
{
|
||||
if (icon) {
|
||||
if (icon >= ICON_MOUSE_LMB && icon <= ICON_MOUSE_RMB_DRAG) {
|
||||
/* Negative space before all narrow mice icons. */
|
||||
ed_workspace_status_space(workspace, STATUS_MOUSE_ICON_BEFORE);
|
||||
}
|
||||
ed_workspace_status_item(workspace, {}, icon, 0.0f, inverted);
|
||||
if (icon >= ICON_MOUSE_LMB && icon <= ICON_MOUSE_RMB) {
|
||||
/* Negative space after non-drag mice icons. */
|
||||
ed_workspace_status_space(workspace, STATUS_MOUSE_ICON_AFTER);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#undef STATUS_AFTER_TEXT
|
||||
#undef STATUS_BEFORE_TEXT
|
||||
#undef STATUS_MOUSE_ICON_BEFORE
|
||||
#undef STATUS_MOUSE_ICON_AFTER
|
||||
|
||||
/* Public functions. */
|
||||
|
||||
void WorkspaceStatus::item(std::string text, int icon1, int icon2)
|
||||
{
|
||||
ed_workspace_status_mouse_item(workspace_, icon1);
|
||||
ed_workspace_status_mouse_item(workspace_, icon2);
|
||||
ed_workspace_status_text_item(workspace_, text);
|
||||
}
|
||||
|
||||
void WorkspaceStatus::range(std::string text, int icon1, int icon2)
|
||||
{
|
||||
ed_workspace_status_item(workspace_, {}, icon1);
|
||||
ed_workspace_status_item(workspace_, "-", ICON_NONE);
|
||||
ed_workspace_status_space(workspace_, -0.5f);
|
||||
ed_workspace_status_item(workspace_, {}, icon2);
|
||||
ed_workspace_status_text_item(workspace_, text);
|
||||
}
|
||||
|
||||
void WorkspaceStatus::item_bool(std::string text, bool interted, int icon1, int icon2)
|
||||
{
|
||||
ed_workspace_status_mouse_item(workspace_, icon1, interted);
|
||||
ed_workspace_status_mouse_item(workspace_, icon2, interted);
|
||||
ed_workspace_status_text_item(workspace_, text);
|
||||
}
|
||||
|
||||
void WorkspaceStatus::opmodal(std::string text, wmOperatorType *ot, int propvalue, bool inverted)
|
||||
{
|
||||
wmKeyMap *keymap = WM_keymap_active(wm_, ot->modalkeymap);
|
||||
if (keymap) {
|
||||
const wmKeyMapItem *kmi = WM_modalkeymap_find_propvalue(keymap, propvalue);
|
||||
if (kmi) {
|
||||
int icon = UI_icon_from_event_type(kmi->type, kmi->val);
|
||||
if (!ELEM(kmi->shift, KM_NOTHING, KM_ANY)) {
|
||||
ed_workspace_status_item(workspace_, {}, ICON_EVENT_SHIFT, 0.0f, inverted);
|
||||
}
|
||||
if (!ELEM(kmi->ctrl, KM_NOTHING, KM_ANY)) {
|
||||
ed_workspace_status_item(workspace_, {}, ICON_EVENT_CTRL, 0.0f, inverted);
|
||||
}
|
||||
if (!ELEM(kmi->alt, KM_NOTHING, KM_ANY)) {
|
||||
ed_workspace_status_item(workspace_, {}, ICON_EVENT_ALT, 0.0f, inverted);
|
||||
}
|
||||
if (!ELEM(kmi->oskey, KM_NOTHING, KM_ANY)) {
|
||||
ed_workspace_status_item(workspace_, {}, ICON_EVENT_OS, 0.0f, inverted);
|
||||
}
|
||||
if (kmi->val == KM_DBL_CLICK) {
|
||||
ed_workspace_status_item(workspace_, "2" BLI_STR_UTF8_MULTIPLICATION_SIGN, ICON_NONE);
|
||||
ed_workspace_status_space(workspace_, -0.7f);
|
||||
}
|
||||
ed_workspace_status_mouse_item(workspace_, icon, inverted);
|
||||
ed_workspace_status_text_item(workspace_, text);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ED_workspace_status_text(bContext *C, const char *str)
|
||||
{
|
||||
WorkspaceStatus status(C);
|
||||
status.item(str ? str : "", ICON_NONE);
|
||||
}
|
||||
|
||||
/* ************************************************************ */
|
||||
|
||||
static void area_azone_init(wmWindow *win, const bScreen *screen, ScrArea *area)
|
||||
|
|
|
@ -14,6 +14,15 @@
|
|||
#include "DNA_asset_types.h"
|
||||
#include "DNA_viewer_path_types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
namespace blender::bke {
|
||||
struct WorkSpaceRuntime;
|
||||
}
|
||||
using WorkSpaceRuntimeHandle = blender::bke::WorkSpaceRuntime;
|
||||
#else
|
||||
typedef struct WorkSpaceRuntimeHandle WorkSpaceRuntimeHandle;
|
||||
#endif
|
||||
|
||||
/** #bToolRef_Runtime.flag */
|
||||
enum {
|
||||
/**
|
||||
|
@ -137,7 +146,7 @@ typedef struct WorkSpace {
|
|||
int order;
|
||||
|
||||
/** Info text from modal operators (runtime). */
|
||||
char *status_text;
|
||||
WorkSpaceRuntimeHandle *runtime;
|
||||
|
||||
/** Workspace-wide active asset library, for asset UIs to use (e.g. asset view UI template). The
|
||||
* Asset Browser has its own and doesn't use this. */
|
||||
|
|
Loading…
Reference in New Issue
Don't use pointers, use
blender::Vector<WorkSpaceStatusItem>
.Don't do unnecessary
= {}
, that's automatic.