Introduce put_line().
This function is a fully-fletched, high-level pixel-based line printer, that combines functionality of several firmware and list functions. It can draw spacing, icons and text in a single call, in any order and each multiple times. It can also apply line decorations at the same time. It features printf-like semantics by accepting a format string that contain format tags as well as inline text. It's accessible directly, but also through the multi-screen api for plugins. Change-Id: I70f5a77bbf4b0252521f2e47ead377b9d6d29b54
This commit is contained in:
parent
5752d029fd
commit
5d6974641b
6 changed files with 490 additions and 0 deletions
|
@ -77,6 +77,7 @@ gui/buttonbar.c
|
|||
gui/icon.c
|
||||
#endif
|
||||
gui/list.c
|
||||
gui/line.c
|
||||
#ifdef HAVE_LCD_BITMAP
|
||||
gui/bitmap/list.c
|
||||
gui/bitmap/list-skinned.c
|
||||
|
|
349
apps/gui/line.c
Normal file
349
apps/gui/line.c
Normal file
|
@ -0,0 +1,349 @@
|
|||
/***************************************************************************
|
||||
* __________ __ ___.
|
||||
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
||||
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
||||
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
||||
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
||||
* \/ \/ \/ \/ \/
|
||||
* $Id$
|
||||
*
|
||||
* Copyright (C) 2013 Thomas Martitz
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
||||
* KIND, either express or implied.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#include <ctype.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "scroll_engine.h"
|
||||
#include "system.h"
|
||||
#include "line.h"
|
||||
#include "gcc_extensions.h"
|
||||
#include "icon.h"
|
||||
#include "screens.h"
|
||||
#include "settings.h"
|
||||
#include "debug.h"
|
||||
|
||||
#ifdef HAVE_REMOTE_LCD
|
||||
#define MAX_LINES (LCD_SCROLLABLE_LINES + LCD_REMOTE_SCROLLABLE_LINES)
|
||||
#else
|
||||
#define MAX_LINES LCD_SCROLLABLE_LINES
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef HAVE_LCD_CHARCELLS
|
||||
#define style_line(d, x, y, l)
|
||||
#else
|
||||
static void style_line(struct screen *display, int x, int y, struct line_desc *line);
|
||||
#endif
|
||||
|
||||
static void put_text(struct screen *display, int x, int y, struct line_desc *line,
|
||||
const char *text, bool prevent_scroll, int text_skip_pixels);
|
||||
|
||||
|
||||
static struct line_desc *get_line_desc(void)
|
||||
{
|
||||
static struct line_desc lines[MAX_LINES];
|
||||
static unsigned line_index;
|
||||
struct line_desc *ret;
|
||||
|
||||
ret = &lines[line_index++];
|
||||
if (line_index >= ARRAYLEN(lines))
|
||||
line_index = 0;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void scroller(struct scrollinfo *s, struct screen *display)
|
||||
{
|
||||
/* style_line() expects the entire line rect, including padding, to
|
||||
* draw selector properly across the text+padding. however struct scrollinfo
|
||||
* has only the rect for the text itself, which is off depending on the
|
||||
* line padding. this needs to be corrected for calling style_line().
|
||||
* The alternative would be to really redraw only the text area,
|
||||
* but that would complicate the code a lot */
|
||||
struct line_desc *line = s->userdata;
|
||||
style_line(display, s->x, s->y - (line->height/2 - display->getcharheight()/2), line);
|
||||
put_text(display, s->x, s->y, line, s->line, true, s->offset);
|
||||
}
|
||||
|
||||
static void scroller_main(struct scrollinfo *s)
|
||||
{
|
||||
scroller(s, &screens[SCREEN_MAIN]);
|
||||
}
|
||||
|
||||
#ifdef HAVE_REMOTE_LCD
|
||||
static void scroller_remote(struct scrollinfo *s)
|
||||
{
|
||||
scroller(s, &screens[SCREEN_REMOTE]);
|
||||
}
|
||||
#endif
|
||||
|
||||
static void (*scrollers[NB_SCREENS])(struct scrollinfo *s) = {
|
||||
scroller_main,
|
||||
#ifdef HAVE_REMOTE_LCD
|
||||
scroller_remote,
|
||||
#endif
|
||||
};
|
||||
|
||||
static void put_icon(struct screen *display, int x, int y,
|
||||
struct line_desc *line,
|
||||
enum themable_icons icon)
|
||||
{
|
||||
unsigned drmode = DRMODE_FG;
|
||||
/* Need to change the drawmode:
|
||||
* mono icons should behave like text, inverted on the selector bar
|
||||
* native (colored) icons should be drawn as-is */
|
||||
if (!get_icon_format(display->screen_type) == FORMAT_MONO && (line->style & STYLE_INVERT))
|
||||
drmode = DRMODE_SOLID | DRMODE_INVERSEVID;
|
||||
|
||||
display->set_drawmode(drmode);
|
||||
screen_put_iconxy(display, x, y, icon);
|
||||
}
|
||||
|
||||
|
||||
static void put_text(struct screen *display,
|
||||
int x, int y, struct line_desc *line,
|
||||
const char *text, bool prevent_scroll,
|
||||
int text_skip_pixels)
|
||||
{
|
||||
/* set drawmode because put_icon() might have changed it */
|
||||
unsigned drmode = DRMODE_FG;
|
||||
if (line->style & STYLE_INVERT)
|
||||
drmode = DRMODE_SOLID | DRMODE_INVERSEVID;
|
||||
|
||||
display->set_drawmode(drmode);
|
||||
|
||||
if (line->scroll && !prevent_scroll)
|
||||
{
|
||||
struct line_desc *line_data = get_line_desc();
|
||||
*line_data = *line;
|
||||
/* precalculate to avoid doing it in the scroller, it's save to
|
||||
* do this on the copy of the original line_desc*/
|
||||
if (line_data->height == -1)
|
||||
line_data->height = display->getcharheight();
|
||||
display->putsxy_scroll_func(x, y, text,
|
||||
scrollers[display->screen_type], line_data, text_skip_pixels);
|
||||
}
|
||||
else
|
||||
display->putsxy_scroll_func(x, y, text, NULL, NULL, text_skip_pixels);
|
||||
}
|
||||
|
||||
/* A line consists of:
|
||||
* |[Ss]|[i]|[Ss]|[t]|, where s is empty space (pixels), S is empty space
|
||||
* (n space characters), i is an icon and t is the text.
|
||||
*
|
||||
* All components are optional. However, even if none are specified the whole
|
||||
* line will be cleared and redrawn.
|
||||
*
|
||||
* For empty space with the width of an icon use i and pass Icon_NOICON as
|
||||
* corresponding argument.
|
||||
*/
|
||||
static void print_line(struct screen *display,
|
||||
int x, int y, struct line_desc *line,
|
||||
const char *fmt, va_list ap)
|
||||
{
|
||||
const char *str;
|
||||
bool num_is_valid;
|
||||
int ch, num, height;
|
||||
int xpos = x;
|
||||
int icon_y, icon_h, icon_w;
|
||||
enum themable_icons icon;
|
||||
char tempbuf[128];
|
||||
int tempbuf_idx;
|
||||
|
||||
height = line->height == -1 ? display->getcharheight() : line->height;
|
||||
icon_h = get_icon_height(display->screen_type);
|
||||
icon_w = get_icon_width(display->screen_type);
|
||||
tempbuf_idx = 0;
|
||||
/* vertically center string on the line
|
||||
* x/2 - y/2 rounds up compared to (x-y)/2 if one of x and y is odd */
|
||||
icon_y = y + height/2 - icon_h/2;
|
||||
y += height/2 - display->getcharheight()/2;
|
||||
|
||||
/* parse format string */
|
||||
while (1)
|
||||
{
|
||||
ch = *fmt++;
|
||||
/* need to check for escaped '$' */
|
||||
if (ch == '$' && *fmt != '$')
|
||||
{
|
||||
/* extra flag as num == 0 can be valid */
|
||||
num_is_valid = false;
|
||||
num = 0;
|
||||
if (tempbuf_idx)
|
||||
{ /* flush pending inline text */
|
||||
tempbuf_idx = tempbuf[tempbuf_idx] = 0;
|
||||
put_text(display, xpos, y, line, tempbuf, false, 0);
|
||||
xpos += display->getstringsize(tempbuf, NULL, NULL);
|
||||
}
|
||||
next:
|
||||
ch = *fmt++;
|
||||
switch(ch)
|
||||
{
|
||||
case '*': /* num from parameter list */
|
||||
num = va_arg(ap, int);
|
||||
num_is_valid = true;
|
||||
goto next;
|
||||
|
||||
case 'i': /* icon (without pad) */
|
||||
case 'I': /* icon with pad */
|
||||
if (ch == 'i')
|
||||
num = 0;
|
||||
else /* 'I' */
|
||||
if (!num_is_valid)
|
||||
num = 1;
|
||||
icon = va_arg(ap, int);
|
||||
/* draw it, then skip over */
|
||||
if (icon != Icon_NOICON)
|
||||
put_icon(display, xpos + num, icon_y, line, icon);
|
||||
xpos += icon_w + num*2;
|
||||
break;
|
||||
|
||||
case 'S':
|
||||
if (!num_is_valid)
|
||||
num = 1;
|
||||
xpos += num * display->getcharwidth();
|
||||
break;
|
||||
|
||||
case 's':
|
||||
if (!num_is_valid)
|
||||
num = 1;
|
||||
xpos += num;
|
||||
break;
|
||||
|
||||
case 't':
|
||||
str = va_arg(ap, const char *);
|
||||
put_text(display, xpos, y, line, str, false, num);
|
||||
xpos += display->getstringsize(str, NULL, NULL);
|
||||
break;
|
||||
|
||||
default:
|
||||
if (LIKELY(isdigit(ch)))
|
||||
{
|
||||
num_is_valid = true;
|
||||
num = 10*num + ch - '0';
|
||||
goto next;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* any other character here is an erroneous format string */
|
||||
snprintf(tempbuf, sizeof(tempbuf), "<E:%c>", ch);
|
||||
display->putsxy(xpos, y, tempbuf);
|
||||
/* Don't consider going forward, fix the caller */
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{ /* handle string constant in format string */
|
||||
tempbuf[tempbuf_idx++] = ch;
|
||||
if (!ch)
|
||||
{ /* end of string. put it online */
|
||||
put_text(display, xpos, y, line, tempbuf, false, 0);
|
||||
return;
|
||||
}
|
||||
else if (ch == '$')
|
||||
fmt++; /* escaped '$', display just once */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef HAVE_LCD_BITMAP
|
||||
static void style_line(struct screen *display,
|
||||
int x, int y, struct line_desc *line)
|
||||
{
|
||||
int style = line->style;
|
||||
int width = display->getwidth();
|
||||
int height = line->height == -1 ? display->getcharheight() : line->height;
|
||||
unsigned mask = STYLE_MODE_MASK & ~STYLE_COLORED;
|
||||
|
||||
/* mask out gradient and colorbar styles for non-color displays */
|
||||
if (display->depth < 16)
|
||||
{
|
||||
if (style & (STYLE_COLORBAR|STYLE_GRADIENT))
|
||||
{
|
||||
style &= ~(STYLE_COLORBAR|STYLE_GRADIENT);
|
||||
style |= STYLE_INVERT;
|
||||
}
|
||||
style &= ~STYLE_COLORED;
|
||||
}
|
||||
|
||||
switch (style & mask)
|
||||
{
|
||||
#if (LCD_DEPTH > 1 || (defined(LCD_REMOTE_DEPTH) && LCD_REMOTE_DEPTH > 1))
|
||||
case STYLE_GRADIENT:
|
||||
display->set_drawmode(DRMODE_FG);
|
||||
display->gradient_fillrect_part(x, y, width, height,
|
||||
line->line_color,
|
||||
line->line_end_color,
|
||||
height*line->nlines,
|
||||
height*line->line);
|
||||
break;
|
||||
case STYLE_COLORBAR:
|
||||
display->set_drawmode(DRMODE_FG);
|
||||
display->set_foreground(line->line_color);
|
||||
display->fillrect(x, y, width - x, height);
|
||||
break;
|
||||
#endif
|
||||
case STYLE_INVERT:
|
||||
display->set_drawmode(DRMODE_FG);
|
||||
display->fillrect(x, y, width - x, height);
|
||||
break;
|
||||
case STYLE_DEFAULT: default:
|
||||
display->set_drawmode(DRMODE_BG | DRMODE_INVERSEVID);
|
||||
display->fillrect(x, y, width - x, height);
|
||||
break;
|
||||
case STYLE_NONE:
|
||||
break;
|
||||
}
|
||||
#if (LCD_DEPTH > 1 || (defined(LCD_REMOTE_DEPTH) && LCD_REMOTE_DEPTH > 1))
|
||||
/* fg color and bg color are left as-is for text drawing */
|
||||
if (display->depth > 1)
|
||||
{
|
||||
if (style & STYLE_COLORED)
|
||||
{
|
||||
if (style & STYLE_INVERT)
|
||||
display->set_background(line->text_color);
|
||||
else
|
||||
display->set_foreground(line->text_color);
|
||||
}
|
||||
else if (style & (STYLE_GRADIENT|STYLE_COLORBAR))
|
||||
display->set_foreground(line->text_color);
|
||||
else
|
||||
display->set_foreground(global_settings.fg_color);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#endif /* HAVE_LCD_BITMAP */
|
||||
|
||||
void vput_line(struct screen *display,
|
||||
int x, int y, struct line_desc *line,
|
||||
const char *fmt, va_list ap)
|
||||
{
|
||||
style_line(display, x, y, line);
|
||||
print_line(display, x, y, line, fmt, ap);
|
||||
#if (LCD_DEPTH > 1 || (defined(LCD_REMOTE_DEPTH) && LCD_REMOTE_DEPTH > 1))
|
||||
if (display->depth > 1)
|
||||
display->set_foreground(global_settings.fg_color);
|
||||
#endif
|
||||
display->set_drawmode(DRMODE_SOLID);
|
||||
}
|
||||
|
||||
void put_line(struct screen *display,
|
||||
int x, int y, struct line_desc *line,
|
||||
const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
vput_line(display, x, y, line, fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
117
apps/gui/line.h
Normal file
117
apps/gui/line.h
Normal file
|
@ -0,0 +1,117 @@
|
|||
/***************************************************************************
|
||||
* __________ __ ___.
|
||||
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
||||
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
||||
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
||||
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
||||
* \/ \/ \/ \/ \/
|
||||
* $Id$
|
||||
*
|
||||
* Copyright (C) 2013 Thomas Martitz
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
||||
* KIND, either express or implied.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef __LINE_H__
|
||||
#define __LINE_H__
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
#include "lcd.h"
|
||||
#include "screens.h"
|
||||
|
||||
struct line_desc {
|
||||
/* height of the line (in pixels). -1 to inherit the height
|
||||
* from the font. The text will be centered if the height is larger,
|
||||
* but the decorations will span the entire height */
|
||||
int height;
|
||||
/* multiline support: For some decorations (e.g. gradient) to work
|
||||
* across multiple lines (e.g. to draw a line selector across 2 lines)
|
||||
* the line index and line count must be known. For normal, single
|
||||
* lines specify nlines=1 and line=0 */
|
||||
/* line count of a group */
|
||||
int16_t nlines;
|
||||
/* index of the line in the group */
|
||||
int16_t line;
|
||||
/* line text color if STYLE_COLORED is specified, in native
|
||||
* lcd format (convert with LCD_RGBPACK() if necessary) */
|
||||
fb_data text_color;
|
||||
/* line color if STYLE_COLORBAR or STYLE_GRADIENT is specified, in native
|
||||
* lcd format (convert with LCD_RGBPACK() if necessary) */
|
||||
fb_data line_color, line_end_color;
|
||||
/* line decorations, see STYLE_DEFAULT etc. */
|
||||
int style;
|
||||
/* whether the line can scroll */
|
||||
bool scroll;
|
||||
};
|
||||
|
||||
/* default initializer, can be used for static initialitation also.
|
||||
* This initializer will result in single lines without style that don't scroll */
|
||||
#define LINE_DESC_DEFINIT { .style = STYLE_DEFAULT, .height = -1, .line = 0, .nlines = 1, .scroll = false }
|
||||
|
||||
/**
|
||||
* Print a line at a given pixel postion, using decoration information from
|
||||
* line and content information from the format specifier. The format specifier
|
||||
* can include tags that depend on further parameters given to the function
|
||||
* (similar to the well-known printf()).
|
||||
*
|
||||
* Tags start with the $ sign. Below is a list of the currently supported tags:
|
||||
* $s - insert a column (1px wide) of empty space.
|
||||
* $S - insert a column (1 char wide) of empty space.
|
||||
* $i - insert an icon. put_line() expects a corresponding parameter of the
|
||||
* type 'enum themable_icons'. If Icon_NOICON is passed, then empty
|
||||
* space (icon-wide) will be inserted.
|
||||
* $I - insert an icon with padding. Works like $i but additionally
|
||||
* adds a column (1px wide) of empty space on either side of the icon.
|
||||
* $t - insert text. put_line() expects a corresponding parameter of the type
|
||||
* 'const char *'
|
||||
* $$ - insert a '$' char, use it to escape $.
|
||||
*
|
||||
* $I, $s and $S support the following two forms:
|
||||
* $n[IsS] - inserts n columns (pixels/chars respectively) of empty space
|
||||
* $*[IsS] - inserts n columns (pixels/chars respectively) of empty space. put_line()
|
||||
* expects a correspinding paramter of the type 'int' that specifies n.
|
||||
*
|
||||
* $t supports the following two forms:
|
||||
* $nt - skips the first n pixels when displaying the string
|
||||
* $*t - skips the first n pixels when displaying the string. put_line()
|
||||
* expects a correspinding paramter of the type 'int' that specifies n.
|
||||
*
|
||||
* Inline text will be printed as is and can be freely intermixed with tags,
|
||||
* except when the line can scroll. Due to limitations of the scroll engine
|
||||
* only the last piece of text (whether inline or via $t) can scroll.
|
||||
*
|
||||
* x, y - pixel postion of the line.
|
||||
* line - holds information for the line decorations
|
||||
* fmt - holds the text and optionally format tags
|
||||
* ... - additional paramters for the format tags
|
||||
*
|
||||
*/
|
||||
void put_line(struct screen *display,
|
||||
int x, int y, struct line_desc *line,
|
||||
const char *fmt, ...);
|
||||
|
||||
|
||||
/**
|
||||
* Print a line at a given pixel postion, using decoration information from
|
||||
* line and content information from the format specifier. The format specifier
|
||||
* can include tags that depend on further parameters given to the function
|
||||
* (similar to the well-known vprintf()).
|
||||
*
|
||||
* For details, see put_line(). This function is equivalent, except for
|
||||
* accepting a va_list instead of a variable paramter list.
|
||||
*/
|
||||
void vput_line(struct screen *display,
|
||||
int x, int y, struct line_desc *line,
|
||||
const char *fmt, va_list ap);
|
||||
|
||||
#endif /* __LINE_H__*/
|
|
@ -113,6 +113,7 @@ void* plugin_get_buffer(size_t *buffer_size);
|
|||
#include "crc32.h"
|
||||
#include "rbpaths.h"
|
||||
#include "core_alloc.h"
|
||||
#include "screen_access.h"
|
||||
|
||||
#ifdef HAVE_ALBUMART
|
||||
#include "albumart.h"
|
||||
|
|
|
@ -100,6 +100,15 @@ static void screen_helper_set_drawmode(int mode)
|
|||
#endif
|
||||
}
|
||||
|
||||
static void screen_helper_put_line(int x, int y, struct line_desc *line,
|
||||
const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
vput_line(&screens[0], x, y, line, fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
#if NB_SCREENS == 2
|
||||
static int screen_helper_remote_getcharwidth(void)
|
||||
{
|
||||
|
@ -156,6 +165,15 @@ static void screen_helper_remote_setuifont(int font)
|
|||
#endif
|
||||
}
|
||||
|
||||
static void screen_helper_remote_put_line(int x, int y, struct line_desc *line,
|
||||
const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
vput_line(&screens[0], x, y, line, fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
struct screen screens[NB_SCREENS] =
|
||||
|
@ -280,6 +298,7 @@ struct screen screens[NB_SCREENS] =
|
|||
.gradient_fillrect_part = lcd_gradient_fillrect_part,
|
||||
#endif
|
||||
#endif
|
||||
.put_line = screen_helper_put_line,
|
||||
},
|
||||
#if NB_SCREENS == 2
|
||||
{
|
||||
|
@ -380,6 +399,7 @@ struct screen screens[NB_SCREENS] =
|
|||
#if defined(HAVE_LCD_BITMAP)
|
||||
.set_framebuffer = (void*)lcd_remote_set_framebuffer,
|
||||
#endif
|
||||
.put_line = screen_helper_remote_put_line,
|
||||
}
|
||||
#endif /* NB_SCREENS == 2 */
|
||||
};
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
#include "buttonbar.h"
|
||||
#include "scroll_engine.h"
|
||||
#include "backdrop.h"
|
||||
#include "line.h"
|
||||
|
||||
#if defined(HAVE_REMOTE_LCD) && !defined (ROCKBOX_HAS_LOGF)
|
||||
#define NB_SCREENS 2
|
||||
|
@ -177,6 +178,7 @@ struct screen
|
|||
void (*nine_segment_bmp)(const struct bitmap* bm, int x, int y,
|
||||
int width, int height);
|
||||
#endif
|
||||
void (*put_line)(int x, int y, struct line_desc *line, const char *fmt, ...);
|
||||
};
|
||||
|
||||
#if defined(HAVE_LCD_BITMAP) || defined(HAVE_REMOTE_LCD)
|
||||
|
|
Loading…
Reference in a new issue