diff options
author | Julian Eisel <eiseljulian@gmail.com> | 2018-05-07 02:31:18 +0300 |
---|---|---|
committer | Julian Eisel <eiseljulian@gmail.com> | 2018-05-07 12:42:12 +0300 |
commit | 51efeb683451d896531baac949de65043edff0b6 (patch) | |
tree | a36b7c037205b77a78a0b0266a7e9ca4571a0a5a /source | |
parent | 4ec467d3b52744ba2aa4f938973efa27202b23ee (diff) |
UI: Hide & shrink scroll-bars based on cursor position
Scroll-bars are now hidden unless the cursor approaches them, in which case they
smoothly grow and become more & more visible. Note that since 0d309144020168e55,
scroll-bars are drawn on top of editor contents. There's no more jumping of
buttons when scroll-bars appear.
Technical notes:
* AZones are used to adjust scrollbars based on mouse movements
We may want to support screen level AZones if we want scrollbars to also
smoothly appear when approaching them from a different area.
I also plan to make further changes to AZones to clean up stuff a bit.
* Had to move AZone handling to a post ARegion init stage, since we need the
updated View2D data from there.
* View2D masks and scroller rectangles are now updated on every redraw. It's
cheap to do that though.
Diffstat (limited to 'source')
-rw-r--r-- | source/blender/blenloader/intern/readfile.c | 2 | ||||
-rw-r--r-- | source/blender/editors/include/ED_screen_types.h | 26 | ||||
-rw-r--r-- | source/blender/editors/include/UI_view2d.h | 2 | ||||
-rw-r--r-- | source/blender/editors/interface/view2d.c | 37 | ||||
-rw-r--r-- | source/blender/editors/screen/area.c | 136 | ||||
-rw-r--r-- | source/blender/editors/screen/screen_ops.c | 56 | ||||
-rw-r--r-- | source/blender/makesdna/DNA_view2d_types.h | 5 |
7 files changed, 214 insertions, 50 deletions
diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c index a042e42775b..96e3f4704d9 100644 --- a/source/blender/blenloader/intern/readfile.c +++ b/source/blender/blenloader/intern/readfile.c @@ -6382,6 +6382,8 @@ static void direct_link_region(FileData *fd, ARegion *ar, int spacetype) ar->v2d.tab_num = 0; ar->v2d.tab_cur = 0; ar->v2d.sms = NULL; + ar->v2d.alpha_hor = ar->v2d.alpha_vert = 0; + ar->v2d.size_hor = ar->v2d.size_vert = 0; BLI_listbase_clear(&ar->panels_category); BLI_listbase_clear(&ar->handlers); BLI_listbase_clear(&ar->uiblocks); diff --git a/source/blender/editors/include/ED_screen_types.h b/source/blender/editors/include/ED_screen_types.h index 1c41b14a874..0fed5eb03fd 100644 --- a/source/blender/editors/include/ED_screen_types.h +++ b/source/blender/editors/include/ED_screen_types.h @@ -83,13 +83,22 @@ typedef enum { AE_BOTTOM_TO_TOPLEFT /* Region located at the top, _bottom_ edge is action zone. Region minimized to the top left */ } AZEdge; +typedef enum { + AZ_SCROLL_VERT, + AZ_SCROLL_HOR, +} AZScrollDirection; + /* for editing areas/regions */ typedef struct AZone { struct AZone *next, *prev; ARegion *ar; int type; - /* region-azone, which of the edges (only for AZONE_REGION) */ - AZEdge edge; + + union { + /* region-azone, which of the edges (only for AZONE_REGION) */ + AZEdge edge; + AZScrollDirection direction; + }; /* for draw */ short x1, y1, x2, y2; /* for clip */ @@ -99,8 +108,15 @@ typedef struct AZone { } AZone; /* actionzone type */ -#define AZONE_AREA 1 /* corner widgets for splitting areas */ -#define AZONE_REGION 2 /* when a region is collapsed, draw a handle to expose */ -#define AZONE_FULLSCREEN 3 /* when in editor fullscreen draw a corner to go to normal mode */ +enum { + /* corner widgets for splitting areas */ + AZONE_AREA = 1, + /* when a region is collapsed, draw a handle to expose */ + AZONE_REGION, + /* when in editor fullscreen draw a corner to go to normal mode */ + AZONE_FULLSCREEN, + /* Hotspot azone around scrollbars to show/hide them. */ + AZONE_REGION_SCROLL, +}; #endif /* __ED_SCREEN_TYPES_H__ */ diff --git a/source/blender/editors/include/UI_view2d.h b/source/blender/editors/include/UI_view2d.h index a19b2f05e2e..1f106e3f08d 100644 --- a/source/blender/editors/include/UI_view2d.h +++ b/source/blender/editors/include/UI_view2d.h @@ -103,6 +103,8 @@ enum eView2D_Gridlines { /* ------ Defines for Scrollers ----- */ /* scroller area */ +#define V2D_SCROLL_HEIGHT_MIN (0.25f * U.widget_unit) +#define V2D_SCROLL_WIDTH_MIN (0.25f * U.widget_unit) #define V2D_SCROLL_HEIGHT (0.45f * U.widget_unit) #define V2D_SCROLL_WIDTH (0.45f * U.widget_unit) /* For scrollers with scale markings (text written onto them) */ diff --git a/source/blender/editors/interface/view2d.c b/source/blender/editors/interface/view2d.c index 8342387f956..2a687118eaa 100644 --- a/source/blender/editors/interface/view2d.c +++ b/source/blender/editors/interface/view2d.c @@ -152,10 +152,11 @@ static void view2d_masks(View2D *v2d, bool check_scrollers) * - if they overlap, they must not occupy the corners (which are reserved for other widgets) */ if (scroll) { - const int scroll_width = (v2d->scroll & V2D_SCROLL_SCALE_VERTICAL) ? - V2D_SCROLL_WIDTH_TEXT : V2D_SCROLL_WIDTH; - const int scroll_height = (v2d->scroll & V2D_SCROLL_SCALE_HORIZONTAL) ? - V2D_SCROLL_HEIGHT_TEXT : V2D_SCROLL_HEIGHT; + int scroll_width = (v2d->scroll & V2D_SCROLL_SCALE_VERTICAL) ? V2D_SCROLL_WIDTH_TEXT : v2d->size_vert; + int scroll_height = (v2d->scroll & V2D_SCROLL_SCALE_HORIZONTAL) ? V2D_SCROLL_HEIGHT_TEXT : v2d->size_hor; + + CLAMP_MIN(scroll_width, V2D_SCROLL_WIDTH_MIN); + CLAMP_MIN(scroll_height, V2D_SCROLL_HEIGHT_MIN); /* vertical scroller */ if (scroll & V2D_SCROLL_LEFT) { @@ -357,6 +358,11 @@ void UI_view2d_region_reinit(View2D *v2d, short type, int winx, int winy) /* set masks (always do), but leave scroller scheck to totrect_set */ view2d_masks(v2d, 0); + if (do_init) { + /* Visible by default. */ + v2d->alpha_hor = v2d->alpha_vert = 255; + } + /* set 'tot' rect before setting cur? */ /* XXX confusing stuff here still - I made this function not check scroller hide - that happens in totrect_set */ if (tot_changed) @@ -1641,6 +1647,9 @@ View2DScrollers *UI_view2d_scrollers_calc( /* scrollers is allocated here... */ scrollers = MEM_callocN(sizeof(View2DScrollers), "View2DScrollers"); + /* Always update before drawing (for dynamically sized scrollers). */ + view2d_masks(v2d, false); + vert = v2d->vert; hor = v2d->hor; @@ -1804,9 +1813,11 @@ static void scroll_printstr(Scene *scene, float x, float y, float val, int power /* Draw scrollbars in the given 2d-region */ void UI_view2d_scrollers_draw(const bContext *C, View2D *v2d, View2DScrollers *vs) { + bTheme *btheme = UI_GetTheme(); Scene *scene = CTX_data_scene(C); rcti vert, hor; - int scroll = view2d_scroll_mapped(v2d->scroll); + const int scroll = view2d_scroll_mapped(v2d->scroll); + const char emboss_alpha = btheme->tui.widget_emboss[3]; unsigned char scrollers_back_color[4]; /* Color for scrollbar backs */ @@ -1818,8 +1829,8 @@ void UI_view2d_scrollers_draw(const bContext *C, View2D *v2d, View2DScrollers *v /* horizontal scrollbar */ if (scroll & V2D_SCROLL_HORIZONTAL) { - bTheme *btheme = UI_GetTheme(); uiWidgetColors wcol = btheme->tui.wcol_scroll; + const float alpha_fac = v2d->alpha_hor / 255.0f; rcti slider; int state; @@ -1830,6 +1841,11 @@ void UI_view2d_scrollers_draw(const bContext *C, View2D *v2d, View2DScrollers *v state = (v2d->scroll_ui & V2D_SCROLL_H_ACTIVE) ? UI_SCROLL_PRESSED : 0; + wcol.inner[3] *= alpha_fac; + wcol.item[3] *= alpha_fac; + wcol.outline[3] *= alpha_fac; + btheme->tui.widget_emboss[3] *= alpha_fac; /* will be reset later */ + /* show zoom handles if: * - zooming on x-axis is allowed (no scroll otherwise) * - slider bubble is large enough (no overdraw confusion) @@ -1916,9 +1932,9 @@ void UI_view2d_scrollers_draw(const bContext *C, View2D *v2d, View2DScrollers *v /* vertical scrollbar */ if (scroll & V2D_SCROLL_VERTICAL) { - bTheme *btheme = UI_GetTheme(); uiWidgetColors wcol = btheme->tui.wcol_scroll; rcti slider; + const float alpha_fac = v2d->alpha_vert / 255.0f; int state; slider.xmin = vert.xmin; @@ -1928,6 +1944,11 @@ void UI_view2d_scrollers_draw(const bContext *C, View2D *v2d, View2DScrollers *v state = (v2d->scroll_ui & V2D_SCROLL_V_ACTIVE) ? UI_SCROLL_PRESSED : 0; + wcol.inner[3] *= alpha_fac; + wcol.item[3] *= alpha_fac; + wcol.outline[3] *= alpha_fac; + btheme->tui.widget_emboss[3] *= alpha_fac; /* will be reset later */ + /* show zoom handles if: * - zooming on y-axis is allowed (no scroll otherwise) * - slider bubble is large enough (no overdraw confusion) @@ -1990,6 +2011,8 @@ void UI_view2d_scrollers_draw(const bContext *C, View2D *v2d, View2DScrollers *v } } + /* Was changed above, so reset. */ + btheme->tui.widget_emboss[3] = emboss_alpha; } /* free temporary memory used for drawing scrollers */ diff --git a/source/blender/editors/screen/area.c b/source/blender/editors/screen/area.c index 58dbebd2b75..e86655ab2cf 100644 --- a/source/blender/editors/screen/area.c +++ b/source/blender/editors/screen/area.c @@ -203,6 +203,24 @@ void ED_area_azones_update(ScrArea *sa, const int mouse_xy[2]) break; } } + else if (az->type == AZONE_REGION_SCROLL) { + /* only if mouse is not hovering the azone */ + if (BLI_rcti_isect_pt_v(&az->rect, mouse_xy) == false) { + View2D *v2d = &az->ar->v2d; + + if (az->direction == AZ_SCROLL_VERT) { + az->alpha = v2d->alpha_vert = 0; + changed = true; + } + else if (az->direction == AZ_SCROLL_HOR) { + az->alpha = v2d->alpha_hor = 0; + changed = true; + } + else { + BLI_assert(0); + } + } + } } if (changed) { @@ -464,6 +482,12 @@ static void region_draw_azones(ScrArea *sa, ARegion *ar) area_azone_tag_update(sa); } } + else if (az->type == AZONE_REGION_SCROLL) { + if (az->alpha != 0.0f) { + area_azone_tag_update(sa); + } + /* Don't draw this azone. */ + } } } @@ -1043,7 +1067,7 @@ static void region_azone_tria(ScrArea *sa, AZone *az, ARegion *ar) } -static void region_azone_initialize(ScrArea *sa, ARegion *ar, AZEdge edge, const bool is_fullscreen) +static void region_azone_edge_initialize(ScrArea *sa, ARegion *ar, AZEdge edge, const bool is_fullscreen) { AZone *az = NULL; const bool is_hidden = (ar->flag & (RGN_FLAG_HIDDEN | RGN_FLAG_TOO_SMALL)) == 0; @@ -1074,21 +1098,76 @@ static void region_azone_initialize(ScrArea *sa, ARegion *ar, AZEdge edge, const } +static void region_azone_scrollbar_initialize(ScrArea *sa, ARegion *ar, AZScrollDirection direction) +{ + rcti scroller_vert = (direction == AZ_SCROLL_VERT) ? ar->v2d.vert : ar->v2d.hor; + AZone *az = MEM_callocN(sizeof(*az), __func__); + + BLI_addtail(&sa->actionzones, az); + az->type = AZONE_REGION_SCROLL; + az->ar = ar; + az->direction = direction; + + if (direction == AZ_SCROLL_VERT) { + az->ar->v2d.alpha_vert = 0; + } + else if (direction == AZ_SCROLL_HOR) { + az->ar->v2d.alpha_hor = 0; + } + + BLI_rcti_translate(&scroller_vert, ar->winrct.xmin, ar->winrct.ymin); + az->x1 = scroller_vert.xmin - AZONEFADEIN; + az->y1 = scroller_vert.ymin - AZONEFADEIN; + az->x2 = scroller_vert.xmax + AZONEFADEIN; + az->y2 = scroller_vert.ymax + AZONEFADEIN; + + BLI_rcti_init(&az->rect, az->x1, az->x2, az->y1, az->y2); +} + +static void region_azones_scrollbars_initialize(ScrArea *sa, ARegion *ar) +{ + const View2D *v2d = &ar->v2d; + + if ((v2d->scroll & V2D_SCROLL_VERTICAL) && ((v2d->scroll & V2D_SCROLL_SCALE_VERTICAL) == 0)) { + region_azone_scrollbar_initialize(sa, ar, AZ_SCROLL_VERT); + } + if ((v2d->scroll & V2D_SCROLL_HORIZONTAL) && ((v2d->scroll & V2D_SCROLL_SCALE_HORIZONTAL) == 0)) { + region_azone_scrollbar_initialize(sa, ar, AZ_SCROLL_HOR); + } +} + /* *************************************************************** */ -static void region_azone_add(ScrArea *sa, ARegion *ar, const int alignment, const bool is_fullscreen) +static void region_azones_add(const bScreen *screen, ScrArea *sa, ARegion *ar, const int alignment) { + const bool is_fullscreen = screen->state == SCREENFULL; + /* edge code (t b l r) is along which area edge azone will be drawn */ - - if (alignment == RGN_ALIGN_TOP) - region_azone_initialize(sa, ar, AE_BOTTOM_TO_TOPLEFT, is_fullscreen); + + if (ar->regiontype == RGN_TYPE_HEADER && ar->winy + 6 > sa->winy) { + /* The logic for this is: when the header takes up the full area, + * disallow hiding it to view the main window. + * + * Without this, you can drag down the file selectors header and hide it + * by accident very easily (highly annoying!), the value 6 is arbitrary + * but accounts for small common rounding problems when scaling the UI, + * must be minimum '4' */ + } + else if (alignment == RGN_ALIGN_TOP) + region_azone_edge_initialize(sa, ar, AE_BOTTOM_TO_TOPLEFT, is_fullscreen); else if (alignment == RGN_ALIGN_BOTTOM) - region_azone_initialize(sa, ar, AE_TOP_TO_BOTTOMRIGHT, is_fullscreen); + region_azone_edge_initialize(sa, ar, AE_TOP_TO_BOTTOMRIGHT, is_fullscreen); else if (alignment == RGN_ALIGN_RIGHT) - region_azone_initialize(sa, ar, AE_LEFT_TO_TOPRIGHT, is_fullscreen); + region_azone_edge_initialize(sa, ar, AE_LEFT_TO_TOPRIGHT, is_fullscreen); else if (alignment == RGN_ALIGN_LEFT) - region_azone_initialize(sa, ar, AE_RIGHT_TO_TOPLEFT, is_fullscreen); + region_azone_edge_initialize(sa, ar, AE_RIGHT_TO_TOPLEFT, is_fullscreen); + + if (is_fullscreen) { + fullscreen_azone_initialize(sa, ar); + } + + region_azones_scrollbars_initialize(sa, ar); } /* dir is direction to check, not the splitting edge direction! */ @@ -1193,7 +1272,7 @@ static bool region_is_overlap(ScrArea *sa, ARegion *ar) return 0; } -static void region_rect_recursive(wmWindow *win, ScrArea *sa, ARegion *ar, rcti *remainder, rcti *overlap_remainder, int quad, bool add_azones) +static void region_rect_recursive(wmWindow *win, ScrArea *sa, ARegion *ar, rcti *remainder, rcti *overlap_remainder, int quad) { rcti *remainder_prev = remainder; int prefsizex, prefsizey; @@ -1421,30 +1500,8 @@ static void region_rect_recursive(wmWindow *win, ScrArea *sa, ARegion *ar, rcti if (!ar->overlap) { *overlap_remainder = *remainder; } - - /* in end, add azones, where appropriate */ - if (ar->regiontype == RGN_TYPE_HEADER && ar->winy + 6 > sa->winy) { - /* The logic for this is: when the header takes up the full area, - * disallow hiding it to view the main window. - * - * Without this, you can drag down the file selectors header and hide it - * by accident very easily (highly annoying!), the value 6 is arbitrary - * but accounts for small common rounding problems when scaling the UI, - * must be minimum '4' */ - } - else if (add_azones) { - const bScreen *screen = WM_window_get_active_screen(win); - if (ELEM(screen->state, SCREENNORMAL, SCREENMAXIMIZED)) { - region_azone_add(sa, ar, alignment, false); - } - else { - region_azone_add(sa, ar, alignment, true); - fullscreen_azone_initialize(sa, ar); - } - } - - region_rect_recursive(win, sa, ar->next, remainder, overlap_remainder, quad, add_azones); + region_rect_recursive(win, sa, ar->next, remainder, overlap_remainder, quad); } static void area_calc_totrct(ScrArea *sa, int window_size_x, int window_size_y) @@ -1574,7 +1631,7 @@ void ED_area_update_region_sizes(wmWindowManager *wm, wmWindow *win, ScrArea *ar /* region rect sizes */ rect = area->totrct; overlap_rect = rect; - region_rect_recursive(win, area, area->regionbase.first, &rect, &overlap_rect, 0, false); + region_rect_recursive(win, area, area->regionbase.first, &rect, &overlap_rect, 0); for (ARegion *ar = area->regionbase.first; ar; ar = ar->next) { region_subwindow(ar); @@ -1615,13 +1672,10 @@ void ED_area_initialize(wmWindowManager *wm, wmWindow *win, ScrArea *sa) /* area sizes */ area_calc_totrct(sa, window_size_x, window_size_y); - /* clear all azones, add the area triange widgets */ - area_azone_initialize(win, screen, sa); - /* region rect sizes */ rect = sa->totrct; overlap_rect = rect; - region_rect_recursive(win, sa, sa->regionbase.first, &rect, &overlap_rect, 0, true); + region_rect_recursive(win, sa, sa->regionbase.first, &rect, &overlap_rect, 0); sa->flag &= ~AREA_FLAG_REGION_SIZE_UPDATE; /* default area handlers */ @@ -1629,7 +1683,10 @@ void ED_area_initialize(wmWindowManager *wm, wmWindow *win, ScrArea *sa) /* checks spacedata, adds own handlers */ if (sa->type->init) sa->type->init(wm, sa); - + + /* clear all azones, add the area triange widgets */ + area_azone_initialize(win, screen, sa); + /* region windows, default and own handlers */ for (ar = sa->regionbase.first; ar; ar = ar->next) { region_subwindow(ar); @@ -1646,6 +1703,9 @@ void ED_area_initialize(wmWindowManager *wm, wmWindow *win, ScrArea *sa) /* prevent uiblocks to run */ UI_blocklist_free(NULL, &ar->uiblocks); } + + /* Some AZones use View2D data which is only updated in region init, so call that first! */ + region_azones_add(screen, sa, ar, ar->alignment & ~RGN_SPLIT_PREV); } } diff --git a/source/blender/editors/screen/screen_ops.c b/source/blender/editors/screen/screen_ops.c index 3d18e9cbaee..df0466075ef 100644 --- a/source/blender/editors/screen/screen_ops.c +++ b/source/blender/editors/screen/screen_ops.c @@ -710,6 +710,59 @@ AZone *is_in_area_actionzone(ScrArea *sa, const int xy[2]) ED_area_tag_redraw(sa); break; } + else if (az->type == AZONE_REGION_SCROLL) { + ARegion *ar = az->ar; + View2D *v2d = &ar->v2d; + const short isect_value = UI_view2d_mouse_in_scrollers(ar, v2d, xy[0], xy[1]); + bool redraw = false; + + if (isect_value == 'h') { + if (az->direction == AZ_SCROLL_HOR) { + az->alpha = 1.0f; + v2d->alpha_hor = 255; + v2d->size_hor = V2D_SCROLL_HEIGHT; + redraw = true; + } + } + else if (isect_value == 'v') { + if (az->direction == AZ_SCROLL_VERT) { + az->alpha = 1.0f; + v2d->alpha_vert = 255; + v2d->size_vert = V2D_SCROLL_WIDTH; + redraw = true; + } + } + else { + const int local_xy[2] = {xy[0] - ar->winrct.xmin, xy[1] - ar->winrct.ymin}; + float dist_fac = 0.0f, alpha = 0.0f; + + if (az->direction == AZ_SCROLL_HOR) { + dist_fac = BLI_rcti_length_y(&v2d->hor, local_xy[1]) / AZONEFADEIN; + CLAMP(dist_fac, 0.0f, 1.0f); + alpha = 1.0f - dist_fac; + + v2d->alpha_hor = alpha * 255; + v2d->size_hor = round_fl_to_int(V2D_SCROLL_HEIGHT - + ((V2D_SCROLL_HEIGHT - V2D_SCROLL_HEIGHT_MIN) * dist_fac)); + } + else if (az->direction == AZ_SCROLL_VERT) { + dist_fac = BLI_rcti_length_x(&v2d->vert, local_xy[0]) / AZONEFADEIN; + CLAMP(dist_fac, 0.0f, 1.0f); + alpha = 1.0f - dist_fac; + + v2d->alpha_vert = alpha * 255; + v2d->size_vert = round_fl_to_int(V2D_SCROLL_WIDTH - + ((V2D_SCROLL_WIDTH - V2D_SCROLL_WIDTH_MIN) * dist_fac)); + } + az->alpha = alpha; + redraw = true; + } + + if (redraw) { + ED_area_tag_redraw(sa); + } + /* Don't return! */ + } } } @@ -772,6 +825,9 @@ static int actionzone_invoke(bContext *C, wmOperator *op, const wmEvent *event) actionzone_exit(op); return OPERATOR_FINISHED; } + else if (ELEM(sad->az->type, AZONE_REGION_SCROLL)) { + return OPERATOR_PASS_THROUGH; + } else { /* add modal handler */ WM_event_add_modal_handler(C, op); diff --git a/source/blender/makesdna/DNA_view2d_types.h b/source/blender/makesdna/DNA_view2d_types.h index 3319fed8cdd..a0480aa361b 100644 --- a/source/blender/makesdna/DNA_view2d_types.h +++ b/source/blender/makesdna/DNA_view2d_types.h @@ -64,6 +64,11 @@ typedef struct View2D { int tab_num; /* number of tabs stored */ int tab_cur; /* current tab */ + /* Usually set externally (as in, not in view2d files). */ + char alpha_vert, alpha_hor; /* alpha of vertical and horizontal scrollbars (range is [0, 255]) */ + short size_vert, size_hor; /* Dynamic size for scrollers without scale markers (no V2D_SCROLL_SCALE_FOO) */ + short pad; + /* animated smooth view */ struct SmoothView2DStore *sms; struct wmTimer *smooth_timer; |