vhakulinen / gnvim

GUI for neovim, without any web bloat
MIT License
1.83k stars 69 forks source link

Feature request: scrollbars #25

Open agraven opened 5 years ago

agraven commented 5 years ago

Scrollbars are a pretty crucial feature for editing larger files, and one I sorely miss from gvim. It would be perfect if it could also obey settings from guioptions.

If pointed in the right direction, I'm more than willing to hack on this myself.

vhakulinen commented 5 years ago

Implementing scrollbars without first implementing support for multigrid would be really hacky, if even possible at all (reliably). Once multigrid support is in place, implementing scrollbars should be trivial.

I would really much love to implement multigrid support, but lately my time (and mind) has been too much on work related things. If you're still interested in implementing scrollbars (which requires support for multigrid), I can brainstorm/guide and share my thoughts with you.

agraven commented 5 years ago

I am definitely interested in doing so. I will definitely attempt if given the right pointers.

vhakulinen commented 5 years ago

To support multigrid events, we first need to patch daa84/neovim-lib and add multigrid UI option here. No need to get it actually patched upstream, you can just point cargo to your local copy of that crate (to get things rolling). After enabling multigrid, you can run gnvim, but everything will fail miserably. Problems resides here. I did some experiments and here is a diff that manages to draw part of the (currently only) window, with multigrid enabled:

