svn commit: r1932985 - subversion/trunk/subversion/svnbrowse

[email protected]
Newsgroups gmane.comp.version-control.subversion.svn
Message-ID <177593426405.1806626.10410676144017882703@svn03-he-fi>
Author: rinrab
Date: Sat Apr 11 19:04:23 2026
New Revision: 1932985

Log:
svnbrowse: Improve application style, adding colours and better composition of
list items and the header infobar.

- Fixed layout of list elements; we can now pad them properly and show
  selection for the whole row.
- The list items now highlight directories with cyan.
- The directories now display '(dir)' instead of size.
- Show branding with title 'Apache Subversion' in the info bar.

* subversion/svnbrowse/pallete.py: Add simple script to test current palette of
  the terminal; for development use only.
* subversion/svnbrowse/svnbrowse.c
  (COLOR_PRIMARY, COLOR_SECONDARY, COLOR_BRANDING): New macros; branding means
   a customly picked colour that represents the tool's overall look/adds some
   originality. It's just something that's shown in the header, that's why.
  (COLOR_PAIR_INFO_BAR, COLOR_PAIR_DIR, COLOR_PAIR_DIR_SELECTED,
   COLOR_PAIR_FILE, COLOR_PAIR_FILE_SELECTED, COLOR_COUNT): New constants,
   defined through a enum.
   (rightpad, leftpad): New functions for string manipulation.
   (format_node_size, format_node_name, get_item_style): Factor-out those
    functions to simplify item rendering.
   (view_draw_item): Rewrite function to use new padding system, utilise new
    function for formatting texts,  and paint the list.
   (view_draw_info_bar): Factor-out function for displaying the info bar.
   (view_draw): Call view_draw_info_bar() and update usage of view_draw_item().
   (sub_main): Initialize colour pairs.

Added:
   subversion/trunk/subversion/svnbrowse/palette.py   (contents, props changed)
Modified:
   subversion/trunk/subversion/svnbrowse/svnbrowse.c

