Skip to content

Commit

Permalink
z-index aware text composing
Browse files Browse the repository at this point in the history
  • Loading branch information
Yatao Li committed Aug 15, 2021
1 parent 328bec0 commit 353daaa
Show file tree
Hide file tree
Showing 4 changed files with 117 additions and 71 deletions.
54 changes: 45 additions & 9 deletions ViewModels/GridViewModel.fs
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ open Avalonia.Media
open FSharp.Control.Reactive

open System
open System.Collections.ObjectModel
open model
open System.Collections.Generic

#nowarn "0025"

Expand All @@ -37,7 +37,7 @@ and GridViewModel(_gridid: int, ?_parent: GridViewModel, ?_gridsize: GridSize) a

let m_cursor_vm = new CursorViewModel(None)
let m_popupmenu_vm = new PopupMenuViewModel()
let m_child_grids = ObservableCollection<GridViewModel>()
let m_child_grids = ResizeArray<GridViewModel>()
let m_resize_ev = Event<IGridUI>()
let m_input_ev = Event<int * InputEvent>()
let m_ext_winclose_ev = Event<unit>()
Expand Down Expand Up @@ -65,9 +65,10 @@ and GridViewModel(_gridid: int, ?_parent: GridViewModel, ?_gridsize: GridSize) a
let mutable m_anchor_col = 0
let mutable m_hidden = false
let mutable m_is_external = false
let mutable m_is_float = false
let mutable m_z = -100
let mutable m_winid = 0 // for single-purpose windows e.g. floats and exts


let raiseInputEvent id e = m_input_ev.Trigger(id, e)

let getPos (p: Point) =
Expand Down Expand Up @@ -137,7 +138,7 @@ and GridViewModel(_gridid: int, ?_parent: GridViewModel, ?_gridsize: GridSize) a
let markAllDirty () =
m_griddirty <- true
for c in m_child_grids do
c.MarkAllDirty()
c.MarkDirty()

let rec markDirty ({ row = row; col = col; height = h; width = w } as dirty) =
if h > 1 then
Expand Down Expand Up @@ -302,10 +303,21 @@ and GridViewModel(_gridid: int, ?_parent: GridViewModel, ?_gridsize: GridSize) a
let setMouse (en:bool) =
m_mouse_en <- en

let getRootGrid() =
let mutable p = m_parent
let mutable q = this
while p.IsSome do
q <- p.Value
p <- p.Value.Parent
q

let closeGrid() =
trace _gridid "closeGrid"
if m_is_external then
m_ext_winclose_ev.Trigger()
elif m_is_float then
m_hidden <- true
getRootGrid().MarkDirty()

let setWinPos startrow startcol r c f =
let oldRegion = { row = m_anchor_row; col = m_anchor_col; height = m_gridsize.rows; width = m_gridsize.cols}
Expand All @@ -324,6 +336,13 @@ and GridViewModel(_gridid: int, ?_parent: GridViewModel, ?_gridsize: GridSize) a
this.Focusable <- f
parent.OnChildChanged oldRegion newRegion

let setWinFloatPos win anchor anchor_grid r c f z =
m_winid <- win
m_is_float <- true
m_z <- z
trace _gridid "setWinFloatPos: z = %d" z
setWinPos (int r) (int c) m_gridsize.rows m_gridsize.cols f // XXX assume assume NW

let hidePopupMenu() =
m_popupmenu_vm.Show <- false

