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

draft: Implemented some wrapping examples using shape the term package #53

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
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
2 changes: 1 addition & 1 deletion dune-project
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
(description "A longer description")
(depends
(ocaml (>= "5.1"))
(riot (= "0.0.8"))
(riot (= "0.0.9"))
(mdx (and :with-test (>= "2.3.1")))
(tty (>= "0.0.2"))
uuseg)
Expand Down
4 changes: 2 additions & 2 deletions examples/list/dune
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
(executable
(name main)
(libraries minttea spices leaves str))
(name main)
(libraries minttea spices leaves str))
3 changes: 3 additions & 0 deletions examples/text-wrapping/dune
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
(executable
(name main)
(libraries minttea shape-the-term spices leaves))
44 changes: 44 additions & 0 deletions examples/text-wrapping/main.ml
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
open Minttea
open Leaves

type model = { text : Text_input.t; quitting : bool }

let mint = Spices.color "#77e5b7"
let white = Spices.color "#FFFFFF"

let cursor =
Cursor.make ~style:Spices.(default |> bg mint |> fg white |> bold true) ()

let initial_model =
{
text = Text_input.make "" ~placeholder:"Type something" ~cursor ();
quitting = false;
}

let init _ = Command.Hide_cursor

let update (event : Event.t) model =
let s =
match event with
| e ->
if e = Event.KeyDown (Enter, No_modifier) then
({ model with quitting = true }, Command.Quit)
else
let text = Text_input.update model.text e in
({ model with text }, Command.Noop)
in
s

let view model =
let style = Spices.(default |> fg mint |> build) in
let width = 12 in
let show s = Shape_the_term.wrap width @@ style "%s" s in
if model.quitting then
Format.sprintf "\n🐫 Your text wrapped ✨ (to %s characters width):\n%s\n" (string_of_int width)
@@ show @@ Text_input.current_text model.text
else
Format.sprintf "\n🐫 Text area (%s characters width):\n%s\n" (string_of_int width)
@@ show @@ Text_input.view model.text

