UI: fix errors in screen edge drawing, moving and region hiding.

Summary:
- Fixes an off-by-one error in screen_test_scale() which causes the areas and
  regions to draw one pixel bigger on the right and top side of the window,
  therefor hiding one line of pixels.
- Fixes an off-by-one error in rct_fits() which causes regions to incorrectly
  hide even though it would fit inside the area.
- Correctly set the limits for the screen edge move operator so it will always
  go up to AREAMINX and headery.
- Change screen_find_active_scredge() so it doesn't show the arrows cursor on
  the screen edges along the window border.

The import thing to understand is how integer rects are used in this part of the
code. They are constructed as a lower left and top right point and are INCLUSIVE.
Meaning that if you have a rect's xmin = 10 and xmax = 30 then the total number
of pixels is 21. So to get the size of a rect you have to do xmax - xmin + 1,
which is easy to forget and result in off-by-one errors.

Reviewed By: brecht

Differential Revision: http://developer.blender.org/D41
This commit is contained in:
Anthony Edlin 2013-11-25 13:40:58 +01:00 committed by Brecht Van Lommel
parent ab9822eff8
commit e626998a26
Notes: blender-bot 2023-02-14 11:33:22 +01:00
Referenced by commit a55b00b53b, Fix a number of small errors in area coordinate handling
Referenced by commit 48ad67ef82, Cleanup: Add/use function to get area width/height from area vertices
Referenced by issue #38463, Main window won't resize
Referenced by issue #37617, "Add plane" now actually adds a 2*2 grid!
4 changed files with 116 additions and 56 deletions

View File

