Introduction
Introduction Statistics Contact Development Disclaimer Help
add horizontal and monocle layout and rework/cleanup drawing logic - sfeed_curs…
git clone git://git.codemadness.org/sfeed_curses
Log
Files
Refs
README
LICENSE
---
commit 83f15d40dff4c353efba704e7c51f05e077179d2
parent 18bd36ede3a9d2ea2f8dbad50a3ce0858d272584
Author: Hiltjo Posthuma <[email protected]>
Date: Wed, 10 Mar 2021 17:22:05 +0100
add horizontal and monocle layout and rework/cleanup drawing logic
- Add the layouts horizontal and monocle.
- The monocle layout replaces the mode of hiding the sidebar using 's'. This
's' keybind now toggles between a monocle and non-monocle layout (vertical or
horizontal). When a feed is read from stdin (no filenames) then the default
layout will now be monocle also.
- The monocle layout also works nicely on smaller portable screens (such as a
Motorola Droid4) or on smaller terminals or if a more newsboat-like style is
wanted.
- Layouts can be changed with the keybinds '1', '2', '3', a preference can also
be set at startup using the recently added SFEED_AUTOCMD feature, like so:
SFEED_AUTOCMD=2 sfeed_curses.
Diffstat:
M sfeed_curses.1 | 22 +++++++++++++++++-----
M sfeed_curses.c | 273 ++++++++++++++++++++++-------…
M themes/mono.h | 1 +
M themes/newsboat.h | 1 +
M themes/templeos.h | 5 +++++
5 files changed, 222 insertions(+), 80 deletions(-)
---
diff --git a/sfeed_curses.1 b/sfeed_curses.1
@@ -1,4 +1,4 @@
-.Dd February 20, 2021
+.Dd March 10, 2021
.Dt SFEED_CURSES 1
.Os
.Sh NAME
@@ -78,13 +78,16 @@ Reload all feed files which were specified as arguments on …
.It m
Toggle mouse-mode.
.It s
-Toggle showing the feeds pane sidebar.
+Toggle between monocle layout and the previous non-monocle layout.
.It <
-Use fixed sidebar width and decrease fixed width by 1 column.
+Use a fixed sidebar size for the current layout and decrease the fixed width or
+height by 1 column.
.It >
-Use fixed sidebar width and increase fixed width by 1 column.
+Use a fixed sidebar size for the current layout and increase the fixed width or
+height by 1 column.
.It =
-Reset sidebar width to automatic adjustment.
+Reset the sidebar width or height to automatic adjustment for the current
+layout.
.It t
Toggle showing only feeds with new items in the sidebar.
.It a, e, @
@@ -128,6 +131,15 @@ Mark all items of the current loaded feed as unread.
This will only work when
.Ev SFEED_URL_FILE
is set.
+.It 1
+Set the current layout to a vertical mode. Showing a feeds sidebar to the left
+and the feed items to the right.
+.It 2
+Set the current layout to a horizontal mode. Showing a feeds sidebar on the top
+and the feed items on the bottom.
+.It 3
+Set the current layout to a monocle mode. Showing only one feeds or an feed
+items pane at once.
.It q, EOF
Quit
.El
diff --git a/sfeed_curses.c b/sfeed_curses.c
@@ -25,12 +25,14 @@
#include "minicurses.h"
#endif
-#define LEN(a) sizeof((a))/sizeof((a)[0])
+#define LEN(a) sizeof((a))/sizeof((a)[0])
#define MAX(a,b) ((a) > (b) ? (a) : (b))
#define PAD_TRUNCATE_SYMBOL "\xe2\x80\xa6" /* symbol: "ellipsis" */
#define SCROLLBAR_SYMBOL_BAR "\xe2\x94\x82" /* symbol: "light vertical" */
#define SCROLLBAR_SYMBOL_TICK " "
+#define LINEBAR_SYMBOL_BAR "\xe2\x94\x80" /* symbol: "light horizontal" */
+#define LINEBAR_SYMBOL_RIGHT "\xe2\x94\xa4" /* symbol: "light vertical and l…
#define UTF_INVALID_SYMBOL "\xef\xbf\xbd" /* symbol: "replacement" */
/* color-theme */
@@ -43,6 +45,10 @@ enum {
ATTR_RESET = 0, ATTR_BOLD_ON = 1, ATTR_FAINT_ON = 2, ATTR_REVER…
};
+enum Layout {
+ LayoutVertical = 0, LayoutHorizontal, LayoutMonocle, LayoutLast
+};
+
enum Pane { PaneFeeds, PaneItems, PaneLast };
enum {
@@ -100,6 +106,14 @@ struct statusbar {
int dirty; /* needs draw update */
};
+struct linebar {
+ int x; /* absolute x position on the screen */
+ int y; /* absolute y position on the screen */
+ int width; /* absolute width of the line */
+ int hidden; /* is visible or not */
+ int dirty; /* needs draw update */
+};
+
/* /UI */
struct item {
@@ -130,7 +144,7 @@ struct feed {
void alldirty(void);
void cleanup(void);
void draw(void);
-int getsidebarwidth(void);
+int getsidebarsize(void);
void markread(struct pane *, off_t, off_t, int);
void pane_draw(struct pane *);
void sighandler(int);
@@ -140,12 +154,15 @@ void urls_free(void);
int urls_isnew(const char *);
void urls_read(void);
+static struct linebar linebar;
static struct statusbar statusbar;
static struct pane panes[PaneLast];
static struct scrollbar scrollbars[PaneLast]; /* each pane has a scrollbar */
static struct win win;
static size_t selpane;
-static int fixedsidebarwidth = -1; /* fixed sidebar width, < 0 is automatic */
+/* fixed sidebar size, < 0 is automatic */
+static int fixedsidebarsizes[LayoutLast] = { -1, -1, -1 };
+static int layout = LayoutVertical, prevlayout = LayoutVertical;
static int onlynew = 0; /* show only new in sidebar */
static int usemouse = 1; /* use xterm mouse tracking */
@@ -817,36 +834,75 @@ pane_draw(struct pane *p)
}
void
+setlayout(int n)
+{
+ if (layout != LayoutMonocle)
+ prevlayout = layout; /* previous non-monocle layout */
+ layout = n;
+}
+
+void
updategeom(void)
{
- int w, x;
-
- panes[PaneFeeds].width = getsidebarwidth();
- if (win.width && panes[PaneFeeds].width >= win.width)
- panes[PaneFeeds].width = win.width - 1;
- panes[PaneFeeds].x = 0;
- panes[PaneFeeds].y = 0;
- /* reserve space for statusbar */
- panes[PaneFeeds].height = MAX(win.height - 1, 1);
-
- /* NOTE: updatesidebar() must happen before this function for the
- remaining width */
- if (!panes[PaneFeeds].hidden) {
- w = win.width - panes[PaneFeeds].width;
- x = panes[PaneFeeds].x + panes[PaneFeeds].width;
+ int barsize, h, w, x = 0, y = 0;
+
+ panes[PaneFeeds].hidden = layout == LayoutMonocle && (selpane != PaneF…
+ panes[PaneItems].hidden = layout == LayoutMonocle && (selpane != PaneI…
+ linebar.hidden = layout != LayoutHorizontal;
+
+ w = win.width;
+ /* always reserve space for statusbar */
+ h = MAX(win.height - 1, 1);
+
+ panes[PaneFeeds].x = x;
+ panes[PaneFeeds].y = y;
+
+ switch (layout) {
+ case LayoutVertical:
+ /* NOTE: updatesidebar() must happen before this function for …
+ remaining width */
+ barsize = getsidebarsize();
+ if (w && barsize >= w)
+ barsize = w - 1;
+
+ panes[PaneFeeds].width = MAX(barsize, 0);
+ x += panes[PaneFeeds].width;
+ w -= panes[PaneFeeds].width;
+
/* space for scrollbar if sidebar is visible */
w--;
x++;
- } else {
- w = win.width;
- x = 0;
+
+ panes[PaneFeeds].height = MAX(h, 1);
+ break;
+ case LayoutHorizontal:
+ barsize = getsidebarsize();
+ if (h && barsize >= h / 2)
+ barsize = h / 2;
+
+ panes[PaneFeeds].height = MAX(barsize, 1);
+
+ h -= panes[PaneFeeds].height;
+ y += panes[PaneFeeds].height;
+
+ linebar.x = 0;
+ linebar.y = y;
+ linebar.width = win.width;
+
+ h -= 1;
+ y += 1;
+ panes[PaneFeeds].width = MAX(w - 1, 0);
+ break;
+ case LayoutMonocle:
+ panes[PaneFeeds].height = MAX(h, 1);
+ panes[PaneFeeds].width = MAX(w - 1, 0);
+ break;
}
+ panes[PaneItems].width = MAX(w - 1, 0);
+ panes[PaneItems].height = MAX(h, 1);
panes[PaneItems].x = x;
- panes[PaneItems].width = MAX(w - 1, 0); /* rest and space for scrollba…
- panes[PaneItems].height = panes[PaneFeeds].height;
- panes[PaneItems].y = panes[PaneFeeds].y;
- panes[PaneItems].hidden = !panes[PaneItems].width || !panes[PaneItems]…
+ panes[PaneItems].y = y;
scrollbars[PaneFeeds].x = panes[PaneFeeds].x + panes[PaneFeeds].width;
scrollbars[PaneFeeds].y = panes[PaneFeeds].y;
@@ -856,7 +912,7 @@ updategeom(void)
scrollbars[PaneItems].x = panes[PaneItems].x + panes[PaneItems].width;
scrollbars[PaneItems].y = panes[PaneItems].y;
scrollbars[PaneItems].size = panes[PaneItems].height;
- scrollbars[PaneItems].hidden = panes[PaneItems].hidden;
+ scrollbars[PaneItems].hidden = panes[PaneItems].width ? 0 : 1;
/* statusbar below */
statusbar.width = win.width;
@@ -1046,6 +1102,27 @@ uiprompt(int x, int y, char *fmt, ...)
}
void
+linebar_draw(struct linebar *b)
+{
+ int i;
+
+ if (!b->dirty)
+ return;
+ b->dirty = 0;
+ if (b->hidden || !b->width)
+ return;
+
+ cursorsave();
+ cursormove(b->x, b->y);
+ THEME_LINEBAR();
+ for (i = 0; i < b->width - 1; i++)
+ ttywrite(LINEBAR_SYMBOL_BAR);
+ ttywrite(LINEBAR_SYMBOL_RIGHT);
+ attrmode(ATTR_RESET);
+ cursorrestore();
+}
+
+void
statusbar_draw(struct statusbar *s)
{
if (!s->dirty)
@@ -1338,29 +1415,40 @@ feeds_reloadall(void)
}
int
-getsidebarwidth(void)
+getsidebarsize(void)
{
struct feed *feed;
size_t i;
- int len, width = 0;
-
- /* fixed sidebar width? else calculate an optimal size automatically */
- if (fixedsidebarwidth >= 0)
- return fixedsidebarwidth;
-
- for (i = 0; i < nfeeds; i++) {
- feed = &feeds[i];
-
- len = snprintf(NULL, 0, " (%lu/%lu)", feed->totalnew, feed->to…
- colw(feed->name);
- if (len > width)
- width = len;
-
- if (onlynew && feed->totalnew == 0)
- continue;
+ int len, size;
+
+ /* fixed sidebar size? else calculate an optimal size automatically */
+ if (fixedsidebarsizes[layout] >= 0)
+ return fixedsidebarsizes[layout];
+
+ switch (layout) {
+ case LayoutVertical:
+ for (i = 0, size = 0; i < nfeeds; i++) {
+ feed = &feeds[i];
+ len = snprintf(NULL, 0, " (%lu/%lu)",
+ feed->totalnew, feed->total) +
+ colw(feed->name);
+ if (len > size)
+ size = len;
+
+ if (onlynew && feed->totalnew == 0)
+ continue;
+ }
+ return size;
+ case LayoutHorizontal:
+ for (i = 0, size = 0; i < nfeeds; i++) {
+ feed = &feeds[i];
+ if (onlynew && feed->totalnew == 0)
+ continue;
+ size++;
+ }
+ return size;
}
-
- return width;
+ return 0;
}
void
@@ -1370,15 +1458,24 @@ updatesidebar(void)
struct row *row;
struct feed *feed;
size_t i, nrows;
- int oldwidth;
+ int oldvalue, newvalue;
p = &panes[PaneFeeds];
-
if (!p->rows)
p->rows = ecalloc(sizeof(p->rows[0]), nfeeds + 1);
- oldwidth = p->width;
- p->width = getsidebarwidth();
+ switch (layout) {
+ case LayoutVertical:
+ oldvalue = p->width;
+ newvalue = getsidebarsize();
+ p->width = newvalue;
+ break;
+ case LayoutHorizontal:
+ oldvalue = p->height;
+ newvalue = getsidebarsize();
+ p->height = newvalue;
+ break;
+ }
nrows = 0;
for (i = 0; i < nfeeds; i++) {
@@ -1395,10 +1492,18 @@ updatesidebar(void)
}
p->nrows = nrows;
- if (p->width != oldwidth)
- updategeom();
- else
+ switch (layout) {
+ case LayoutVertical:
+ case LayoutHorizontal:
+ if (oldvalue != newvalue)
+ updategeom();
+ else
+ p->dirty = 1;
+ break;
+ default:
p->dirty = 1;
+ break;
+ }
if (!p->nrows)
p->pos = 0;
@@ -1429,6 +1534,7 @@ alldirty(void)
panes[PaneItems].dirty = 1;
scrollbars[PaneFeeds].dirty = 1;
scrollbars[PaneItems].dirty = 1;
+ linebar.dirty = 1;
statusbar.dirty = 1;
}
@@ -1440,8 +1546,8 @@ draw(void)
size_t i;
if (win.dirty) {
- clearscreen();
win.dirty = 0;
+ clearscreen();
}
/* There is the same amount and indices of panes and scrollbars. */
@@ -1456,6 +1562,8 @@ draw(void)
scrollbar_draw(&scrollbars[i]);
}
+ linebar_draw(&linebar);
+
/* If item selection text changed then update the status text. */
if ((row = pane_row_get(&panes[PaneItems], panes[PaneItems].pos))) {
item = (struct item *)row->data;
@@ -1481,7 +1589,7 @@ mousereport(int button, int release, int x, int y)
for (i = 0; i < LEN(panes); i++) {
p = &panes[i];
- if (p->hidden)
+ if (p->hidden || !p->width || !p->height)
continue;
if (!(x >= p->x && x < p->x + p->width &&
@@ -1510,6 +1618,11 @@ mousereport(int button, int release, int x, int y)
/* redraw row: counts could be changed */
updatesidebar();
updatetitle();
+
+ if (layout == LayoutMonocle) {
+ selpane = PaneItems;
+ updategeom();
+ }
} else if (i == PaneItems) {
if (dblclick && !changedpane) {
row = pane_row_get(p, p->pos);
@@ -1788,6 +1901,9 @@ main(int argc, char *argv[])
urlfile = getenv("SFEED_URL_FILE"); /* can be NULL */
cmdenv = getenv("SFEED_AUTOCMD"); /* can be NULL */
+ setlayout(argc <= 1 ? LayoutMonocle : LayoutVertical);
+ selpane = layout == LayoutMonocle ? PaneItems : PaneFeeds;
+
panes[PaneFeeds].row_format = feed_row_format;
panes[PaneFeeds].row_match = feed_row_match;
panes[PaneItems].row_format = item_row_format;
@@ -1825,14 +1941,6 @@ main(int argc, char *argv[])
if (argc == 1)
feeds[0].fp = NULL;
- if (argc > 1) {
- panes[PaneFeeds].hidden = 0;
- selpane = PaneFeeds;
- } else {
- panes[PaneFeeds].hidden = 1;
- selpane = PaneItems;
- }
-
if ((devnullfd = open("/dev/null", O_WRONLY)) == -1)
die("open: /dev/null");
@@ -1918,15 +2026,21 @@ keyleft:
if (selpane == PaneFeeds)
break;
selpane = PaneFeeds;
+ if (layout == LayoutMonocle)
+ updategeom();
break;
keyright:
case 'l':
if (selpane == PaneItems)
break;
selpane = PaneItems;
+ if (layout == LayoutMonocle)
+ updategeom();
break;
case '\t':
selpane = selpane == PaneFeeds ? PaneItems : PaneFeeds;
+ if (layout == LayoutMonocle)
+ updategeom();
break;
startpos:
case 'g':
@@ -2004,24 +2118,18 @@ nextpage:
usemouse = !usemouse;
mousemode(usemouse);
break;
- case 's': /* toggle sidebar */
- panes[PaneFeeds].hidden = !panes[PaneFeeds].hidden;
- if (selpane == PaneFeeds && panes[selpane].hidden)
- selpane = PaneItems;
- updategeom();
- break;
case '<': /* decrease fixed sidebar width */
case '>': /* increase fixed sidebar width */
- if (fixedsidebarwidth < 0)
- fixedsidebarwidth = getsidebarwidth();
- if (ch == '<' && fixedsidebarwidth > 0)
- fixedsidebarwidth--;
+ if (fixedsidebarsizes[layout] < 0)
+ fixedsidebarsizes[layout] = getsidebarsize();
+ if (ch == '<' && fixedsidebarsizes[layout] > 0)
+ fixedsidebarsizes[layout]--;
else if (ch != '<')
- fixedsidebarwidth++;
+ fixedsidebarsizes[layout]++;
updategeom();
break;
- case '=': /* reset fixed sidebar width to automatic size */
- fixedsidebarwidth = -1;
+ case '=': /* reset fixed sidebar to automatic size */
+ fixedsidebarsizes[layout] = -1;
updategeom();
break;
case 't': /* toggle showing only new in sidebar */
@@ -2043,6 +2151,11 @@ nextpage:
/* redraw row: counts could be changed */
updatesidebar();
updatetitle();
+
+ if (layout == LayoutMonocle) {
+ selpane = PaneItems;
+ updategeom();
+ }
} else if (selpane == PaneItems && panes[selpane].nrow…
row = pane_row_get(p, p->pos);
item = (struct item *)row->data;
@@ -2083,6 +2196,16 @@ nextpage:
markread(p, p->pos, p->pos, ch == 'r');
}
break;
+ case 's': /* toggle layout between monocle or non-monocle */
+ setlayout(layout == LayoutMonocle ? prevlayout : Layou…
+ updategeom();
+ break;
+ case '1': /* vertical layout */
+ case '2': /* horizontal layout */
+ case '3': /* monocle layout */
+ setlayout(ch - '1');
+ updategeom();
+ break;
case 4: /* EOT */
case 'q': goto end;
}
diff --git a/themes/mono.h b/themes/mono.h
@@ -7,6 +7,7 @@
#define THEME_SCROLLBAR_NORMAL() do { attrmode(ATTR_FAINT_ON); } while(…
#define THEME_SCROLLBAR_TICK_FOCUS() do { attrmode(ATTR_REVERSE_ON); } while(…
#define THEME_SCROLLBAR_TICK_NORMAL() do { attrmode(ATTR_REVERSE_ON); } while(…
+#define THEME_LINEBAR() do { attrmode(ATTR_FAINT_ON); } while(…
#define THEME_STATUSBAR() do { attrmode(ATTR_REVERSE_ON); } while(…
#define THEME_INPUT_LABEL() do { attrmode(ATTR_REVERSE_ON); } while(…
#define THEME_INPUT_NORMAL() do { } while(…
diff --git a/themes/newsboat.h b/themes/newsboat.h
@@ -7,6 +7,7 @@
#define THEME_SCROLLBAR_NORMAL() do { ttywrite("\x1b[34m"); } while(0)
#define THEME_SCROLLBAR_TICK_FOCUS() do { ttywrite("\x1b[44m"); } while(0)…
#define THEME_SCROLLBAR_TICK_NORMAL() do { ttywrite("\x1b[44m"); } while(0)
+#define THEME_LINEBAR() do { ttywrite("\x1b[34m"); } while(0)
#define THEME_STATUSBAR() do { attrmode(ATTR_BOLD_ON); ttywrite("\…
#define THEME_INPUT_LABEL() do { } while(0)
#define THEME_INPUT_NORMAL() do { } while(0)
diff --git a/themes/templeos.h b/themes/templeos.h
@@ -11,9 +11,14 @@
#define THEME_SCROLLBAR_NORMAL() do { SETFGCOLOR(0x00, 0x00, 0xaa); SETBG…
#define THEME_SCROLLBAR_TICK_FOCUS() do { SETBGCOLOR(0x00, 0x00, 0xaa); SETFG…
#define THEME_SCROLLBAR_TICK_NORMAL() do { SETBGCOLOR(0x00, 0x00, 0xaa); SETFG…
+#define THEME_LINEBAR() do { SETFGCOLOR(0x00, 0x00, 0xaa); SETBG…
#define THEME_STATUSBAR() do { ttywrite("\x1b[6m"); SETBGCOLOR(0x0…
#define THEME_INPUT_LABEL() do { SETFGCOLOR(0x00, 0x00, 0xaa); SETBG…
#define THEME_INPUT_NORMAL() do { SETFGCOLOR(0x00, 0x00, 0xaa); SETBG…
#undef SCROLLBAR_SYMBOL_BAR
#define SCROLLBAR_SYMBOL_BAR "\xe2\x95\x91" /* symbol: "double vertical" */
+#undef LINEBAR_SYMBOL_BAR
+#define LINEBAR_SYMBOL_BAR "\xe2\x95\x90" /* symbol: "double horizontal" */
+#undef LINEBAR_SYMBOL_RIGHT
+#define LINEBAR_SYMBOL_RIGHT "\xe2\x95\xa3" /* symbol: "double vertical and …
You are viewing proxied material from codemadness.org. The copyright of proxied material belongs to its original authors. Any comments or complaints in relation to proxied material should be directed to the original authors of the content concerned. Please see the disclaimer for more details.