let app = Minttea.app ~init ~update ~view ()
let () = Minttea.start ~config:(config ~render_mode: `clear ~fps: 60 ()) ~initial_model app
3 changes: 3 additions & 0 deletions examples/word-wrapping/dune
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
(executable
(name main)
(libraries minttea shape-the-term spices leaves))
44 changes: 44 additions & 0 deletions examples/word-wrapping/main.ml
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
open Minttea
open Leaves

type model = { text : Text_input.t; quitting : bool }

let mint = Spices.color "#77e5b7"
let white = Spices.color "#FFFFFF"

let cursor =
Cursor.make ~style:Spices.(default |> bg mint |> fg white |> bold true) ()

let initial_model =
{
text = Text_input.make "" ~placeholder:"Type something" ~cursor ();
quitting = false;
}

let init _ = Command.Hide_cursor

let update (event : Event.t) model =
let s =
match event with
| e ->
if e = Event.KeyDown (Enter, No_modifier) then
({ model with quitting = true }, Command.Quit)
else
let text = Text_input.update model.text e in
({ model with text }, Command.Noop)
in
s

let view model =
let style = Spices.(default |> fg mint |> build) in
let show width s = Shape_the_term.wrap_by_word width @@ style "%s" s in
if model.quitting then
Format.sprintf "\n🐫 Your text wrapped by word ✨ (to 12 characters width):\n%s\n"
@@ show 12 @@ Text_input.current_text model.text
else
(Format.sprintf "\n%s\n" @@ show 36 @@ Text_input.view model.text) ^
Format.sprintf "\n🐫 Wrap by word (to 12 characters width):\n%s\n"
@@ show 12 @@ Text_input.current_text model.text

let app = Minttea.app ~init ~update ~view ()
let () = Minttea.start ~config:(config ~render_mode: `clear ~fps: 60 ()) ~initial_model app
2 changes: 1 addition & 1 deletion minttea.opam
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ bug-reports: "https://github.com/leostera/minttea/issues"
depends: [
"dune" {>= "3.11"}
"ocaml" {>= "5.1"}
"riot" {= "0.0.8"}
"riot" {= "0.0.9"}
"mdx" {with-test & >= "2.3.1"}
"tty" {>= "0.0.2"}
"uuseg"
Expand Down
1 change: 1 addition & 0 deletions minttea/config.ml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
type t = { render_mode : [ `clear | `persist ]; fps : int }
2 changes: 1 addition & 1 deletion minttea/io_loop.ml
Original file line number Diff line number Diff line change
Expand Up @@ -46,5 +46,5 @@ let run runner =
link runner;
let termios = Stdin.setup () in
let _worker = spawn_link (fun () -> loop runner) in
let _ = receive () in
let _ = receive_any () in
Stdin.shutdown termios
10 changes: 6 additions & 4 deletions minttea/minttea.ml
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,23 @@ module Event = Event
module Command = Command
module App = App
module Program = Program
module Config = Config

let config ?(render_mode = `clear) ?(fps = 60) () = Config.{ render_mode; fps }
let app = App.make

let run ?(fps = 60) ~initial_model app =
let prog = Program.make ~app ~fps in
let run ?(config = config ()) ~initial_model app =
let prog = Program.make ~app ~config in
Program.run prog initial_model;
Logger.trace (fun f -> f "terminating")

let start app ~initial_model =
let start ?(config = config ()) app ~initial_model =
let module App = struct
let start () =
Logger.set_log_level None;
let pid =
spawn_link (fun () ->
run app ~initial_model;
run ~config app ~initial_model;
Logger.trace (fun f -> f "about to shutdown");
shutdown ~status:0 ())
in
Expand Down
10 changes: 8 additions & 2 deletions minttea/minttea.mli
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
open Riot

module Config : sig
type t
end

val config : ?render_mode:[ `clear | `persist ] -> ?fps:int -> unit -> Config.t

module Event : sig
type modifier = No_modifier | Ctrl

Expand Down Expand Up @@ -46,5 +52,5 @@ val app :
unit ->
'model App.t

val run : ?fps:int -> initial_model:'model -> 'model App.t -> unit
val start : 'model App.t -> initial_model:'model -> unit
val run : ?config:Config.t -> initial_model:'model -> 'model App.t -> unit
val start : ?config:Config.t -> 'model App.t -> initial_model:'model -> unit
10 changes: 5 additions & 5 deletions minttea/program.ml
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
open Riot

type Message.t += Timer of unit Ref.t | Shutdown
type 'model t = { app : 'model App.t; fps : int }
type 'model t = { app : 'model App.t; config : Config.t }

let make ~app ~fps = { app; fps }
let make ~app ~config = { app; config }

exception Exit

let rec loop renderer (app : 'model App.t) (model : 'model) =
let event =
match receive () with
match receive_any () with
| Timer ref -> Event.Timer ref
| Io_loop.Input event -> event
| message -> Event.Custom message
Expand Down Expand Up @@ -55,14 +55,14 @@ let init { app; _ } initial_model renderer =
Renderer.render renderer view;
loop renderer app initial_model

let run ({ fps; _ } as t) initial_model =
let run ({ config; _ } as t) initial_model =
Printexc.record_backtrace true;
let renderer =
spawn (fun () ->
(* NOTE(@leostera): reintroduce this when riot brings back process-stealing *)
(* process_flag (Priority High); *)
let runner = Process.await_name "Minttea.runner" in
Renderer.run ~fps ~runner)
Renderer.run ~config ~runner)
in
let runner =
spawn (fun () ->
Expand Down
9 changes: 6 additions & 3 deletions minttea/renderer.ml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ type t = {
ticker : Timer.timer;
width : int;
height : int;
render_mode : [ `clear | `persist ];
mutable buffer : string;
mutable last_render : string;
mutable lines_rendered : int;
Expand All @@ -27,7 +28,7 @@ let same_as_last_flush t = t.buffer = t.last_render
let lines t = t.buffer |> String.split_on_char '\n'

let rec loop t =
match receive () with
match receive_any () with
| Shutdown ->
flush t;
restore t
Expand Down Expand Up @@ -61,7 +62,7 @@ and flush t =
let new_lines_this_flush = List.length new_lines in

(* clean last rendered lines *)
if t.lines_rendered > 0 then
if t.render_mode = `clear && t.lines_rendered > 0 then
for _i = 1 to t.lines_rendered - 1 do
Terminal.clear_line ();
Terminal.cursor_up 1
Expand Down Expand Up @@ -107,7 +108,8 @@ let max_fps = 120
let cap fps = Int.max 1 (Int.min fps max_fps) |> Int.to_float
let fps_to_float fps = 1. /. cap fps *. 1_000. |> Int64.of_float

let run ~fps ~runner =
let run ~config ~runner =
let Config.{ render_mode; fps } = config in
let ticker =
Riot.Timer.send_interval ~every:(fps_to_float fps) (self ()) Tick
|> Result.get_ok
Expand All @@ -123,6 +125,7 @@ let run ~fps ~runner =
is_altscreen_active = false;
lines_rendered = 0;
cursor_visibility = `visible;
render_mode;
}

let render pid output = send pid (Render output)
Expand Down
2 changes: 1 addition & 1 deletion minttea/renderer.mli
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
open Riot

val run : fps:int -> runner:Pid.t -> unit
val run : config:Config.t -> runner:Pid.t -> unit
val render : Pid.t -> string -> unit
val enter_alt_screen : Pid.t -> unit
val exit_alt_screen : Pid.t -> unit
Expand Down
Loading