Expand All @@ -350,9 +369,7 @@ and GridViewModel(_gridid: int, ?_parent: GridViewModel, ?_gridsize: GridSize) a
| WinPos(_, _, startrow, startcol, c, r) -> setWinPos startrow startcol r c true
| WinHide(_) -> m_hidden <- true
| MsgSetPos(_, row, scrolled, sep_char) -> setWinPos row 0 m_gridsize.rows m_gridsize.cols true
| WinFloatPos (_, win, anchor, anchor_grid, r, c, f, z) ->
m_winid <- win
setWinPos (int r + 1) (int c) m_gridsize.rows m_gridsize.cols f // XXX assume attaching to grid #1, assume NW
| WinFloatPos (_, win, anchor, anchor_grid, r, c, f, z) -> setWinFloatPos win anchor anchor_grid r c f z
| PopupMenuShow(items, selected, row, col, grid) -> this.ShowPopupMenu grid items selected row col
| PopupMenuSelect(selected) -> selectPopupMenuPassive selected
| PopupMenuHide -> hidePopupMenu ()
Expand Down Expand Up @@ -456,8 +473,16 @@ and GridViewModel(_gridid: int, ?_parent: GridViewModel, ?_gridsize: GridSize) a
member __.CreateChild id r c =
trace _gridid "CreateChild: #%d" id
let child = GridViewModel(id, this, {rows=r; cols=c})
m_child_grids.Add child
m_child_grids.Add child |> ignore
child.ZIndex <- this.ZIndex + 1
child :> IGridUI
member __.AddChild c =
let c = c :?> GridViewModel
trace _gridid "AddChild: #%d" c.GridId
m_child_grids.Add c |> ignore
c.Parent <- (Some this)
c.ZIndex <- this.ZIndex + 1
markAllDirty()
member __.RemoveChild c =
ignore <| m_child_grids.Remove (c:?>GridViewModel)
markAllDirty()
Expand All @@ -467,6 +492,8 @@ and GridViewModel(_gridid: int, ?_parent: GridViewModel, ?_gridsize: GridSize) a
| Some p ->
(p:>IGridUI).RemoveChild this
m_parent <- None
m_z <- -100
markAllDirty()

member __.CursorGoto id row col =
if m_parent.IsSome && id = _gridid then
Expand Down Expand Up @@ -519,7 +546,7 @@ and GridViewModel(_gridid: int, ?_parent: GridViewModel, ?_gridsize: GridSize) a
for c in m_child_grids do
c.MarkClean()

member __.MarkAllDirty = markAllDirty
member __.MarkDirty = markAllDirty

// converts grid position to UI Point
member __.GetPoint row col =
Expand Down Expand Up @@ -576,6 +603,12 @@ and GridViewModel(_gridid: int, ?_parent: GridViewModel, ?_gridsize: GridSize) a
member __.Rows with get() = m_gridsize.rows
member __.AnchorRow with get() = m_anchor_row
member __.AnchorCol with get() = m_anchor_col
member __.AbsAnchor with get() =
match m_parent with
| Some p ->
let pr,pc = p.AbsAnchor
pr + m_anchor_row, pc + m_anchor_col
| _ -> m_anchor_row, m_anchor_col
member __.Dirty with get() = m_griddirty
member __.DrawOps with get() = m_drawops
member __.Hidden with get():bool = m_hidden
Expand All @@ -597,6 +630,9 @@ and GridViewModel(_gridid: int, ?_parent: GridViewModel, ?_gridsize: GridSize) a
member __.Focusable with get() = m_gridfocusable and set(v) = ignore <| this.RaiseAndSetIfChanged(&m_gridfocusable, v)
member __.ExtWinId = m_winid
member __.ExtWinClosed = m_ext_winclose_ev.Publish
member __.Parent with get() = m_parent and set(v) = m_parent <- v
member __.ZIndex with get() = m_z and set(v) = m_z <- v


(******************* Events ***********************)

Expand Down
100 changes: 44 additions & 56 deletions Views/Grid.xaml.fs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ open Avalonia.Platform
open Avalonia.Media.Imaging
open Avalonia.VisualTree
open System
open System.Collections.Specialized
open FSharp.Control.Reactive
open Avalonia.Data
open Avalonia.Visuals.Media.Imaging
Expand All @@ -29,6 +28,7 @@ open GridHelper
open model
open Avalonia.Input.TextInput
open Avalonia.Input
open System.Collections.Generic

type Grid() as this =
inherit Canvas()
Expand Down Expand Up @@ -113,7 +113,8 @@ type Grid() as this =
| CharType.Emoji -> true
| _ -> issym

let topLeft = grid_vm.GetPoint (row + vm.AnchorRow) (col + vm.AnchorCol)
let abs_r,abs_c = vm.AbsAnchor
let topLeft = grid_vm.GetPoint (row + abs_r) (col + abs_c)
let bottomRight = topLeft + grid_vm.GetPoint 1 nr_col
let bg_region = Rect(topLeft, bottomRight)

Expand Down Expand Up @@ -245,26 +246,42 @@ type Grid() as this =

// prevent repetitive drawings
let _drawnRegions = ResizeArray()
let _drawVMs = ResizeArray()

