.
This commit is contained in:
289
ogui.ml
289
ogui.ml
@ -17,6 +17,7 @@ module Margin = struct
|
|||||||
bottom : size1;
|
bottom : size1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let empty = { left = 0.; right = 0.; top = 0.; bottom = 0. }
|
||||||
let symmetric h w = { left = w; right = w; top = h; bottom = h }
|
let symmetric h w = { left = w; right = w; top = h; bottom = h }
|
||||||
let sum t : size2 = Size2.v (t.left +. t.right) (t.top +. t.bottom)
|
let sum t : size2 = Size2.v (t.left +. t.right) (t.top +. t.bottom)
|
||||||
|
|
||||||
@ -24,6 +25,15 @@ module Margin = struct
|
|||||||
Box2.v
|
Box2.v
|
||||||
(V2.v (Box2.minx b +. t.left) (Box2.miny b +. t.top))
|
(V2.v (Box2.minx b +. t.left) (Box2.miny b +. t.top))
|
||||||
(V2.v (Box2.maxx b -. t.right) (Box2.maxy b -. t.bottom))
|
(V2.v (Box2.maxx b -. t.right) (Box2.maxy b -. t.bottom))
|
||||||
|
|
||||||
|
let outer t b =
|
||||||
|
Box2.(
|
||||||
|
v
|
||||||
|
(V2.v (minx b -. t.left) (miny b -. t.top))
|
||||||
|
(V2.v (maxx b +. t.right) (maxy b +. t.bottom)))
|
||||||
|
|
||||||
|
let pp ppf t =
|
||||||
|
F.pf ppf "l=%f@;r=%f@;t=%f@;b=%f" t.left t.right t.top t.bottom
|
||||||
end
|
end
|
||||||
|
|
||||||
type margin = Margin.t
|
type margin = Margin.t
|
||||||
@ -1296,7 +1306,38 @@ end
|
|||||||
module Layout = struct
|
module Layout = struct
|
||||||
open Gg
|
open Gg
|
||||||
|
|
||||||
type frame = { t : t; mutable size : size }
|
module Style = struct
|
||||||
|
type t = {
|
||||||
|
stroke : float option * Gv.Color.t;
|
||||||
|
fill : Gv.Color.t;
|
||||||
|
margin : Margin.t;
|
||||||
|
}
|
||||||
|
|
||||||
|
let default =
|
||||||
|
{
|
||||||
|
stroke = (None, Gv.Color.transparent);
|
||||||
|
fill = Gv.Color.transparent;
|
||||||
|
margin = Margin.empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
let pp ppf t =
|
||||||
|
F.pf ppf "%a"
|
||||||
|
F.(
|
||||||
|
record
|
||||||
|
[
|
||||||
|
field "stroke"
|
||||||
|
(fun t -> t.stroke)
|
||||||
|
(hbox
|
||||||
|
@@ pair ~sep:comma
|
||||||
|
(option ~none:(any "None") float)
|
||||||
|
pp_color);
|
||||||
|
field "fill" (fun t -> t.fill) pp_color;
|
||||||
|
field "margin" (fun t -> t.margin) Margin.pp;
|
||||||
|
])
|
||||||
|
t
|
||||||
|
end
|
||||||
|
|
||||||
|
type frame = { t : t; mutable size : size; style : Style.t }
|
||||||
|
|
||||||
and t =
|
and t =
|
||||||
[ `Box of [ `H | `V | `Z ] * frame list
|
[ `Box of [ `H | `V | `Z ] * frame list
|
||||||
@ -1305,23 +1346,52 @@ module Layout = struct
|
|||||||
| `TextEdit of TextEdit.t
|
| `TextEdit of TextEdit.t
|
||||||
| `None ]
|
| `None ]
|
||||||
|
|
||||||
and size =
|
and size = [ `Fixed of size2 | `Max | `Min ]
|
||||||
[ `Fixed of p2 | `Percent (* of container *) of p2 | `Auto ]
|
|
||||||
|
|
||||||
let frame ?(size = `Auto) t : frame = { t; size }
|
let frame ?(size = `Max) ?(style = Style.default) t : frame =
|
||||||
let box d t = frame (`Box (d, t))
|
{ t; size; style }
|
||||||
|
|
||||||
|
let box d ?style t = frame ?style (`Box (d, t))
|
||||||
let hbox, vbox, zbox = (box `H, box `V, box `Z)
|
let hbox, vbox, zbox = (box `H, box `V, box `Z)
|
||||||
|
|
||||||
let pp_t_frame ppf f =
|
let textedit_style =
|
||||||
|
Style.
|
||||||
|
{
|
||||||
|
default with
|
||||||
|
stroke = (Some 1.2, Gv.Color.rgbf ~r:0.9 ~g:0.9 ~b:0.9);
|
||||||
|
margin = Margin.symmetric 10. 10.;
|
||||||
|
}
|
||||||
|
|
||||||
|
let textedit ?size ?(style = textedit_style) te =
|
||||||
|
frame ?size ~style (`TextEdit te)
|
||||||
|
|
||||||
|
let pp_dir ppf (t : [ `H | `V | `Z ]) =
|
||||||
F.pf ppf "%s"
|
F.pf ppf "%s"
|
||||||
(match f with
|
(match t with `H -> "`H" | `V -> "`V" | `Z -> "`Z")
|
||||||
| `Hbox -> "`Hbox"
|
|
||||||
| `Vbox -> "`Vbox"
|
let pp_t ppf (t : t) =
|
||||||
| `Buffer -> "`Buffer"
|
F.pf ppf "%s"
|
||||||
| `TextEdit -> "`TextEdit"
|
(match t with
|
||||||
| `S s -> F.str "%s" s
|
| `Box (d, _) -> F.str "`Box %a" pp_dir d
|
||||||
|
| `Buffer _ -> "`Buffer"
|
||||||
|
| `TextEdit _ -> "`TextEdit"
|
||||||
|
| `String s -> F.str "`String %s" s
|
||||||
| `None -> "`None")
|
| `None -> "`None")
|
||||||
|
|
||||||
|
let pp_size ppf = function
|
||||||
|
| `Fixed p -> F.pf ppf "`Fixed %a" Gg.V2.pp p
|
||||||
|
| `Max -> F.pf ppf "`Max"
|
||||||
|
| `Min -> F.pf ppf "`Min"
|
||||||
|
|
||||||
|
let pp_frame =
|
||||||
|
F.(
|
||||||
|
record
|
||||||
|
[
|
||||||
|
field "t" (fun t -> t.t) pp_t;
|
||||||
|
field "size" (fun t -> t.size) pp_size;
|
||||||
|
field "style" (fun t -> t.style) Style.pp;
|
||||||
|
])
|
||||||
|
|
||||||
let parse_t_frame s =
|
let parse_t_frame s =
|
||||||
match s with
|
match s with
|
||||||
| "`Box" -> `Vbox
|
| "`Box" -> `Vbox
|
||||||
@ -1335,105 +1405,104 @@ module Painter = struct
|
|||||||
open Layout
|
open Layout
|
||||||
open Gg
|
open Gg
|
||||||
|
|
||||||
|
let draw_box (t : Gv.t) ~(box : Gg.box2) ~(style : Layout.Style.t) =
|
||||||
|
let open Gv in
|
||||||
|
let open Box2 in
|
||||||
|
Path.begin_ t;
|
||||||
|
Path.rect t ~x:(minx box) ~y:(miny box) ~w:(w box) ~h:(h box);
|
||||||
|
set_fill_color t ~color:style.fill;
|
||||||
|
set_stroke_color t ~color:(snd style.stroke);
|
||||||
|
(match style.stroke with
|
||||||
|
| None, _ -> ()
|
||||||
|
| Some width, _ ->
|
||||||
|
set_stroke_width t ~width;
|
||||||
|
stroke t);
|
||||||
|
fill t
|
||||||
|
|
||||||
|
let set_text_format (t : Gv.t) (format : TextLayout.text_format) =
|
||||||
|
let font_name, font_size =
|
||||||
|
match format.font_id with
|
||||||
|
| Default -> ("mono", 18.)
|
||||||
|
| FontId (s, size) -> (s, size)
|
||||||
|
in
|
||||||
|
let open Gv in
|
||||||
|
Text.set_font_face t ~name:font_name;
|
||||||
|
Text.set_size t ~size:font_size;
|
||||||
|
Text.set_align t ~align:Align.(left lor top)
|
||||||
|
|
||||||
let paint_galley (t : Gv.t) (g : TextLayout.galley) : box2 Lwt.t =
|
let paint_galley (t : Gv.t) (g : TextLayout.galley) : box2 Lwt.t =
|
||||||
TextBuffer.contents g.job.text >>= fun contents ->
|
TextBuffer.contents g.job.text >>= fun contents ->
|
||||||
let contents_len = String.length contents in
|
let contents_len = String.length contents in
|
||||||
(*F.epr
|
|
||||||
"Painter.galley (String.length g.job.text)=%d (Array.length \
|
|
||||||
g.rows)=%d @."
|
|
||||||
contents_len (Array.length g.rows);
|
|
||||||
F.epr "g.job=%a@." TextLayout.pp_layout_job g.job;
|
|
||||||
F.epr "g.rows=%a@." F.(braces (array TextLayout.pp_row)) g.rows; *)
|
|
||||||
g.rows
|
g.rows
|
||||||
|> Array.fold_left
|
|> ( Array.iter @@ fun (row : TextLayout.row) ->
|
||||||
(fun (br : box2) (row : TextLayout.row) ->
|
let sections =
|
||||||
let sections =
|
List.filter
|
||||||
List.filter
|
(fun (r : TextLayout.layout_section) ->
|
||||||
(fun (r : TextLayout.layout_section) ->
|
fst r.byte_range <= row.text_row.end_index
|
||||||
fst r.byte_range <= row.text_row.end_index
|
&& snd r.byte_range > row.text_row.start_index)
|
||||||
&& snd r.byte_range > row.text_row.start_index)
|
Array.(
|
||||||
(Array.to_list
|
to_list
|
||||||
@@ Array.sub g.job.sections row.section_index_at_start
|
@@ sub g.job.sections row.section_index_at_start
|
||||||
(Array.length g.job.sections
|
@@ (length g.job.sections - row.section_index_at_start))
|
||||||
- row.section_index_at_start))
|
in
|
||||||
in
|
assert (List.length sections > 0);
|
||||||
assert (List.length sections > 0);
|
let y = Box2.miny row.rect in
|
||||||
|
List.fold_left
|
||||||
|
(fun x (sec : TextLayout.layout_section) ->
|
||||||
|
let start, end_ =
|
||||||
|
Stdlib.
|
||||||
|
( row.text_row.start_index
|
||||||
|
|> max (fst sec.byte_range)
|
||||||
|
|> min contents_len,
|
||||||
|
row.text_row.end_index |> min contents_len
|
||||||
|
|> min (snd sec.byte_range) )
|
||||||
|
in
|
||||||
|
let metrics = Gv.Text.metrics t in
|
||||||
|
let bounds =
|
||||||
|
if start == row.text_row.end_index then
|
||||||
|
(* hack to display cursor at end of row *)
|
||||||
|
Gv.Text.bounds t ~x ~y:0. " "
|
||||||
|
else Gv.Text.bounds t ~x ~y:0. ~start ~end_ contents
|
||||||
|
in
|
||||||
|
|
||||||
(*F.epr "paint_galley sections:%a@."
|
let line_height =
|
||||||
F.(list TextLayout.pp_layout_section)
|
Option.value ~default:metrics.line_height
|
||||||
sections; *)
|
sec.format.line_height
|
||||||
ignore
|
in
|
||||||
(List.fold_left
|
|
||||||
(fun x (sec : TextLayout.layout_section) ->
|
|
||||||
let start, end_ =
|
|
||||||
( min (contents_len - 1)
|
|
||||||
(max 0
|
|
||||||
(max (fst sec.byte_range)
|
|
||||||
row.text_row.start_index)),
|
|
||||||
min (contents_len - 1)
|
|
||||||
(max 0
|
|
||||||
(min (snd sec.byte_range)
|
|
||||||
row.text_row.end_index)) )
|
|
||||||
in
|
|
||||||
let font_name, font_size =
|
|
||||||
match sec.format.font_id with
|
|
||||||
| Default -> ("mono", 18.)
|
|
||||||
| FontId (s, size) -> (s, size)
|
|
||||||
in
|
|
||||||
let open Gv in
|
|
||||||
Text.set_font_face t ~name:font_name;
|
|
||||||
Text.set_size t ~size:font_size;
|
|
||||||
Text.set_align t ~align:Align.(left lor top);
|
|
||||||
let metrics = Gv.Text.metrics t in
|
|
||||||
let bounds =
|
|
||||||
if start == row.text_row.end_index then
|
|
||||||
(* hack to display cursor at end of row *)
|
|
||||||
Gv.Text.bounds t ~x ~y:0. " "
|
|
||||||
else
|
|
||||||
Gv.Text.bounds t ~x ~y:0. ~start ~end_ contents
|
|
||||||
in
|
|
||||||
|
|
||||||
Path.begin_ t;
|
draw_box t
|
||||||
Path.rect t ~x ~y:(Box2.miny row.rect)
|
~box:
|
||||||
~w:bounds.advance ~h:metrics.line_height;
|
Box2.(v (V2.v x y) (V2.v bounds.advance line_height))
|
||||||
set_fill_color t ~color:sec.format.background;
|
~style:
|
||||||
set_stroke_color t
|
Layout.Style.
|
||||||
~color:(Gv.Color.rgbf ~r:0.9 ~g:0.2 ~b:0.2);
|
{ default with fill = sec.format.background };
|
||||||
set_stroke_width t ~width:2.0;
|
|
||||||
fill t;
|
|
||||||
|
|
||||||
(* stroke t; *)
|
set_text_format t sec.format;
|
||||||
set_fill_color t ~color:sec.format.color;
|
Gv.set_fill_color t ~color:sec.format.color;
|
||||||
(*F.epr "paint_galley row=%d:%d %d:%d %S@."
|
Gv.Text.text_w t ~x ~y ~start ~end_ contents)
|
||||||
row.text_row.start_index row.text_row.end_index
|
(Box2.minx row.rect) sections
|
||||||
start end_
|
|> ignore )
|
||||||
(String.sub contents start (end_ - start)); *)
|
|> ignore;
|
||||||
Text.text_w t ~x ~y:(Box2.miny row.rect) ~start
|
Lwt.return g.rect
|
||||||
~end_ contents)
|
|
||||||
(Box2.minx row.rect) sections);
|
|
||||||
Box2.(union br row.rect))
|
|
||||||
Box2.empty
|
|
||||||
|> Lwt.return
|
|
||||||
|
|
||||||
let rec layout (box : box2) (ui : Ui.t) (frame : frame) : box2 Lwt.t
|
let rec layout (box : box2) (ui : Ui.t) (frame : frame) : box2 Lwt.t
|
||||||
=
|
=
|
||||||
match frame.t with
|
let box' = Margin.inner frame.style.margin box in
|
||||||
|
(match frame.t with
|
||||||
| `Box (dir, ll) ->
|
| `Box (dir, ll) ->
|
||||||
Lwt_list.fold_left_s
|
Lwt_list.fold_left_s
|
||||||
(fun (o : box2) f ->
|
(fun (c : box2) f ->
|
||||||
layout
|
layout c ui f >>= fun r ->
|
||||||
(match dir with
|
let c' =
|
||||||
| `H ->
|
let open Box2 in
|
||||||
Box2.of_pts
|
match dir with
|
||||||
V2.(v (Box2.minx o) (Box2.miny box))
|
| `V -> Box2.of_pts (V2.v (minx c) (maxy r)) (max c)
|
||||||
(Box2.br_pt o)
|
| `H -> Box2.of_pts (V2.v (maxx r) (miny c)) (max c)
|
||||||
| `V ->
|
| `Z -> box
|
||||||
Box2.of_pts
|
in
|
||||||
V2.(v (Box2.minx box) (Box2.miny o))
|
|
||||||
(Box2.br_pt o)
|
Lwt.return c')
|
||||||
| `Z -> box)
|
box' ll
|
||||||
ui f)
|
|
||||||
box ll
|
|
||||||
| `TextEdit t ->
|
| `TextEdit t ->
|
||||||
let font =
|
let font =
|
||||||
match Gv.Text.find_font ui.gv ~name:"mono" with
|
match Gv.Text.find_font ui.gv ~name:"mono" with
|
||||||
@ -1442,15 +1511,21 @@ module Painter = struct
|
|||||||
in
|
in
|
||||||
(if t.multiline then
|
(if t.multiline then
|
||||||
TextLayout.simple t.text ~format:t.text_format
|
TextLayout.simple t.text ~format:t.text_format
|
||||||
(Option.value ~default:(Box2.w box) t.desired_width)
|
(Option.value ~default:(Box2.w box') t.desired_width)
|
||||||
else TextLayout.singleline t.text t.text_format)
|
else TextLayout.singleline t.text t.text_format)
|
||||||
>>= fun layout_job ->
|
>>= fun layout_job ->
|
||||||
Ui.fonts ui.gv (fun gv ->
|
Ui.fonts ui.gv
|
||||||
TextLayout.layout gv font
|
TextLayout.(
|
||||||
(TextLayout.with_cursor t.cursor
|
fun gv ->
|
||||||
(TextLayout.with_mark t.mark t.cursor.index
|
(layout gv font
|
||||||
layout_job))
|
(with_cursor t.cursor
|
||||||
(Box2.o box))
|
(with_mark t.mark t.cursor.index layout_job)))
|
||||||
|
(Box2.o box'))
|
||||||
>>= fun galley -> paint_galley ui.gv galley
|
>>= fun galley -> paint_galley ui.gv galley
|
||||||
| _ -> Lwt.return box
|
| _ -> Lwt.return box)
|
||||||
|
>>= fun r ->
|
||||||
|
F.epr "@[<v>layout@;box=%a@;box'=%a@;r=%a@;%a@]@." Box2.pp box
|
||||||
|
Box2.pp box' Box2.pp r pp_frame frame;
|
||||||
|
draw_box ui.gv ~box:r ~style:frame.style;
|
||||||
|
Lwt.return r
|
||||||
end
|
end
|
||||||
|
|||||||
14
oplevel.ml
14
oplevel.ml
@ -115,12 +115,14 @@ let () =
|
|||||||
Layout.(
|
Layout.(
|
||||||
vbox
|
vbox
|
||||||
[
|
[
|
||||||
frame
|
textedit
|
||||||
(`TextEdit
|
(TextEdit.multiline ui
|
||||||
(TextEdit.multiline ui
|
(TextBuffer.of_repo ~path:[ "README" ] ~repo:rootrepo));
|
||||||
(TextBuffer.of_repo
|
textedit
|
||||||
~path:[ "README" ] (*[ ".config"; "init.ml" ] *)
|
(TextEdit.multiline ui
|
||||||
~repo:rootrepo)));
|
(TextBuffer.of_repo
|
||||||
|
~path:[ ".config"; "init.ml" ]
|
||||||
|
~repo:rootrepo));
|
||||||
])
|
])
|
||||||
in
|
in
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user