Text Editor: smooth scrolling
authorCampbell Barton <ideasman42@gmail.com>
Sun, 24 Nov 2019 08:59:38 +0000 (19:59 +1100)
committerCampbell Barton <ideasman42@gmail.com>
Sun, 24 Nov 2019 09:19:24 +0000 (20:19 +1100)
Add smooth scrolling support for vertical scrolling.

This is only active while scrolling so we don't need to support
pixel-level offsets for operators, interactions.

source/blender/blenloader/intern/readfile.c
source/blender/editors/space_text/text_draw.c
source/blender/editors/space_text/text_ops.c
source/blender/makesdna/DNA_space_types.h

index 145d36626c1849496bb8d914ecd0a6fe8c18b493..b9ba17d1120ccd1756a4f040a152fe6fadf8723f 100644 (file)
@@ -7375,8 +7375,8 @@ static void direct_link_area(FileData *fd, ScrArea *area)
       SpaceText *st = (SpaceText *)sl;
 
       st->drawcache = NULL;
-      st->scroll_accum[0] = 0.0f;
-      st->scroll_accum[1] = 0.0f;
+      st->scroll_ofs_px[0] = 0;
+      st->scroll_ofs_px[1] = 0;
     }
     else if (sl->spacetype == SPACE_SEQ) {
       SpaceSeq *sseq = (SpaceSeq *)sl;
index 816bbca4afa719d786c3e85f9d446dfbddc0c39a..a8b141eff15a8c2a4984131527a76e3b7f1743bd 100644 (file)
@@ -1269,6 +1269,9 @@ static void draw_text_decoration(SpaceText *st, ARegion *ar)
 
     x = TXT_BODY_LEFT(st);
     y = ar->winy;
+    if (st->flags & ST_SCROLL_SELECT) {
+      y += st->scroll_ofs_px[1];
+    }
 
     if (vcurl == vsell) {
       y -= vcurl * lheight;
@@ -1344,9 +1347,11 @@ static void draw_text_decoration(SpaceText *st, ARegion *ar)
 
   if (!hidden) {
     /* Draw the cursor itself (we draw the sel. cursor as this is the leading edge) */
-    x = TXT_BODY_LEFT(st);
-    x += vselc * st->cwidth;
+    x = TXT_BODY_LEFT(st) + (vselc * st->cwidth);
     y = ar->winy - vsell * lheight;
+    if (st->flags & ST_SCROLL_SELECT) {
+      y += st->scroll_ofs_px[1];
+    }
 
     immUniformThemeColor(TH_HILITE);
 
@@ -1498,6 +1503,9 @@ static void draw_brackets(const SpaceText *st, const TextDrawContext *tdc, ARegi
   UI_FontThemeColor(tdc->font_id, TH_HILITE);
   x = TXT_BODY_LEFT(st);
   y = ar->winy - st->lheight_dpi;
+  if (st->flags & ST_SCROLL_SELECT) {
+    y += st->scroll_ofs_px[1];
+  }
 
   /* draw opening bracket */
   ch = startl->line[startc];
@@ -1613,6 +1621,12 @@ void draw_text_main(SpaceText *st, ARegion *ar)
 
   x = TXT_BODY_LEFT(st);
   y = ar->winy - st->lheight_dpi;
+  int viewlines = st->viewlines;
+  if (st->flags & ST_SCROLL_SELECT) {
+    y += st->scroll_ofs_px[1];
+    viewlines += 1;
+  }
+
   winx = ar->winx - TXT_SCROLL_WIDTH;
 
   /* draw cursor, margin, selection and highlight */
@@ -1621,7 +1635,7 @@ void draw_text_main(SpaceText *st, ARegion *ar)
   /* draw the text */
   UI_FontThemeColor(tdc.font_id, TH_TEXT);
 
-  for (i = 0; y > clip_min_y && i < st->viewlines && tmp; i++, tmp = tmp->next) {
+  for (i = 0; y > clip_min_y && i < viewlines && tmp; i++, tmp = tmp->next) {
     if (tdc.syntax_highlight && !tmp->format) {
       tft->format_line(st, tmp, false);
     }
@@ -1763,8 +1777,8 @@ void text_scroll_to_cursor(SpaceText *st, ARegion *ar, const bool center)
     st->left = 0;
   }
 
-  st->scroll_accum[0] = 0.0f;
-  st->scroll_accum[1] = 0.0f;
+  st->scroll_ofs_px[0] = 0;
+  st->scroll_ofs_px[1] = 0;
 }
 
 /* takes an area instead of a region, use for listeners */
index 71e24090aa14c4ce60fb66a35d7005f2509c2051..969e2ae3290c93550db42d8690b4b8a39aae5013 100644 (file)
@@ -270,8 +270,8 @@ static int text_new_exec(bContext *C, wmOperator *UNUSED(op))
     st->text = text;
     st->left = 0;
     st->top = 0;
-    st->scroll_accum[0] = 0.0f;
-    st->scroll_accum[1] = 0.0f;
+    st->scroll_ofs_px[0] = 0;
+    st->scroll_ofs_px[1] = 0;
     text_drawcache_tag_update(st, 1);
   }
 
@@ -353,8 +353,8 @@ static int text_open_exec(bContext *C, wmOperator *op)
     st->text = text;
     st->left = 0;
     st->top = 0;
-    st->scroll_accum[0] = 0.0f;
-    st->scroll_accum[1] = 0.0f;
+    st->scroll_ofs_px[0] = 0;
+    st->scroll_ofs_px[1] = 0;
   }
 
   text_drawcache_tag_update(st, 1);
@@ -2526,8 +2526,29 @@ typedef struct TextScroll {
   int scrollbar;
 
   int zone;
+
+  /* Store the state of the display, cache some constant vars. */
+  struct {
+    int ofs_init[2];
+    int ofs_max[2];
+    int size_px[2];
+  } state;
+  int ofs_delta[2];
+  int ofs_delta_px[2];
 } TextScroll;
 
+static void text_scroll_state_init(TextScroll *tsc, SpaceText *st, ARegion *ar)
+{
+  tsc->state.ofs_init[0] = st->left;
+  tsc->state.ofs_init[1] = st->top;
+
+  tsc->state.ofs_max[0] = INT_MAX;
+  tsc->state.ofs_max[1] = text_get_total_lines(st, ar) - (st->viewlines / 2);
+
+  tsc->state.size_px[0] = st->cwidth;
+  tsc->state.size_px[1] = TXT_LINE_HEIGHT(st);
+}
+
 static bool text_scroll_poll(bContext *C)
 {
   /* it should be possible to still scroll linked texts to read them,
@@ -2556,10 +2577,8 @@ static int text_scroll_exec(bContext *C, wmOperator *op)
 static void text_scroll_apply(bContext *C, wmOperator *op, const wmEvent *event)
 {
   SpaceText *st = CTX_wm_space_text(C);
-  ARegion *ar = CTX_wm_region(C);
   TextScroll *tsc = op->customdata;
   int mval[2] = {event->x, event->y};
-  int scroll_steps[2] = {0, 0};
 
   text_update_character_width(st);
 
@@ -2578,34 +2597,65 @@ static void text_scroll_apply(bContext *C, wmOperator *op, const wmEvent *event)
   /* accumulate scroll, in float values for events that give less than one
    * line offset but taken together should still scroll */
   if (!tsc->scrollbar) {
-    st->scroll_accum[0] += -tsc->delta[0] / (float)st->cwidth;
-    st->scroll_accum[1] += tsc->delta[1] / (float)(TXT_LINE_HEIGHT(st));
+    tsc->ofs_delta_px[0] -= tsc->delta[0];
+    tsc->ofs_delta_px[1] += tsc->delta[1];
   }
   else {
-    st->scroll_accum[1] += -tsc->delta[1] * st->pix_per_line;
+    tsc->ofs_delta_px[1] -= (tsc->delta[1] * st->pix_per_line) * tsc->state.size_px[1];
   }
 
-  /* round to number of lines to scroll */
-  scroll_steps[0] = (int)st->scroll_accum[0];
-  scroll_steps[1] = (int)st->scroll_accum[1];
+  for (int i = 0; i < 2; i += 1) {
+    int lines_from_pixels = tsc->ofs_delta_px[i] / tsc->state.size_px[i];
+    tsc->ofs_delta[i] += lines_from_pixels;
+    tsc->ofs_delta_px[i] -= lines_from_pixels * tsc->state.size_px[i];
+  }
 
-  st->scroll_accum[0] -= scroll_steps[0];
-  st->scroll_accum[1] -= scroll_steps[1];
+  /* The final values need to be calculated from the inputs,
+   * so clamping and ensuring an unsigned pixel offset doesn't conflict with
+   * updating the cursor delta. */
+  int scroll_ofs_new[2] = {
+      tsc->state.ofs_init[0] + tsc->ofs_delta[0],
+      tsc->state.ofs_init[1] + tsc->ofs_delta[1],
+  };
+  int scroll_ofs_px_new[2] = {
+      tsc->ofs_delta_px[0],
+      tsc->ofs_delta_px[1],
+  };
 
-  /* perform vertical and/or horizontal scroll */
-  if (scroll_steps[0] || scroll_steps[1]) {
-    txt_screen_skip(st, ar, scroll_steps[1]);
+  for (int i = 0; i < 2; i += 1) {
+    /* Ensure always unsigned (adjusting line/column accordingly). */
+    while (scroll_ofs_px_new[i] < 0) {
+      scroll_ofs_px_new[i] += tsc->state.size_px[i];
+      scroll_ofs_new[i] -= 1;
+    }
 
-    if (st->wordwrap) {
-      st->left = 0;
+    /* Clamp within usable region. */
+    if (scroll_ofs_new[i] < 0) {
+      scroll_ofs_new[i] = 0;
+      scroll_ofs_px_new[i] = 0;
     }
-    else {
-      st->left += scroll_steps[0];
-      if (st->left < 0) {
-        st->left = 0;
-      }
+    else if (scroll_ofs_new[i] >= tsc->state.ofs_max[i]) {
+      scroll_ofs_new[i] = tsc->state.ofs_max[i];
+      scroll_ofs_px_new[i] = 0;
     }
+  }
 
+  /* Override for word-wrap. */
+  if (st->wordwrap) {
+    scroll_ofs_new[0] = 0;
+    scroll_ofs_px_new[0] = 0;
+  }
+
+  /* Apply to the screen. */
+  if (scroll_ofs_new[0] != st->left || scroll_ofs_new[1] != st->top ||
+      /* Horizontal sub-pixel offset currently isn't used. */
+      /* scroll_ofs_px_new[0] != st->scroll_ofs_px[0] || */
+      scroll_ofs_px_new[1] != st->scroll_ofs_px[1]) {
+
+    st->left = scroll_ofs_new[0];
+    st->top = scroll_ofs_new[1];
+    st->scroll_ofs_px[0] = scroll_ofs_px_new[0];
+    st->scroll_ofs_px[1] = scroll_ofs_px_new[1];
     ED_area_tag_redraw(CTX_wm_area(C));
   }
 
@@ -2616,8 +2666,18 @@ static void text_scroll_apply(bContext *C, wmOperator *op, const wmEvent *event)
 static void scroll_exit(bContext *C, wmOperator *op)
 {
   SpaceText *st = CTX_wm_space_text(C);
+  TextScroll *tsc = op->customdata;
 
   st->flags &= ~ST_SCROLL_SELECT;
+
+  if (st->scroll_ofs_px[1] > tsc->state.size_px[1] / 2) {
+    st->top += 1;
+  }
+
+  st->scroll_ofs_px[0] = 0;
+  st->scroll_ofs_px[1] = 0;
+  ED_area_tag_redraw(CTX_wm_area(C));
+
   MEM_freeN(op->customdata);
 }
 
@@ -2659,6 +2719,8 @@ static void text_scroll_cancel(bContext *C, wmOperator *op)
 static int text_scroll_invoke(bContext *C, wmOperator *op, const wmEvent *event)
 {
   SpaceText *st = CTX_wm_space_text(C);
+  ARegion *ar = CTX_wm_region(C);
+
   TextScroll *tsc;
 
   if (RNA_struct_property_is_set(op->ptr, "lines")) {
@@ -2668,6 +2730,9 @@ static int text_scroll_invoke(bContext *C, wmOperator *op, const wmEvent *event)
   tsc = MEM_callocN(sizeof(TextScroll), "TextScroll");
   tsc->first = 1;
   tsc->zone = SCROLLHANDLE_BAR;
+
+  text_scroll_state_init(tsc, st, ar);
+
   op->customdata = tsc;
 
   st->flags |= ST_SCROLL_SELECT;
@@ -2780,6 +2845,8 @@ static int text_scroll_bar_invoke(bContext *C, wmOperator *op, const wmEvent *ev
   op->customdata = tsc;
   st->flags |= ST_SCROLL_SELECT;
 
+  text_scroll_state_init(tsc, st, ar);
+
   /* jump scroll, works in v2d but needs to be added here too :S */
   if (event->type == MIDDLEMOUSE) {
     tsc->old[0] = ar->winrct.xmin + BLI_rcti_cent_x(&st->txtbar);
index 576a0a85b6d1458ed0c9dc9e535c7139c95a03f1..8e4c4cc6ed4bbd02f011f195771ea208c4c33caa 100644 (file)
@@ -1217,8 +1217,11 @@ typedef struct SpaceText {
   /** Cache for faster drawing. */
   void *drawcache;
 
-  /** Runtime, for scroll increments smaller than a line. */
-  float scroll_accum[2];
+  /**
+   * Run-time for scroll increments smaller than a line (smooth scroll).
+   * Values must be between zero and the line, column width: (cwidth, TXT_LINE_HEIGHT(st)).
+   */
+  int scroll_ofs_px[2];
 } SpaceText;
 
 /* SpaceText flags (moved from DNA_text_types.h) */