let rec scanDrawVMs (vm: GridViewModel) =
if vm.Hidden then ()
else
_drawVMs.Add(vm)
vm.ChildGrids |> Seq.iter scanDrawVMs

let drawOps (vm: GridViewModel) =
let abs_r,abs_c = vm.AbsAnchor
// if other grids tainted the region, mark it dirty
let touched = _drawnRegions
|> Seq.exists(fun(r,c,ce) ->
abs_r <= r &&
r < abs_r + vm.Rows &&
not( abs_c >= ce || c >= abs_c + vm.Cols ))
if touched then vm.MarkDirty()

trace vm "drawOps: z=%d" vm.ZIndex

let rec drawOps (vm: GridViewModel) =
if vm.Hidden then false
elif vm.Dirty then
trace vm "drawing whole grid"
for row = 0 to vm.Rows - 1 do
drawBufferLine vm grid_dc row 0 vm.Cols
for c in vm.ChildGrids do
c.MarkAllDirty()
drawOps c |> ignore
_drawnRegions.Add(row + abs_r, abs_c, vm.Cols + abs_c)
true
else
// not tainted. can draw with my draw ops.
let draw row col colend =
let covered = _drawnRegions |> Seq.exists(fun (r, c, ce) -> r = row + vm.AnchorRow && c <= col + vm.AnchorCol && ce >= colend + vm.AnchorCol)
let covered = _drawnRegions |> Seq.exists(fun (r, c, ce) -> r = row + abs_r && c <= col + abs_c && ce >= colend + abs_c)
if not covered then
drawBufferLine vm grid_dc row col colend
_drawnRegions.Add(row + vm.AnchorRow, col + vm.AnchorCol, colend + vm.AnchorCol)
_drawnRegions.Add(row + abs_r, col + abs_c, colend + abs_c)
else
()
//trace vm "region %d %d %d waived" row col colend

vm.DrawOps |> Seq.iter (
function
Expand All @@ -274,55 +291,19 @@ type Grid() as this =
else (top - row, left, bot, right)
for row = t to b - 1 do
draw row l r

(*Note: the captured bitmap still misaligns with the rendered text*)
(*Until this is fixed, we have to draw the scrolled region line by line...*)

(*let s_topLeft = grid_vm.GetPoint top left*)
(*let s_bottomRight = grid_vm.GetPoint (bot) (right)*)
(*let vec = grid_vm.GetPoint row 0*)
(*let src, dst = *)
(*if row > 0 then*)
(*Rect(s_topLeft + vec, s_bottomRight),*)
(*Rect(s_topLeft, s_bottomRight - vec)*)
(*else*)
(*Rect(s_topLeft, s_bottomRight + vec),*)
(*Rect(s_topLeft - vec, s_bottomRight)*)

(*grid_dc_scroll.PushClip(dst)*)
(*grid_dc_scroll.Clear(Avalonia.Media.Colors.Transparent)*)
(*grid_dc_scroll.PopClip()*)

(*use r1 = grid_fb.PlatformImpl.CloneAs<_>()*)
(*grid_dc_scroll.DrawBitmap(r1, 1.0, src, dst)*)

(*grid_dc.PushClip(dst)*)
(*grid_dc.Clear(Avalonia.Media.Colors.Transparent)*)
(*grid_dc.PopClip()*)

(*use r2 = grid_fb_scroll.PlatformImpl.CloneAs<_>()*)
(*grid_dc.DrawBitmap(r2, 1.0, dst, dst)*)

| Put r ->
for row = r.row to r.row_end - 1 do
draw row r.col r.col_end
)
let mutable drawn = vm.DrawOps.Count <> 0
// for the base grid, do not block drawing of children, and
// mark child grid dirty if base updates overlapped it
if vm.GridId = grid_vm.GridId then
for cgrid in vm.ChildGrids do
let touched = _drawnRegions
|> Seq.exists(fun(r,c,ce) ->
cgrid.AnchorRow <= r &&
r < cgrid.AnchorRow + cgrid.Rows &&
not( cgrid.AnchorCol >= ce || c >= cgrid.AnchorCol + cgrid.Cols ))
if touched then cgrid.MarkAllDirty()
_drawnRegions.Clear()
for c in vm.ChildGrids do
let child_drawn = drawOps c
drawn <- drawn || child_drawn
drawn
vm.DrawOps.Count <> 0

