diff --git a/apps/gui/bitmap/list.c b/apps/gui/bitmap/list.c
index 6ce8972..da4f570 100644
--- a/apps/gui/bitmap/list.c
+++ b/apps/gui/bitmap/list.c
@@ -53,6 +53,9 @@ static enum {
SCROLL_BAR, /* scroll by using the scrollbar */
SCROLL_SWIPE, /* scroll by wiping over the screen */
} scroll_mode;
+
+static int y_offset;
+static int last_nb_items;
#endif

int gui_list_get_item_offset(struct gui_synclist * gui_list, int item_width,
@@ -146,6 +149,13 @@ void list_draw(struct screen *display, struct gui_synclist *list)
bool show_title;
struct viewport *list_text_vp = &list_text[screen];

+ /* hack! */
+ if (last_nb_items != list->nb_items)
+ {
+ y_offset = 0;
+ last_nb_items = list->nb_items;
+ }
+
line_height = font_get(parent->font)->height;
display->set_viewport(parent);
display->clear_viewport();
@@ -157,8 +167,17 @@ void list_draw(struct screen *display, struct gui_synclist *list)
list_text_vp->height -= line_height;
}

+ int y_off = y_offset;
start = list_start_item;
end = start + viewport_get_nb_lines(list_text_vp);
+ if (y_offset > 0)
+ {
+ y_off -= line_height; /* make it negative */
+ if (start > 0)
+ start--;
+ }
+ if (y_offset < 0)
+ end++;

