Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added horizontal scrolling functionality #52

Draft
wants to merge 20 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 44 additions & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
name: Install, test and format

on:
push:
branches:
- "main"
pull_request:
branches:
- "main"

permissions: read-all

jobs:
build:
strategy:
fail-fast: false
matrix:
os:
- macos-latest
- ubuntu-latest
ocaml-compiler:
- "5.1"

runs-on: ${{ matrix.os }}

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Set-up OCaml version:${{ matrix.ocaml-compiler }} in os:${{ matrix.os }}
uses: ocaml/setup-ocaml@v2
with:
ocaml-compiler: ${{ matrix.ocaml-compiler }}

- name: Install Dependencies
run: |
opam install --deps-only -t -y .
opam install ocamlformat.0.26.1

- name: Test
run: opam exec -- dune runtest

- name: Format
run: opam exec -- dune build @fmt
50 changes: 50 additions & 0 deletions .github/workflows/ocaml-ci-setup.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
name: CI Run test

on:
push:
branches:
- 'main'
pull_request:
branches:
- 'main'

jobs:
# See: https://github.com/ocaml/setup-ocaml
setup-ocaml:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: Setup OCaml environment
run: setup-ocaml

install-dependencies:
runs-on: ubuntu-latest
needs: setup-ocaml
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: Install project dependencies
run: opam install --deps-only -t -y

run-tests:
runs-on: ubuntu-latest
needs: install-dependencies
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: Run tests
run: dune runtest

check-formatting:
runs-on: ubuntu-latest
needs: install-dependencies
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: Check formatting
env:
OCAMLFORMAT: "0.26.1"
run: dune build @fmt