@ -859,10 +859,10 @@ static void region_azone_add(ScrArea *sa, ARegion *ar, int alignment)
static int rct_fits(rcti *rect, char dir, int size)
{
if (dir == 'h') {
return BLI_rcti_size_x(rect) - size;
return BLI_rcti_size_x(rect) + 1 - size;
}
else { /* 'v' */
return BLI_rcti_size_y(rect) - size;
return BLI_rcti_size_y(rect) + 1 - size;
}
}
@ -1157,16 +1157,16 @@ static void region_rect_recursive(wmWindow *win, ScrArea *sa, ARegion *ar, rcti
static void area_calc_totrct(ScrArea *sa, int sizex, int sizey)
{
short rt = U.pixelsize > 1.0f ? 1 : 0;
short rt = (short) U.pixelsize;
if (sa->v1->vec.x > 0) sa->totrct.xmin = sa->v1->vec.x + 1 + rt;
if (sa->v1->vec.x > 0) sa->totrct.xmin = sa->v1->vec.x + rt;
else sa->totrct.xmin = sa->v1->vec.x;
if (sa->v4->vec.x < sizex - 1) sa->totrct.xmax = sa->v4->vec.x - 1 - rt;
if (sa->v4->vec.x < sizex - 1) sa->totrct.xmax = sa->v4->vec.x - rt;
else sa->totrct.xmax = sa->v4->vec.x;
if (sa->v1->vec.y > 0) sa->totrct.ymin = sa->v1->vec.y + 1 + rt;
if (sa->v1->vec.y > 0) sa->totrct.ymin = sa->v1->vec.y + rt;
else sa->totrct.ymin = sa->v1->vec.y;
if (sa->v2->vec.y < sizey - 1) sa->totrct.ymax = sa->v2->vec.y - 1 - rt;
if (sa->v2->vec.y < sizey - 1) sa->totrct.ymax = sa->v2->vec.y - rt;
else sa->totrct.ymax = sa->v2->vec.y;
/* for speedup */

View File

@ -264,7 +264,8 @@ int scredge_is_horizontal(ScrEdge *se)
return (se->v1->vec.y == se->v2->vec.y);
}
ScrEdge *screen_find_active_scredge(bScreen *sc, int mx, int my)
/* need win size to make sure not to include edges along screen edge */
ScrEdge *screen_find_active_scredge(bScreen *sc, int winsizex, int winsizey, int mx, int my)
{
ScrEdge *se;
int safety = U.widget_unit / 10;
@ -273,20 +274,24 @@ ScrEdge *screen_find_active_scredge(bScreen *sc, int mx, int my)
for (se = sc->edgebase.first; se; se = se->next) {
if (scredge_is_horizontal(se)) {
short min, max;
min = MIN2(se->v1->vec.x, se->v2->vec.x);
max = MAX2(se->v1->vec.x, se->v2->vec.x);
if (abs(my - se->v1->vec.y) <= safety && mx >= min && mx <= max)
return se;
if (se->v1->vec.y > 0 && se->v1->vec.y < winsizey - 1) {
short min, max;
min = MIN2(se->v1->vec.x, se->v2->vec.x);
max = MAX2(se->v1->vec.x, se->v2->vec.x);
if (abs(my - se->v1->vec.y) <= safety && mx >= min && mx <= max)
return se;
}
}
else {
short min, max;
min = MIN2(se->v1->vec.y, se->v2->vec.y);
max = MAX2(se->v1->vec.y, se->v2->vec.y);
if (abs(mx - se->v1->vec.x) <= safety && my >= min && my <= max)
return se;
if (se->v1->vec.x > 0 && se->v1->vec.x < winsizex - 1) {
short min, max;
min = MIN2(se->v1->vec.y, se->v2->vec.y);
max = MAX2(se->v1->vec.y, se->v2->vec.y);
if (abs(mx - se->v1->vec.x) <= safety && my >= min && my <= max)
return se;
}
}
}
@ -677,8 +682,8 @@ static void screen_test_scale(bScreen *sc, int winsizex, int winsizey)
sv->vec.y -= min[1];
}
sizex = max[0] - min[0];
sizey = max[1] - min[1];
sizex = max[0] - min[0] + 1;
sizey = max[1] - min[1] + 1;
if (sizex != winsizex || sizey != winsizey) {
facx = winsizex;
@ -695,14 +700,14 @@ static void screen_test_scale(bScreen *sc, int winsizex, int winsizey)
//sv->vec.x += AREAGRID - 1;
//sv->vec.x -= (sv->vec.x % AREAGRID);
CLAMP(sv->vec.x, 0, winsizex);
CLAMP(sv->vec.x, 0, winsizex - 1);
tempf = ((float)sv->vec.y) * facy;
sv->vec.y = (short)(tempf + 0.5f);
//sv->vec.y += AREAGRID - 1;
//sv->vec.y -= (sv->vec.y % AREAGRID);
CLAMP(sv->vec.y, 0, winsizey);
CLAMP(sv->vec.y, 0, winsizey - 1);
}
}
@ -711,9 +716,15 @@ static void screen_test_scale(bScreen *sc, int winsizex, int winsizey)
/* make each window at least ED_area_headersize() high */
for (sa = sc->areabase.first; sa; sa = sa->next) {
int headery = ED_area_headersize() + U.pixelsize;
int headery = ED_area_headersize();
if (sa->v1->vec.y + headery > sa->v2->vec.y) {
/* adjust headery if verts are along the edge of window */
if (sa->v1->vec.y > 0)
headery += U.pixelsize;
if (sa->v2->vec.y < winsizey - 1)
headery += U.pixelsize;
if (sa->v2->vec.y - sa->v1->vec.y + 1 < headery) {
/* lower edge */
ScrEdge *se = screen_findedge(sc, sa->v4, sa->v1);
if (se && sa->v1 != sa->v2) {
@ -722,7 +733,7 @@ static void screen_test_scale(bScreen *sc, int winsizex, int winsizey)
select_connected_scredge(sc, se);
/* all selected vertices get the right offset */
yval = sa->v2->vec.y - headery;
yval = sa->v2->vec.y - headery + 1;
sv = sc->vertbase.first;
while (sv) {
/* if is a collapsed area */
@ -947,7 +958,7 @@ static void drawscredge_area(ScrArea *sa, int sizex, int sizey, int center)
if (U.pixelsize > 1.0f) {
glColor3ub(0x50, 0x50, 0x50);
glLineWidth(1.5f * U.pixelsize);
glLineWidth((2.0f * U.pixelsize) - 1);
drawscredge_area_draw(sizex, sizey, x1, y1, x2, y2, 0);
glLineWidth(1.0f);
}
@ -1247,6 +1258,8 @@ static void screen_cursor_set(wmWindow *win, wmEvent *event)
{
AZone *az = NULL;
ScrArea *sa;
int winsizex = WM_window_pixels_x(win);
int winsizey = WM_window_pixels_y(win);
for (sa = win->screen->areabase.first; sa; sa = sa->next)
if ((az = is_in_area_actionzone(sa, &event->x)))
@ -1263,7 +1276,7 @@ static void screen_cursor_set(wmWindow *win, wmEvent *event)
}
}
else {
ScrEdge *actedge = screen_find_active_scredge(win->screen, event->x, event->y);
ScrEdge *actedge = screen_find_active_scredge(win->screen, winsizex, winsizey, event->x, event->y);
if (actedge) {
if (scredge_is_horizontal(actedge))

View File

@ -53,7 +53,7 @@ void removedouble_scrverts(bScreen *sc);
void removedouble_scredges(bScreen *sc);
void removenotused_scredges(bScreen *sc);
int scredge_is_horizontal(ScrEdge *se);
ScrEdge *screen_find_active_scredge(bScreen *sc, int mx, int my);
ScrEdge *screen_find_active_scredge(bScreen *sc, int winsizex, int winsizey, int mx, int my);
struct AZone *is_in_area_actionzone(ScrArea *sa, const int xy[2]);

View File

@ -695,8 +695,12 @@ static int actionzone_invoke(bContext *C, wmOperator *op, const wmEvent *event)
static int actionzone_modal(bContext *C, wmOperator *op, const wmEvent *event)
{
wmWindow *win = CTX_wm_window(C);
bScreen *sc = CTX_wm_screen(C);
sActionzoneData *sad = op->customdata;
int winsizex = WM_window_pixels_x(win);
int winsizey = WM_window_pixels_y(win);
switch (event->type) {
case MOUSEMOVE:
{
@ -719,7 +723,7 @@ static int actionzone_modal(bContext *C, wmOperator *op, const wmEvent *event)
/* once we drag outside the actionzone, register a gesture
* check we're not on an edge so join finds the other area */
is_gesture = ((is_in_area_actionzone(sad->sa1, &event->x) != sad->az) &&
(screen_find_active_scredge(CTX_wm_screen(C), event->x, event->y) == NULL));
(screen_find_active_scredge(sc, winsizex, winsizey, event->x, event->y) == NULL));
}
else {
const int delta_min = 1;
@ -988,18 +992,28 @@ typedef struct sAreaMoveData {
char dir;
} sAreaMoveData;
/* helper call to move area-edge, sets limits */
static void area_move_set_limits(bScreen *sc, int dir, int *bigger, int *smaller)
/* helper call to move area-edge, sets limits
* need window size in order to get correct limits */
static void area_move_set_limits(bScreen *sc, int dir, int winsizex, int winsizey, int *bigger, int *smaller)
{
ScrArea *sa;
int areaminy = ED_area_headersize() + U.pixelsize; // pixelsize is used as area divider
int areaminy = ED_area_headersize();
int areamin;
/* we check all areas and test for free space with MINSIZE */
*bigger = *smaller = 100000;
for (sa = sc->areabase.first; sa; sa = sa->next) {
if (dir == 'h') {
int y1 = sa->v2->vec.y - sa->v1->vec.y - areaminy;
int y1;
areamin = areaminy;
if (sa->v1->vec.y > 0)
areamin += U.pixelsize;
if (sa->v2->vec.y < winsizey - 1)
areamin += U.pixelsize;
y1 = sa->v2->vec.y - sa->v1->vec.y + 1 - areamin;
/* if top or down edge selected, test height */
if (sa->v1->editflag && sa->v4->editflag)
@ -1008,7 +1022,15 @@ static void area_move_set_limits(bScreen *sc, int dir, int *bigger, int *smaller
*smaller = min_ii(*smaller, y1);
}
else {
int x1 = sa->v4->vec.x - sa->v1->vec.x - AREAMINX;
int x1;
areamin = AREAMINX;
if(sa->v1->vec.x > 0)
areamin += U.pixelsize;
if(sa->v4->vec.x < winsizex - 1)
areamin += U.pixelsize;
x1 = sa->v4->vec.x - sa->v1->vec.x + 1 - areamin;
/* if left or right edge selected, test width */
if (sa->v1->editflag && sa->v2->editflag)
@ -1024,9 +1046,12 @@ static void area_move_set_limits(bScreen *sc, int dir, int *bigger, int *smaller
static int area_move_init(bContext *C, wmOperator *op)
{
bScreen *sc = CTX_wm_screen(C);
wmWindow *win = CTX_wm_window(C);
ScrEdge *actedge;
sAreaMoveData *md;
ScrVert *v1;
int winsizex = WM_window_pixels_x(win);
int winsizey = WM_window_pixels_y(win);
int x, y;
/* required properties */
@ -1034,7 +1059,7 @@ static int area_move_init(bContext *C, wmOperator *op)
y = RNA_int_get(op->ptr, "y");
/* setup */
actedge = screen_find_active_scredge(sc, x, y);
actedge = screen_find_active_scredge(sc, winsizex, winsizey, x, y);
if (actedge == NULL) return 0;
md = MEM_callocN(sizeof(sAreaMoveData), "sAreaMoveData");
@ -1049,7 +1074,7 @@ static int area_move_init(bContext *C, wmOperator *op)
for (v1 = sc->vertbase.first; v1; v1 = v1->next)
v1->editflag = v1->flag;
area_move_set_limits(sc, md->dir, &md->bigger, &md->smaller);
area_move_set_limits(sc, md->dir, winsizex, winsizey, &md->bigger, &md->smaller);
return 1;
}
@ -1061,7 +1086,8 @@ static void area_move_apply_do(bContext *C, int origval, int delta, int dir, int
bScreen *sc = CTX_wm_screen(C);
ScrVert *v1;
ScrArea *sa;
int areaminy = ED_area_headersize() + 1;
int doredraw = 0;
int oldval;
delta = CLAMPIS(delta, -smaller, bigger);
@ -1069,28 +1095,39 @@ static void area_move_apply_do(bContext *C, int origval, int delta, int dir, int
if (v1->editflag) {
/* that way a nice AREAGRID */
if ((dir == 'v') && v1->vec.x > 0 && v1->vec.x < WM_window_pixels_x(win) - 1) {
oldval = v1->vec.x;
v1->vec.x = origval + delta;
if (delta != bigger && delta != -smaller) v1->vec.x -= (v1->vec.x % AREAGRID);
if (delta != bigger && delta != -smaller) {
v1->vec.x -= (v1->vec.x % AREAGRID);
v1->vec.x = CLAMPIS(v1->vec.x, origval - smaller, origval + bigger);
}
if (oldval != v1->vec.x)
doredraw = 1;
}
if ((dir == 'h') && v1->vec.y > 0 && v1->vec.y < WM_window_pixels_y(win) - 1) {
oldval = v1->vec.y;
v1->vec.y = origval + delta;
v1->vec.y += AREAGRID - 1;
v1->vec.y -= (v1->vec.y % AREAGRID);
/* prevent too small top header */
if (v1->vec.y > WM_window_pixels_y(win) - areaminy)
v1->vec.y = WM_window_pixels_y(win) - areaminy;
if (delta != bigger && delta != smaller) {
v1->vec.y -= (v1->vec.y % AREAGRID);
v1->vec.y = CLAMPIS(v1->vec.y, origval - smaller, origval + bigger);
}
if (oldval != v1->vec.y)
doredraw = 1;
}
}
}
for (sa = sc->areabase.first; sa; sa = sa->next) {
if (sa->v1->editflag || sa->v2->editflag || sa->v3->editflag || sa->v4->editflag)
ED_area_tag_redraw(sa);
}
/* only redraw if we actually moved a screen vert, for AREAGRID */
if (doredraw) {
for (sa = sc->areabase.first; sa; sa = sa->next) {
if (sa->v1->editflag || sa->v2->editflag || sa->v3->editflag || sa->v4->editflag)
ED_area_tag_redraw(sa);
}
WM_event_add_notifier(C, NC_SCREEN | NA_EDITED, NULL); /* redraw everything */
WM_event_add_notifier(C, NC_SCREEN | NA_EDITED, NULL); /* redraw everything */
}
}
static void area_move_apply(bContext *C, wmOperator *op)
@ -1410,11 +1447,15 @@ static void area_split_exit(bContext *C, wmOperator *op)
/* UI callback, adds new handler */
static int area_split_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
wmWindow *win = CTX_wm_window(C);
bScreen *sc = CTX_wm_screen(C);
sAreaSplitData *sd;
int winsizex = WM_window_pixels_x(win);
int winsizey = WM_window_pixels_y(win);
int dir;
/* no full window splitting allowed */
if (CTX_wm_screen(C)->full != SCREENNORMAL)
if (sc->full != SCREENNORMAL)
return OPERATOR_CANCELLED;
if (event->type == EVT_ACTIONZONE_AREA) {
@ -1463,7 +1504,7 @@ static int area_split_invoke(bContext *C, wmOperator *op, const wmEvent *event)
else
y = event->x;
actedge = screen_find_active_scredge(CTX_wm_screen(C), x, y);
actedge = screen_find_active_scredge(sc, winsizex, winsizey, x, y);
if (actedge == NULL)
return OPERATOR_CANCELLED;
@ -1486,7 +1527,7 @@ static int area_split_invoke(bContext *C, wmOperator *op, const wmEvent *event)
/* do the split */
if (area_split_apply(C, op)) {
area_move_set_limits(CTX_wm_screen(C), dir, &sd->bigger, &sd->smaller);
area_move_set_limits(sc, dir, winsizex, winsizey, &sd->bigger, &sd->smaller);
/* add temp handler for edge move or cancel */
WM_event_add_modal_handler(C, op);
@ -2574,10 +2615,16 @@ static void SCREEN_OT_area_join(wmOperatorType *ot)
static int screen_area_options_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
wmWindow *win = CTX_wm_window(C);
bScreen *sc = CTX_wm_screen(C);
uiPopupMenu *pup;
uiLayout *layout;
PointerRNA ptr1, ptr2;
ScrEdge *actedge = screen_find_active_scredge(CTX_wm_screen(C), event->x, event->y);
ScrEdge *actedge;
int winsizex = WM_window_pixels_x(win);
int winsizey = WM_window_pixels_y(win);
actedge = screen_find_active_scredge(sc, winsizex, winsizey, event->x, event->y);
if (actedge == NULL) return OPERATOR_CANCELLED;