diff --git a/src/main.rs b/src/main.rs
index ef0cc46..5fe18f5 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -127,6 +127,7 @@ fn build(app: &gtk::Application, opts: &Options) {
     ui_opts.set_popupmenu_external(!opts.disable_ext_popupmenu);
     ui_opts.set_tabline_external(!opts.disable_ext_tabline);
     ui_opts.set_cmdline_external(!opts.disable_ext_cmdline);
+    ui_opts.set_multigrid_external(true);

     ui_opts.set_wildmenu_external(true);
     nvim.ui_attach(80, 30, &ui_opts)
diff --git a/src/ui/grid/grid.rs b/src/ui/grid/grid.rs
index dc9692f..576382a 100644
--- a/src/ui/grid/grid.rs
+++ b/src/ui/grid/grid.rs
@@ -52,6 +52,9 @@ impl Display for MouseButton {
 /// Single grid in the neovim UI. This matches the `ui-linegrid` stuff in
 /// the ui.txt documentation for neovim.
 pub struct Grid {
+    /// Fixed container, for grids other that the "base" grid. A base grid
+    /// has id of 1 (see `Grid::new`).
+    fixed: Option<gtk::Fixed>,
     /// Our internal "widget". This is what is drawn to the screen.
     da: DrawingArea,
     /// EventBox to get mouse events for this grid.
@@ -66,7 +69,7 @@ pub struct Grid {
 }

 impl Grid {
-    pub fn new(_id: u64) -> Self {
+    pub fn new(id: u64) -> Self {
         let da = DrawingArea::new();
         let ctx = Arc::new(ThreadGuard::new(None));

@@ -101,7 +104,18 @@ impl Grid {
         eb.add_events(EventMask::SCROLL_MASK.bits() as i32);
         eb.add(&da);

+        let fixed = if id != 1 {
+            let fixed = gtk::Fixed::new();
+            da.set_size_request(300, 300);
+            fixed.put(&eb, 0, 0);
+            fixed.show_all();
+            Some(fixed)
+        } else {
+            None
+        };
+
         Grid {
+            fixed,
             da: da,
             eb: eb,
             context: ctx,
@@ -111,7 +125,11 @@ impl Grid {
     }

     pub fn widget(&self) -> gtk::Widget {
-        self.eb.clone().upcast()
+        if let Some(ref fixed) = self.fixed {
+            fixed.clone().upcast()
+        } else {
+            self.eb.clone().upcast()
+        }
     }

     pub fn flush(&self, hl_defs: &HlDefs) {
@@ -134,6 +152,15 @@ impl Grid {
         self.im_context = Some(im_context.clone());
     }

+    /// "Forces" a context for this grid. Grid's widget needs to have a parent
+    /// before calling this function.
+    pub fn force_context(&self) {
+        if let Some(ref fixed) = self.fixed {
+            fixed.realize();
+        }
+        self.da.realize();
+    }
+
     /// Returns position (+ width and height) for cell (row, col) relative
     /// to the top level window of this grid.
     pub fn get_rect_for_cell(&self, row: u64, col: u64) -> gdk::Rectangle {
diff --git a/src/ui/ui.rs b/src/ui/ui.rs
index 2b413de..ea6324f 100644
--- a/src/ui/ui.rs
+++ b/src/ui/ui.rs
@@ -531,7 +531,18 @@ fn handle_redraw_event(
                 grid.cursor_goto(*row, *col);
             }
             RedrawEvent::GridResize(grid, width, height) => {
-                let grid = state.grids.get(grid).unwrap();
+                let overlay = state.overlay.clone();
+                let grid = state
+                    .grids
+                    .entry(*grid)
+                    .or_insert_with(|| {
+                        let grid = Grid::new(*grid);
+                        grid.widget().realize();
+                        overlay.add_overlay(&grid.widget());
+                        grid.force_context();
+                        grid.widget().show_all();
+                        grid
+                    });
                 grid.resize(*width, *height);
             }
             RedrawEvent::GridClear(grid) => {

From this point onward, you're in mercy of multigrid docs. I suggest reading through the whole :h ui and parts of :h api. Gtk can be weird, but the externalized GUI elements should provide examples if you hit some road blocks.

Gnvim was designed multigrids in mind, so things should be relatively easy. There are probably a ton of things I should mention, but here is a direction for you to look at. Its not much, but its something. Feel free to ask questions (I'm also on the neovim gitter every now and then, tho' a bit less lately). If you have questions about the neovim side of things (about the API and such), @bfredl is the master mind behind it all (afaik) and he has helped me a lot with this whole project.

badosu commented 5 years ago

The main reason I prefer gnvim to other frontends is that it's simple and unobtrusive.

This particular use case I cover by having the percentage and line/col numbers on the status line. So I think it's good to have it but if possible to have it configurable (the scrollbar presence, multigrid is a better model anyway).

agraven commented 5 years ago

Oh definitely. In terms of configuring it, I think obeying the related flags in the guioptions option should be adequate, unless we want to change the model and allow for a scrollbar per buffer instead of one on each side.

vhakulinen commented 5 years ago

When it comes to configurability, obeying guioptions is the way to go. Gnvim just doesn't currently support any of it. Initial multigrid support should not really change anything (since neovim is still controlling the windowing), but it allows GUIs to add more "GUI" features (such as scroll bars, shadows, window padding etc.). It also allows GUIs to take control of the windowing (think about window managers in linux) - partially, completely or not at all. Pretty much up to the GUI.

My idea (and desire) is to take control of the windowing so we can escape the limitations of the global grid. This should be done in such fashion that its simple, fast and intuitive to existing nvim users. Having the "vanilla" option available too should be possible - and probably wise. But first, we need to implement the basics (multigrid support in this case).

smolck commented 5 years ago

@agraven Have you been able to work on this? I would like to help out; just want to make sure I don't redo anything you (or others) have already done.

agraven commented 5 years ago

I've only done extremely basic changes, as I haven't had the time to familiarize myself with the codebase. It won't matter too much if the minimal changes I have made are redone, as they could probably be done better with a better image of the whole picture anyway.

vhakulinen commented 4 years ago

Since the ext_multigrid support is almost done and its been quite solid for me, I decided to play around with the scrollbars on top of the multigrid support. They are quite trivial to implement (at least to work as one-way): https://github.com/vhakulinen/gnvim/tree/feature/scrollbars.

Looks like this:

image

Svanto24 commented 2 years ago

Awesome! Have they been implemented, in the end?