15 changes: 13 additions & 2 deletions lib/interactive_viewer.ml
Original file line number Diff line number Diff line change
Expand Up @@ -110,12 +110,19 @@ let view (patches : Patch.t list) =
| None -> failwith "zipper_of_list: empty list"
in
let curr_scroll_state = Lwd.var W.default_scroll_state in
let curr_scroll_state_1 = Lwd.var W.default_scroll_state in
let change_scroll_state _action state =
let off_screen = state.W.position > state.W.bound in
if off_screen then
let y_off_screen = state.W.position > state.W.bound in
if y_off_screen then
Lwd.set curr_scroll_state { state with position = state.W.bound }
else Lwd.set curr_scroll_state state
in
let change_scroll_state_1 _action state =
let x_off_screen = state.W.position > state.W.bound in
if x_off_screen then
Lwd.set curr_scroll_state_1 { state with position = state.W.bound }
else Lwd.set curr_scroll_state_1 state
in
W.vbox
[
operation_info z_patches;
Expand All @@ -125,6 +132,10 @@ let view (patches : Patch.t list) =
~state:(Lwd.get curr_scroll_state)
~change:change_scroll_state
@@ current_hunks z_patches;
W.hscroll_area
~state:(Lwd.get curr_scroll_state_1)
~change:change_scroll_state_1
@@ current_hunks z_patches;
Lwd.pure
@@ Ui.keyboard_area
(function
Expand Down
114 changes: 87 additions & 27 deletions vendor/lwd/lib/nottui/nottui_widgets.ml
Original file line number Diff line number Diff line change
Expand Up @@ -141,57 +141,117 @@ let menu_overlay wm g ?(dx=0) ?(dy=0) body around =

let scroll_step = 1

(* type scroll_state = {
y_position: int;
x_position: int;
y_bound : int;
x_bound : int;
y_visible : int;
x_visible : int;
y_total : int;
x_total : int;
} *)
type scroll_state = {
position: int;
bound : int;
visible : int;
total : int;
}

let default_scroll_state = { position = 0; bound = 0; visible = 0; total = 0 }

(* let default_scroll_state = { y_position = 0; y_bound = 0; y_visible = 0; y_total = 0; x_position = 0; x_bound = 0; x_visible = 0; x_total = 0; } *)
let default_scroll_state = { position = 0; bound = 0; visible = 0; total = 0; }

let vscroll_area ~state ~change t =
let visible = ref (-1) in
let total = ref (-1) in
let scroll state delta =
let position = state.position + delta in
let position = clampi position ~min:0 ~max:state.bound in
if position <> state.position then
change `Action {state with position};
`Handled
let y_visible = ref (-1) in
let y_total = ref (-1) in
let scroll state delta del =
let y_position = state.position + delta in
let position = clampi y_position ~min:0 ~max:state.bound in
if position <> state.position then
change `Action {state with position};
`Handled
in
let focus_handler state = function
(*| `Arrow `Left , _ -> scroll (-scroll_step) 0*)
(*| `Arrow `Right, _ -> scroll (+scroll_step) 0*)
| `Arrow `Up , [] -> scroll state (-scroll_step)
| `Arrow `Down , [] -> scroll state (+scroll_step)
| `Page `Up, [] -> scroll state ((-scroll_step) * 8)
| `Page `Down, [] -> scroll state ((+scroll_step) * 8)
| `Arrow `Left , [] -> scroll state (0) (-scroll_step)
| `Arrow `Right, [] -> scroll state (0) (+scroll_step)
| `Arrow `Up , [] -> scroll state (-scroll_step) (0)
| `Arrow `Down , [] -> scroll state (+scroll_step) (0)
| `Page `Up, [] -> scroll state ((-scroll_step) * 8) (0)
| `Page `Down, [] -> scroll state ((+scroll_step) * 8) (0)
| _ -> `Unhandled
in
let scroll_handler state ~x:_ ~y:_ = function
| `Scroll `Up -> scroll state (-scroll_step)
| `Scroll `Down -> scroll state (+scroll_step)
| `Scroll `Up -> scroll state (-scroll_step) (0)
| `Scroll `Down -> scroll state (+scroll_step) (0)

| _ -> `Unhandled
in
Lwd.map2 t state ~f:begin fun t state ->
t
|> Ui.shift_area 0 state.position
|> Ui.resize ~h:0 ~sh:1
|> Ui.size_sensor (fun ~w:_ ~h ->
let tchange =
if !total <> (Ui.layout_spec t).Ui.h
then (total := (Ui.layout_spec t).Ui.h; true)
|> Ui.size_sensor (fun ~w ~h ->
let tychange =
if !y_total <> (Ui.layout_spec t).Ui.h
then (y_total := (Ui.layout_spec t).Ui.h; true)
else false
in
let vychange =
if !y_visible <> h
then (y_visible := h; true)
else false
in
if tychange || vychange then
change `Content {state with visible = !y_visible; total = !y_total;
bound = maxi 0 (!y_total - !y_visible);}
)
|> Ui.mouse_area (scroll_handler state)
|> Ui.keyboard_area (focus_handler state)
end
let hscroll_area ~state ~change t =
let x_visible = ref (-1) in
let x_total = ref (-1) in
let scroll state delta del =
let x_position = state.position + del in
let position = clampi x_position ~min:0 ~max:state.bound in
if x_position <> state.position then
change `Action {state with position};
`Handled
in
let focus_handler state = function
| `Arrow `Left , [] -> scroll state (0) (-scroll_step)
| `Arrow `Right, [] -> scroll state (0) (+scroll_step)
| `Arrow `Up , [] -> scroll state (-scroll_step) (0)
| `Arrow `Down , [] -> scroll state (+scroll_step) (0)
| `Page `Up, [] -> scroll state ((-scroll_step) * 8) (0)
| `Page `Down, [] -> scroll state ((+scroll_step) * 8) (0)
| _ -> `Unhandled
in
let scroll_handler state ~x:_ ~y:_ = function
| `Scroll `Up -> scroll state (-scroll_step) (0)
| `Scroll `Down -> scroll state (+scroll_step) (0)

| _ -> `Unhandled
in
Lwd.map2 t state ~f:begin fun t state ->
t
|> Ui.shift_area state.position 0
|> Ui.resize ~w:0 ~sw:1
|> Ui.size_sensor (fun ~w ~h ->
let txchange =
if !x_total <> (Ui.layout_spec t).Ui.w
then (x_total := (Ui.layout_spec t).Ui.w; true)
else false
in
let vchange =
if !visible <> h
then (visible := h; true)
let vxchange =
if !x_visible <> w
then (x_visible := w; true)
else false
in
if tchange || vchange then
change `Content {state with visible = !visible; total = !total;
bound = maxi 0 (!total - !visible); }
if txchange || vxchange then
change `Content {state with visible = !x_visible; total = !x_total;
bound = maxi 0 (!x_total - !x_visible); }
)
|> Ui.mouse_area (scroll_handler state)
|> Ui.keyboard_area (focus_handler state)
Expand Down
9 changes: 7 additions & 2 deletions vendor/lwd/lib/nottui/nottui_widgets.mli
Original file line number Diff line number Diff line change
Expand Up @@ -31,14 +31,19 @@ val sub_entry : string -> (unit -> unit) -> ui

(* FIXME Explain how scrolling works *)
val scroll_step : int
type scroll_state = { position : int; bound : int; visible : int; total : int }
type scroll_state = { position : int; bound : int; visible : int; total : int; }
val default_scroll_state : scroll_state
val vscroll_area :
state:scroll_state Lwd.t ->
change:([> `Action | `Content ] -> scroll_state -> unit) ->
ui Lwd.t -> ui Lwd.t

val scroll_area :
val hscroll_area :
state:scroll_state Lwd.t ->
change:([> `Action | `Content ] -> scroll_state -> unit) ->
ui Lwd.t -> ui Lwd.t

val scroll_area :
?offset:int * int -> ui Lwd.t -> ui Lwd.t

val scrollbox: ui Lwd.t -> ui Lwd.t
Expand Down