let m_gridComparer =
let makeGridComparer() =
{ new IComparer<GridViewModel> with
member __.Compare(x: GridViewModel, y: GridViewModel): int =
x.ZIndex - y.ZIndex
}
makeGridComparer()

do
this.Watch
Expand All @@ -337,7 +318,7 @@ type Grid() as this =

rpc.register.watch "font" (fun () ->
if grid_vm <> Unchecked.defaultof<_> then
grid_vm.MarkAllDirty()
grid_vm.MarkDirty()
this.InvalidateVisual())

// Input handling
Expand All @@ -360,7 +341,13 @@ type Grid() as this =
grid_dc.PushClip(Rect this.Bounds.Size)
let timer = System.Diagnostics.Stopwatch.StartNew()
_drawnRegions.Clear()
let drawn = drawOps grid_vm
_drawVMs.Clear()
scanDrawVMs grid_vm
_drawVMs.Sort(m_gridComparer)
let mutable drawn = false
for vm in _drawVMs do
let drawn' = drawOps vm
drawn <- drawn || drawn'
if m_debug then drawDebug grid_dc
grid_dc.PopClip()

Expand All @@ -372,6 +359,7 @@ type Grid() as this =
ctx.DrawImage(grid_fb, src_rect, tgt_rect, BitmapInterpolationMode.Default)
timer.Stop()
if drawn then trace grid_vm "drawing end, time = %dms." timer.ElapsedMilliseconds
else trace grid_vm "drawing end, nothing drawn."

override this.MeasureOverride(size) =
trace grid_vm "MeasureOverride: %A" size
Expand Down
33 changes: 27 additions & 6 deletions model.fs
Original file line number Diff line number Diff line change
Expand Up @@ -53,15 +53,34 @@ module private ModelImpl =

let setTitle id title = frames.[id].Title <- title

let _unicast_fail id cmd =
trace "unicast into non-existing grid #%d: %A" id cmd

let unicast id cmd =
match grids.TryGetValue id with
| true, grid -> grid.Redraw cmd
| _ -> trace "unicast into non-existing grid #%d: %A" id cmd

let unicast_detach id cmd =
match grids.TryGetValue id with
| true, grid ->
grid.Detach()
grid.Redraw cmd
| _ -> trace "unicast into non-existing grid #%d: %A" id cmd

let unicast_change_parent id pid cmd =
match (grids.TryGetValue id),(grids.TryGetValue pid) with
| (true, grid),(true, parent) ->
grid.Detach()
parent.AddChild(grid)
grid.Redraw cmd
| _ -> trace "unicast into non-existing grid #%d or parent #%d: %A" id pid cmd


let unicast_create id cmd w h =
if not(grids.ContainsKey id) then
add_grid <| grids.[1].CreateChild id h w
unicast id cmd
if not(grids.ContainsKey id) then
add_grid <| grids.[1].CreateChild id h w
unicast id cmd

let broadcast cmd =
for KeyValue(_,grid) in grids do
Expand Down Expand Up @@ -97,10 +116,12 @@ module private ModelImpl =
| GridCursorGoto _
-> broadcast cmd
// Unicast
| GridClear id | GridScroll(id,_,_,_,_,_,_)
| WinClose id | WinFloatPos(id, _, _, _, _, _, _, _) | WinHide(id)
| WinExternalPos(id,_)
| GridClear id | GridScroll(id,_,_,_,_,_,_)
| WinClose id | WinHide(id)
| WinViewport(id, _, _, _, _, _ ) -> unicast id cmd
| WinExternalPos(id,_) -> unicast_detach id cmd
| WinFloatPos(id, _, _, pid, _, _, _, _)
-> unicast_change_parent id pid cmd
| MsgSetPos(id, _, _, _) -> unicast_create id cmd grids.[1].GridWidth 1
| WinPos(id, _, _, _, w, h)
| GridResize(id, w, h) -> unicast_create id cmd w h
Expand Down
1 change: 1 addition & 0 deletions ui.fs
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ type IGridUI =
abstract RenderScale: float
abstract Redraw: RedrawCommand -> unit
abstract CreateChild: id:int -> rows:int -> cols:int -> IGridUI
abstract AddChild: IGridUI -> unit
abstract RemoveChild: IGridUI -> unit
abstract Detach: unit -> unit

Expand Down

0 comments on commit 353daaa

Please sign in to comment.