Added: subversion/trunk/subversion/svnbrowse/palette.py
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ subversion/trunk/subversion/svnbrowse/palette.py	Sat Apr 11 19:04:23 2026	(r1932985)
@@ -0,0 +1,50 @@
+#!/bin/python
+
+import curses
+
+def main(stdscr):
+    curses.start_color()
+    curses.use_default_colors()
+
+    WIDTH = 32
+
+    for i in range(0, curses.COLORS):
+        curses.init_pair(i + 1, i, -1)
+    for i in range(0, 255):
+        if (i % WIDTH == 0):
+            stdscr.addstr(i // WIDTH, 0, f"{i} - {i + WIDTH - 1}", 0)
+
+        stdscr.addstr(i // WIDTH, i % WIDTH + 12, "x", curses.color_pair(i))
+
+    stdscr.getch()
+    stdscr.clear()
+
+    for i in range(0, curses.COLORS):
+        curses.init_pair(i + 1, -1, i)
+    for i in range(0, 255):
+        if (i % WIDTH == 0):
+            stdscr.addstr(i // WIDTH, 0, f"{i} - {i + WIDTH - 1}", 0)
+
+        stdscr.addstr(i // WIDTH, i % WIDTH + 12, "x", curses.color_pair(i))
+
+    stdscr.getch()
+    stdscr.clear()
+
+    for i in range(0, 16):
+        curses.init_pair(i + 1, i, -1)
+    for i in range(0, 16):
+        curses.init_pair(i + 16 + 1, -1, i)
+
+    for i in range(0, 16):
+        stdscr.addstr(
+            f"The color number {i} of the ",
+            curses.color_pair(i)
+        )
+        stdscr.addstr(
+            f"standard terminal pallete.\n",
+            curses.color_pair(i + 16)
+        )
+
+    stdscr.getch()
+
+curses.wrapper(main)

Modified: subversion/trunk/subversion/svnbrowse/svnbrowse.c
==============================================================================
--- subversion/trunk/subversion/svnbrowse/svnbrowse.c	Sat Apr 11 12:45:04 2026	(r1932984)
+++ subversion/trunk/subversion/svnbrowse/svnbrowse.c	Sat Apr 11 19:04:23 2026	(r1932985)
@@ -110,6 +110,20 @@ const apr_getopt_option_t svn_browse__op
 
 #define KEY_ESC 27
 
+/* handpicked */
+#define COLOR_PRIMARY      238
+#define COLOR_SECONDARY    240
+#define COLOR_BRANDING      96
+
+enum {
+  COLOR_PAIR_INFO_BAR = 1,
+  COLOR_PAIR_DIR,
+  COLOR_PAIR_DIR_SELECTED,
+  COLOR_PAIR_FILE,
+  COLOR_PAIR_FILE_SELECTED,
+  COLOR_COUNT,
+};
+
 typedef struct svn_browse__view_t {
   /* TODO: store information about terminal screen (a WINDOW* in curses world) */
   svn_browse__model_t *model;
@@ -204,36 +218,129 @@ view_on_event(svn_browse__view_t *view,
   return SVN_NO_ERROR;
 }
 
-static void
-view_draw_item(const svn_browse__item_t *item, int y, svn_boolean_t selected)
+static char *
+rightpad(const char *cstr, int padding, apr_pool_t *result_pool)
+{
+  int len = strlen(cstr);
+  char *result = apr_palloc(result_pool, padding + 1);
+
+  if (len < padding)
+    {
+      memcpy(result, cstr, len);
+      memset(result + len, ' ', padding - len);
+    }
+  else
+    {
+      memcpy(result, cstr, len);
+    }
+
+  result[padding] = '\0';
+  return result;
+}
+
+static char *
+leftpad(const char *cstr, int padding, apr_pool_t *result_pool)
+{
+  int len = strlen(cstr);
+  char *result = apr_palloc(result_pool, padding + 1);
+
+  if (len < padding)
+    {
+      int spaces = padding - len;
+      memset(result, ' ', spaces);
+      memcpy(result + spaces, cstr, len);
+    }
+  else
+    {
+      memcpy(result, cstr, len);
+    }
+
+  result[padding] = '\0';
+  return result;
+}
+
+static const char *
+format_node_size(const svn_browse__item_t *item, apr_pool_t *pool)
 {
-  if (selected)
-    standout();
+  if (item->dirent->kind == svn_node_dir)
+    return "(dir)";
+  else
+    return apr_psprintf(pool, "%ld KiB", item->dirent->size / 1024);
+}
 
+static const char *
+format_node_name(const svn_browse__item_t *item, apr_pool_t *pool)
+{
   if (item->dirent->kind == svn_node_dir)
-    mvprintw(y, 0, "%s/", item->name);
-  else if (item->dirent->kind == svn_node_file)
-    mvprintw(y, 0, "%s", item->name);
+    return apr_pstrcat(pool, item->name, "/", SVN_VA_NULL);
   else
-    abort();
+    return item->name;
+}
+
+static int
+get_item_style(svn_node_kind_t kind, svn_boolean_t selected)
+{
+  switch (kind)
+  {
+    case svn_node_dir:
+      return COLOR_PAIR((selected) ? COLOR_PAIR_DIR_SELECTED
+                                   : COLOR_PAIR_DIR);
+    case svn_node_file:
+      return COLOR_PAIR((selected) ? COLOR_PAIR_FILE_SELECTED
+                                   : COLOR_PAIR_FILE);
+    default:
+      abort();
+  }
+}
+
+static void
+view_draw_item(const svn_browse__item_t *item, int y, svn_boolean_t selected,
+               apr_pool_t *scratch_pool)
+{
+  int attrs = 0;
+  const char *prefix;
 
-  mvprintw(y, COLS - 40, "%8ld KiB  r%-8ld  %s",
-           item->dirent->size / 1024,
-           item->dirent->created_rev,
-           item->dirent->last_author);
+  move(y, 0);
+  attrset(get_item_style(item->dirent->kind, selected));
 
-  if (selected)
-    standend();
+  /* 12 + 12 + (20 + 1) + 2 = 47 */
+
+  addch(' ');
+  addstr(rightpad(format_node_name(item, scratch_pool),
+                  COLS - 47, scratch_pool));
+
+  attrset(get_item_style(svn_node_file, selected));
+  addstr(leftpad(format_node_size(item, scratch_pool), 12, scratch_pool));
+  addstr(leftpad(apr_psprintf(scratch_pool, "r%ld", item->dirent->created_rev),
+                 12, scratch_pool));
+  addch(' ');
+  addstr(rightpad(item->dirent->last_author, 20, scratch_pool));
+  addch(' ');
+}
+
+static void
+view_draw_info_bar(svn_browse__view_t *view, apr_pool_t *scratch_pool)
+{
+  const char *abspath = svn_path_url_add_component2(
+      view->model->root, view->model->current->relpath, scratch_pool);
+  svn_stringbuf_t *buf = svn_stringbuf_create_empty(scratch_pool);
+  const char *prefix = "  ";
+  const char *suffix = "Apache Subversion  ";
+
+  move(0, 0);
+  attrset(COLOR_PAIR(COLOR_PAIR_INFO_BAR));
+  addstr(prefix);
+  addstr(rightpad(apr_psprintf(scratch_pool, "URL: %s", abspath),
+                  COLS - strlen(prefix) - strlen(suffix), scratch_pool));
+  addstr(suffix);
 }
 
 static void
 view_draw(svn_browse__view_t *view, apr_pool_t *pool)
 {
   int i;
-  const char *abspath = svn_path_url_add_component2(
-      view->model->root, view->model->current->relpath, pool);
 
-  mvprintw(0, 4, "Browsing: %s", abspath);
+  view_draw_info_bar(view, pool);
 
   for (i = 0; i < view->model->current->list->nelts; i++)
     {
@@ -243,7 +350,7 @@ view_draw(svn_browse__view_t *view, apr_
       int y = i - view->model->current->scroller_offset;
 
       if (0 <= y && y < LINES)
-        view_draw_item(item, y + 1, selected);
+        view_draw_item(item, y + 1, selected, pool);
     }
 }
 
@@ -472,6 +579,15 @@ sub_main(int *code, int argc, const char
   ESCDELAY = 0;
 #endif /* NCURSES_VERSION */
 
+  start_color();
+  use_default_colors();
+
+  init_pair(COLOR_PAIR_INFO_BAR, COLOR_YELLOW, COLOR_BRANDING);
+  init_pair(COLOR_PAIR_DIR, COLOR_CYAN, -1);
+  init_pair(COLOR_PAIR_DIR_SELECTED, COLOR_CYAN, COLOR_PRIMARY);
+  init_pair(COLOR_PAIR_FILE, -1, -1);
+  init_pair(COLOR_PAIR_FILE_SELECTED, -1, COLOR_PRIMARY);
+
   view = view_make(ctx, pool);
 
   iterpool = svn_pool_create(pool);
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.