/* draw the scrollbar if its needed */
if (global_settings.scrollbar &&
@@ -216,6 +235,7 @@ void list_draw(struct screen *display, struct gui_synclist *list)
char entry_buffer[MAX_PATH];
unsigned char *entry_name;
int text_pos = 0;
+ int line = i - start;
s = list->callback_get_item_name(i, list->data, entry_buffer,
sizeof(entry_buffer));
entry_name = P2STR(s);
@@ -285,36 +305,36 @@ void list_draw(struct screen *display, struct gui_synclist *list)
if (item_offset> item_width - (list_text_vp->width - text_pos))
{
/* don't scroll */
- display->puts_style_offset(0, i-start, entry_name,
- style, item_offset);
+ display->puts_style_xyoffset(0, line, entry_name,
+ style, item_offset, y_off);
}
else
{
- display->puts_scroll_style_offset(0, i-start, entry_name,
- style, item_offset);
+ display->puts_scroll_style_xyoffset(0, line, entry_name,
+ style, item_offset, y_off);
}
}
else
{
if (list->scroll_all)
- display->puts_scroll_style_offset(0, i-start, entry_name,
- style, item_offset);
+ display->puts_scroll_style_xyoffset(0, line, entry_name,
+ style, item_offset, y_off);
else
- display->puts_style_offset(0, i-start, entry_name,
- style, item_offset);
+ display->puts_style_xyoffset(0, line, entry_name,
+ style, item_offset, y_off);
}
/* do the icon */
display->set_viewport(&list_icons);
if (list->callback_get_item_icon && global_settings.show_icons)
{
screen_put_icon_with_offset(display, show_cursor?1:0,
- (i-start),show_cursor?ICON_PADDING:0,0,
+ (line),show_cursor?ICON_PADDING:0,y_off,
list->callback_get_item_icon(i, list->data));
}
if (show_cursor && i >= list->selected_item &&
i < list->selected_item + list->selected_size)
{
- screen_put_icon(display, 0, i-start, Icon_Cursor);
+ screen_put_icon_with_offset(display, 0, line, 0, y_off, Icon_Cursor);
}
}
display->set_viewport(parent);
@@ -343,6 +363,8 @@ static int gui_synclist_touchscreen_scrollbar(struct gui_synclist * gui_list,
if (nb_lines < gui_list->nb_items)
{
scroll_mode = SCROLL_BAR;
+ /* scrollbar scrolling is still line based */
+ y_offset = 0;
int scrollbar_size = nb_lines*
font_get(gui_list->parent[screen]->font)->height;
int actual_y = y - list_text[screen].y;
@@ -364,42 +386,76 @@ static int gui_synclist_touchscreen_scrollbar(struct gui_synclist * gui_list,
return ACTION_NONE;
}

+#define SIGN(x) ((x) < 0 ? -1 : 1)
/*
- * returns the number of scrolled items since the last call
+ * returns the number of pixel scrolled since the last call
**/
-static int gui_synclist_touchscreen_scrolling(struct gui_synclist * gui_list, int position)
+static int gui_synclist_touchscreen_scrolling(struct gui_synclist * gui_list, int line_height, int position)
{
- const int screen = screens[SCREEN_MAIN].screen_type;
- const int difference = position - last_position;
+ /* fixme */
+ const enum screen_type screen = screens[SCREEN_MAIN].screen_type;
const int nb_lines = viewport_get_nb_lines(&list_text[screen]);
- if(nb_lines < gui_list->nb_items && difference != 0) /* only scroll if needed */
+ /* in pixels */
+ const int difference = position - last_position;
+
+ /* make selecting items easier */
+ if (abs(difference) < 3 && scroll_mode == SCROLL_NONE)
+ return 0;
+
+ /* does the list even scroll? if no, return but still show
+ * the caller that we would scroll */
+ if (nb_lines >= gui_list->nb_items)
+ return difference;
+
+ const int old_start = gui_list->start_item[screen];
+ int new_start_item = -1;
+ int line_diff = 0;
+
+ /* don't scroll at the edges of the list */
+ if ((old_start == 0 && difference > 0)
+ || (old_start == (gui_list->nb_items - nb_lines) && difference < 0))
+ {
+ y_offset = 0;
+ return difference;
+ }
+
+ /* add up y_offset over time and translate to lines
+ * if scrolled enough */
+ y_offset += difference;
+ if (abs(y_offset) > line_height)
{
- int new_start_item;
- new_start_item = gui_list->start_item[screen] - difference;
+ line_diff = y_offset/line_height;
+ y_offset -= line_diff * line_height;
+ }
+
+ if(line_diff != 0)
+ {
+ new_start_item = old_start - line_diff;
/* check if new_start_item is bigger than list item count */
if(new_start_item > gui_list->nb_items - nb_lines)
new_start_item = gui_list->nb_items - nb_lines;
/* set new_start_item to 0 if it's negative */
- if(new_start_item < 0)
+ if(new_start_item <= 0)
new_start_item = 0;
gui_list->start_item[screen] = new_start_item;
}
+
return difference;
}

unsigned gui_synclist_do_touchscreen(struct gui_synclist * gui_list)
{
short x, y;
- const int button = action_get_touchscreen_press(&x, &y);
- const int screen = SCREEN_MAIN;
+ const enum screen_type screen = SCREEN_MAIN;
+ struct viewport *info_vp = sb_skin_get_info_vp(screen);
+ const int button = action_get_touchscreen_press_in_vp(&x, &y, info_vp);
const int list_start_item = gui_list->start_item[screen];
const struct viewport *list_text_vp = &list_text[screen];
const bool old_released = released;
int line, list_width = list_text_vp->width;

- /* make sure it is inside the UI viewport */
- if (!viewport_point_within_vp(sb_skin_get_info_vp(screen), x, y))
- return BUTTON_NONE;
+ if (button == ACTION_NONE || button == ACTION_UNKNOWN)
+ return ACTION_NONE;

released = (button&BUTTON_REL) != 0;

@@ -478,10 +534,6 @@ unsigned gui_synclist_do_touchscreen(struct gui_synclist * gui_list)
/* Pressed below the list*/
if (list_start_item + line >= gui_list->nb_items)
return ACTION_NONE;
-
- /* Pressed a border */
- if(UNLIKELY(actual_y % line_height == 0))
- return ACTION_NONE;

if (released)
{
@@ -524,9 +576,9 @@ unsigned gui_synclist_do_touchscreen(struct gui_synclist * gui_list)
/* select current item */
gui_synclist_select_item(gui_list, list_start_item+line);
if (last_position == 0)
- last_position = line;
+ last_position = actual_y;
else
- result = gui_synclist_touchscreen_scrolling(gui_list, line);
+ result = gui_synclist_touchscreen_scrolling(gui_list, line_height, actual_y);

/* Start scrolling once the pen is moved without
* releasing it inbetween */
@@ -536,7 +588,7 @@ unsigned gui_synclist_do_touchscreen(struct gui_synclist * gui_list)
scroll_mode = SCROLL_SWIPE;
}

- last_position = line;
+ last_position = actual_y;

return redraw ? ACTION_REDRAW:ACTION_NONE;
}
diff --git a/apps/screen_access.c b/apps/screen_access.c
index 7b64c40..d059547 100644
--- a/apps/screen_access.c
+++ b/apps/screen_access.c
@@ -168,8 +168,10 @@ struct screen screens[NB_SCREENS] =
.hline=&lcd_hline,
.scroll_step=&lcd_scroll_step,
.puts_style_offset=&lcd_puts_style_offset,
+ .puts_style_xyoffset=&lcd_puts_style_xyoffset,
.puts_scroll_style=&lcd_puts_scroll_style,
.puts_scroll_style_offset=&lcd_puts_scroll_style_offset,
+ .puts_scroll_style_xyoffset=&lcd_puts_scroll_style_xyoffset,
#endif /* HAVE_LCD_BITMAP */

#ifdef HAVE_LCD_CHARCELLS
@@ -257,8 +259,10 @@ struct screen screens[NB_SCREENS] =
.hline=&lcd_remote_hline,
.scroll_step=&lcd_remote_scroll_step,
.puts_style_offset=&lcd_remote_puts_style_offset,
+ .puts_style_xyoffset=&lcd_remote_puts_style_xyoffset,
.puts_scroll_style=&lcd_remote_puts_scroll_style,
.puts_scroll_style_offset=&lcd_remote_puts_scroll_style_offset,
+ .puts_scroll_style_xyoffset=&lcd_remote_puts_scroll_style_xyoffset,
#endif /* 1 */

#if 0 /* no charcell remote LCDs so far */
diff --git a/apps/screen_access.h b/apps/screen_access.h
index 463ca3f..a154d20 100644
--- a/apps/screen_access.h
+++ b/apps/screen_access.h
@@ -77,11 +77,15 @@ struct screen

void (*scroll_step)(int pixels);
void (*puts_style_offset)(int x, int y, const unsigned char *str,
- int style, int offset);
+ int style, int x_offset);
+ void (*puts_style_xyoffset)(int x, int y, const unsigned char *str,
+ int style, int x_offset, int y_offset);
void (*puts_scroll_style)(int x, int y, const unsigned char *string,
int style);
void (*puts_scroll_style_offset)(int x, int y, const unsigned char *string,
- int style, int offset);
+ int style, int x_offset);
+ void (*puts_scroll_style_xyoffset)(int x, int y, const unsigned char *string,
+ int style, int x_offset, int y_offset);
void (*mono_bitmap)(const unsigned char *src,
int x, int y, int width, int height);
void (*mono_bitmap_part)(const unsigned char *src, int src_x, int src_y,
@@ -134,7 +138,9 @@ struct screen
void (*puts_offset)(int x, int y, const unsigned char *str, int offset);
void (*puts_scroll)(int x, int y, const unsigned char *string);
void (*puts_scroll_offset)(int x, int y, const unsigned char *string,
- int offset);
+ int x_offset);
+ void (*puts_scroll_xyoffset)(int x, int y, const unsigned char *string,
+ int x_offset, int y_offset);
void (*scroll_speed)(int speed);
void (*scroll_delay)(int ms);
void (*stop_scroll)(void);
diff --git a/firmware/drivers/lcd-bitmap-common.c b/firmware/drivers/lcd-bitmap-common.c
index 9f1d5fe..2ce11e3 100644
--- a/firmware/drivers/lcd-bitmap-common.c
+++ b/firmware/drivers/lcd-bitmap-common.c
@@ -284,8 +284,8 @@ static void LCDFN(putsxyofs_style)(int xpos, int ypos,
/*** Line oriented text output ***/

/* put a string at a given char position */
-void LCDFN(puts_style_offset)(int x, int y, const unsigned char *str,
- int style, int offset)
+void LCDFN(puts_style_xyoffset)(int x, int y, const unsigned char *str,
+ int style, int x_offset, int y_offset)
{
int xpos, ypos, w, h;
LCDFN(scroll_stop_line)(current_vp, y);
@@ -294,8 +294,14 @@ void LCDFN(puts_style_offset)(int x, int y, const unsigned char *str,

LCDFN(getstringsize)(str, &w, &h);
xpos = x * LCDFN(getstringsize)(" ", NULL, NULL);
- ypos = y * h;
- LCDFN(putsxyofs_style)(xpos, ypos, str, style, w, h, offset);
+ ypos = y * h + y_offset;
+ LCDFN(putsxyofs_style)(xpos, ypos, str, style, w, h, x_offset);
+}
+
+void LCDFN(puts_style_offset)(int x, int y, const unsigned char *str,
+ int style, int x_offset)
+{
+ LCDFN(puts_style_xyoffset)(x, y, str, style, x_offset, 0);
}

void LCDFN(puts)(int x, int y, const unsigned char *str)
@@ -326,8 +332,8 @@ void LCDFN(puts_offset)(int x, int y, const unsigned char *str, int offset)

/*** scrolling ***/

-void LCDFN(puts_scroll_style_offset)(int x, int y, const unsigned char *string,
- int style, int offset)
+void LCDFN(puts_scroll_style_xyoffset)(int x, int y, const unsigned char *string,
+ int style, int x_offset, int y_offset)
{
struct scrollinfo* s;
char *end;
@@ -343,7 +349,7 @@ void LCDFN(puts_scroll_style_offset)(int x, int y, const unsigned char *string,
if (LCDFN(scroll_info).lines >= LCDM(SCROLLABLE_LINES)) return;
if (!string)
return;
- LCDFN(puts_style_offset)(x, y, string, style, offset);
+ LCDFN(puts_style_xyoffset)(x, y, string, style, x_offset, y_offset);

LCDFN(getstringsize)(string, &w, &h);

@@ -381,8 +387,9 @@ void LCDFN(puts_scroll_style_offset)(int x, int y, const unsigned char *string,

s->vp = current_vp;
s->y = y;
- s->offset = offset;
+ s->offset = x_offset;
s->startx = x * LCDFN(getstringsize)(" ", NULL, NULL);
+ s->y_offset = y_offset;
s->backward = false;

LCDFN(scroll_info).lines++;
@@ -429,7 +436,7 @@ void LCDFN(scroll_fn)(void)

pf = font_get(current_vp->font);
xpos = s->startx;
- ypos = s->y * pf->height;
+ ypos = s->y * pf->height + s->y_offset;

if (s->bidir) { /* scroll bidirectional */
if (s->offset <= 0) {
@@ -457,3 +464,9 @@ void LCDFN(scroll_fn)(void)
}
LCDFN(set_viewport)(old_vp);
}
+
+void LCDFN(puts_scroll_style_offset)(int x, int y, const unsigned char *string,
+ int style, int x_offset)
+{
+ LCDFN(puts_scroll_style_xyoffset)(x, y, string, style, x_offset, 0);
+}
diff --git a/firmware/export/lcd.h b/firmware/export/lcd.h
index 1cbb286..54a2f3c 100644
--- a/firmware/export/lcd.h
+++ b/firmware/export/lcd.h
@@ -192,6 +192,8 @@ extern void lcd_clear_viewport(void);
extern void lcd_clear_display(void);
extern void lcd_putsxy(int x, int y, const unsigned char *string);
extern void lcd_putsxyf(int x, int y, const unsigned char *fmt, ...);
+extern void lcd_putsxy_style_offset(int x, int y, const unsigned char *str,
+ int style, int offset);
extern void lcd_puts(int x, int y, const unsigned char *string);
extern void lcd_putsf(int x, int y, const unsigned char *fmt, ...);
extern void lcd_puts_style(int x, int y, const unsigned char *string, int style);
@@ -480,9 +482,13 @@ extern void lcd_setfont(int font);
extern int lcd_getfont(void);

extern void lcd_puts_style_offset(int x, int y, const unsigned char *str,
- int style, int offset);
+ int style, int x_offset);
+extern void lcd_puts_style_xyoffset(int x, int y, const unsigned char *str,
+ int style, int x_offset, int y_offset);
extern void lcd_puts_scroll_style_offset(int x, int y, const unsigned char *string,
- int style, int offset);
+ int style, int x_offset);
+extern void lcd_puts_scroll_style_xyoffset(int x, int y, const unsigned char *string,
+ int style, int x_offset, int y_offset);

/* low level drawing function pointer arrays */
#if LCD_DEPTH >= 8
diff --git a/firmware/export/scroll_engine.h b/firmware/export/scroll_engine.h
index ca94828..0fe6fe4 100644
--- a/firmware/export/scroll_engine.h
+++ b/firmware/export/scroll_engine.h
@@ -56,6 +56,7 @@ struct scrollinfo
int y; /* Position of the line on the screen (char co-ordinates) */
int offset;
int startx;
+ int y_offset; /* y offset of the line, used for pixel-accurate list scrolling */
#ifdef HAVE_LCD_BITMAP
int width; /* length of line in pixels */
int style; /* line style */