Skip to content

Commit

Permalink
multigrid improvements:
Browse files Browse the repository at this point in the history
1. correctly parse win_pos and win_float_pos
2. multigrid assumes client to preserve buffer content when resizing
3. implement destroy_grid
4. grid view doesn't kick in until both 1) viewmodel connected 2) visualtree attached
5. initial framebuffer setup without grid resize events
  • Loading branch information
yatli committed Jul 5, 2020
1 parent f432a64 commit 830fe97
Show file tree
Hide file tree
Showing 9 changed files with 108 additions and 38 deletions.
3 changes: 2 additions & 1 deletion Properties/launchSettings.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
{
"profiles": {
"fvim": {
"commandName": "Project"
"commandName": "Project",
"commandLineArgs": "--debug-multigrid"
},
"norc": {
"commandName": "Project",
Expand Down
35 changes: 27 additions & 8 deletions ViewModels/EditorViewModel.fs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ type EditorViewModel(_gridid: int, ?parent: EditorViewModel, ?_gridsize: GridSiz

let m_cursor_vm = new CursorViewModel(_cursormode)
let m_popupmenu_vm = new PopupMenuViewModel()
let m_child_grids = ObservableCollection<EditorViewModel>()
let m_child_grids = ObservableCollection<IGridUI>()
let m_resize_ev = Event<IGridUI>()
let m_input_ev = Event<int * InputEvent>()

Expand Down Expand Up @@ -66,8 +66,20 @@ type EditorViewModel(_gridid: int, ?parent: EditorViewModel, ?_gridsize: GridSiz
m_griddirty.Clear()
m_griddirty.Union{ row = 0; col = 0; height = m_gridsize.rows; width = m_gridsize.cols }

let clearBuffer () =
let clearBuffer preserveContent =
let oldgrid = m_gridbuffer
m_gridbuffer <- Array2D.create m_gridsize.rows m_gridsize.cols GridBufferCell.empty
if preserveContent then
let crow =
Array2D.length1 oldgrid
|> min m_gridsize.rows
let ccol =
Array2D.length2 oldgrid
|> min m_gridsize.cols
for r = 0 to crow-1 do
for c = 0 to ccol-1 do
m_gridbuffer.[r,c] <- oldgrid.[r,c]
markAllDirty()
// notify buffer update and size change
let size: Point = this.GetPoint m_gridsize.rows m_gridsize.cols
m_fb_w <- size.X
Expand All @@ -91,7 +103,7 @@ type EditorViewModel(_gridid: int, ?parent: EditorViewModel, ?_gridsize: GridSiz
// if the buffer under cursor is updated, also notify the cursor view model
if row = m_cursor_vm.row && line.col_start <= m_cursor_vm.col && m_cursor_vm.col < col
then this.cursorConfig()
//trace "redraw" "putBuffer: writing to %A" dirty
// trace _gridid "putBuffer: writing to %A" dirty
// italic font artifacts I: remainders after scrolling and redrawing the dirty part
// workaround: extend the dirty region one cell further towards the end

Expand Down Expand Up @@ -206,7 +218,7 @@ type EditorViewModel(_gridid: int, ?parent: EditorViewModel, ?_gridsize: GridSiz
(* manually resize and position the child grid as per neovim docs *)
let origin: Point = parent.GetPoint startrow startcol
trace _gridid "setWinPos: update parameters: c = %d r = %d X = %f Y = %f" c r origin.X origin.Y
this.initBuffer r c
this.initBuffer r c true
this.X <- origin.X
this.Y <- origin.Y

Expand Down Expand Up @@ -254,8 +266,8 @@ type EditorViewModel(_gridid: int, ?parent: EditorViewModel, ?_gridsize: GridSiz
let redraw(cmd: RedrawCommand) =
//trace "%A" cmd
match cmd with
| GridResize(_, c, r) -> this.initBuffer r c
| GridClear _ -> clearBuffer()
| GridResize(_, c, r) -> this.initBuffer r c true
| GridClear _ -> clearBuffer false
| GridLine lines -> Array.iter putBuffer lines
| GridCursorGoto(id, row, col) -> cursorGoto id row col
| GridScroll(_, top,bot,left,right,rows,cols) -> scrollBuffer top bot left right rows cols
Expand Down Expand Up @@ -318,6 +330,7 @@ type EditorViewModel(_gridid: int, ?parent: EditorViewModel, ?_gridsize: GridSiz
trace _gridid "%s" "ctor"
fontConfig()
this.setCursorEnabled theme.cursor_enabled
clearBuffer false

this.Watch [

Expand Down Expand Up @@ -355,12 +368,12 @@ type EditorViewModel(_gridid: int, ?parent: EditorViewModel, ?_gridsize: GridSiz
member __.GetFontAttrs() =
theme.guifont, theme.guifontwide, m_fontsize

member private __.initBuffer nrow ncol =
member private __.initBuffer nrow ncol preserveContent =
let new_gridsize = { rows = nrow; cols = ncol }
if m_gridsize <> new_gridsize then
m_gridsize <- new_gridsize
trace _gridid "buffer resize = %A" m_gridsize
clearBuffer()
clearBuffer preserveContent

interface IGridUI with
member __.Id = _gridid
Expand All @@ -375,6 +388,12 @@ type EditorViewModel(_gridid: int, ?parent: EditorViewModel, ?_gridsize: GridSiz
let child = new EditorViewModel(id, this, {rows=r; cols=c}, Size(child_size.X, child_size.Y), m_gridscale, m_cursor_vm.modeidx)
m_child_grids.Add child
child :> IGridUI
member __.RemoveChild c =
ignore <| m_child_grids.Remove c
member __.Detach() =
match parent with
| None -> ()
| Some p -> (p:>IGridUI).RemoveChild this

member __.markClean = m_griddirty.Clear

Expand Down
4 changes: 4 additions & 0 deletions Views/Cursor.xaml.fs
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,10 @@ type Cursor() as this =
let cellw p = min (double(p) / 100.0 * this.Width) 1.0
let cellh p = min (double(p) / 100.0 * this.Height) 5.0
let scale = this.GetVisualRoot().RenderScaling

if this.ViewModel = Unchecked.defaultof<CursorViewModel> then ()
else

match this.ViewModel.shape, this.ViewModel.cellPercentage with
| CursorShape.Block, _ ->
let _, typeface = GetTypeface(this.ViewModel.text, this.ViewModel.italic, this.ViewModel.bold, this.ViewModel.typeface, this.ViewModel.wtypeface)
Expand Down
7 changes: 7 additions & 0 deletions Views/Editor.xaml.fs
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,10 @@ type Editor() as this =

let mutable m_debug = States.ui_multigrid

// !Only call this if VisualRoot is attached
let resizeFrameBuffer() =
trace grid_vm "resizeFrameBuffer bufw=%A bufh=%A" grid_vm.BufferWidth grid_vm.BufferHeight
let vroot = this.GetVisualRoot()
grid_scale <- this.GetVisualRoot().RenderScaling
if grid_fb <> null then
grid_fb.Dispose()
Expand Down Expand Up @@ -176,6 +178,7 @@ type Editor() as this =
let onViewModelConnected(vm: EditorViewModel) =
grid_vm <- vm
trace grid_vm "%s" "viewmodel connected"
resizeFrameBuffer()
vm.Watch
[ Observable.merge (vm.ObservableForProperty(fun x -> x.BufferWidth))
(vm.ObservableForProperty(fun x -> x.BufferHeight))
Expand Down Expand Up @@ -240,9 +243,13 @@ type Editor() as this =

do



this.Watch
[ this.GetObservable(Editor.DataContextProperty)
|> Observable.ofType
|> Observable.zip this.AttachedToVisualTree
|> Observable.map snd
|> Observable.subscribe onViewModelConnected

this.Bind(Canvas.LeftProperty, Binding("X"))
Expand Down
7 changes: 7 additions & 0 deletions common.fs
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,13 @@ let (|Integer32|_|) (x:obj) =
| :? uint8 as x -> Some(int32 x)
| _ -> None

let (|Float|_|) (x:obj) =
match x with
| Integer32 x -> Some(float x)
| :? single as x -> Some(float x)
| :? float as x -> Some(float x)
| _ -> None

// converts to bool in a desperate (read: JavaScript) attempt
let (|ForceBool|_|) (x:obj) =
match x with
Expand Down
40 changes: 30 additions & 10 deletions def.fs
Original file line number Diff line number Diff line change
Expand Up @@ -262,7 +262,7 @@ type RedrawCommand =
/// displayed above another grid `anchor_grid` at the specified position
/// `anchor_row` and `anchor_col`. For the meaning of `anchor` and more
/// details of positioning, see |nvim_open_win()|.
| WinFloatPos of grid: int * win: int * anchor: Anchor * anchor_grid: int * anchor_row: int * anchor_col: int * focusable: bool
| WinFloatPos of grid: int * win: int * anchor: Anchor * anchor_grid: int * anchor_row: float * anchor_col: float * focusable: bool
/// Display or reconfigure external window `win`. The window should be
/// displayed as a separate top-level window in the desktop environment,
/// or something similar.
Expand Down Expand Up @@ -315,6 +315,7 @@ type RedrawCommand =
//| UpdateBg of Color
//| UpdateSp of Color
| UnknownCommand of data: obj
| MultiRedrawCommand of xs: RedrawCommand []

type EventParseException(data: obj) =
inherit exn()
Expand Down Expand Up @@ -344,6 +345,15 @@ let (|P|_|) (parser: obj -> 'a option) (xs:obj) =
Some result
| _ -> None

/// Chooses from ObjArray with a parser
let (|PX|_|) (parser: obj -> 'a option) (xs:obj) =
match xs with
| :? (obj seq) as xs ->
let result = Seq.choose parser xs |> Array.ofSeq
if Seq.length xs = Seq.length result then Some result
else None
| _ -> None

/// Matches ObjArray against the form [|key; value|]
let (|KV|_|) (k: string) (x: obj) =
match x with
Expand Down Expand Up @@ -492,6 +502,12 @@ let parse_grid_line (x: obj) =
-> Some {grid = grid; row=row; col_start=col_start; cells=cells}
| _ -> None

let parse_win_pos (x: obj) =
match x with
| ObjArray [| (Integer32 grid); (Integer32 win); (Integer32 start_row); (Integer32 start_col); (Integer32 width); (Integer32 height) |]
-> Some <| WinPos(grid, win, start_row, start_col, width, height)
| _ -> None

let (|Anchor|_|) =
function
| String "NE" -> Some NorthEast
Expand All @@ -500,6 +516,12 @@ let (|Anchor|_|) =
| String "SW" -> Some SouthWest
| _ -> None

let parse_win_float_pos (x: obj) =
match x with
| ObjArray [| (Integer32 grid); (Integer32 win); (Anchor anchor); (Integer32 anchor_grid); (Float anchor_row); (Float anchor_col); (Bool focusable) |]
-> Some <| WinFloatPos(grid, win, anchor, anchor_grid, anchor_row, anchor_col, focusable)
| _ -> None

let parse_complete_item =
function
| ObjArray [| (String word); (String abbr); (String menu); (String info) |] ->
Expand All @@ -522,6 +544,11 @@ let parse_semantic_hlgroup =
| _ -> None
| _ -> None

let unwrap_multi xs =
match xs with
| [| one |] -> one
| _ -> MultiRedrawCommand xs

let parse_redrawcmd (x: obj) =
match x with
| C("option_set", P(parse_uioption)options) -> SetOption options
Expand All @@ -548,15 +575,8 @@ let parse_redrawcmd (x: obj) =
(Integer32 left); (Integer32 right)
(Integer32 rows); (Integer32 cols) |]) -> GridScroll(grid, top, bot, left, right, rows, cols)
| C("grid_line", P(parse_grid_line)lines) -> GridLine lines
| C1("win_pos", [|
(Integer32 grid); (Integer32 win)
(Integer32 start_row); (Integer32 start_col)
(Integer32 width); (Integer32 height) |]) -> WinPos(grid,win,start_row,start_col,width,height)
| C1("win_float_pos", [|
(Integer32 grid); (Integer32 win)
(Anchor anchor); (Integer32 anchor_grid)
(Integer32 anchor_row); (Integer32 anchor_col)
(Bool focusable) |]) -> WinFloatPos(grid, win, anchor, anchor_grid, anchor_row, anchor_col, focusable)
| C("win_pos", PX(parse_win_pos)ps) -> unwrap_multi ps
| C("win_float_pos", PX(parse_win_float_pos)ps) -> unwrap_multi ps
| C1("win_external_pos", [|
(Integer32 grid); (Integer32 win) |]) -> WinExternalPos(grid, win)
| C1("win_hide", [| (Integer32 grid) |]) -> WinHide(grid)
Expand Down
10 changes: 5 additions & 5 deletions fvim.fsproj
Original file line number Diff line number Diff line change
Expand Up @@ -78,11 +78,11 @@
</ItemGroup>

<ItemGroup>
<PackageReference Include="Avalonia" Version="0.9.9" />
<PackageReference Include="Avalonia" Version="0.9.11" />
<PackageReference Include="Avalonia.Angle.Windows.Natives" Version="2.1.0.2019013001" />
<PackageReference Include="Avalonia.Desktop" Version="0.9.9" />
<PackageReference Include="Avalonia.ReactiveUI" Version="0.9.9" />
<PackageReference Include="FSharp.Control.Reactive" Version="4.2.0" />
<PackageReference Include="Avalonia.Desktop" Version="0.9.11" />
<PackageReference Include="Avalonia.ReactiveUI" Version="0.9.11" />
<PackageReference Include="FSharp.Control.Reactive" Version="4.4.2" />
<PackageReference Include="FSharp.Data" Version="3.3.3" />
<PackageReference Include="GitInfo" Version="2.0.26">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
Expand All @@ -98,7 +98,7 @@
</PackageReference>
<PackageReference Include="SkiaSharp.HarfBuzz" Version="1.68.0" />
<PackageReference Include="TaskBuilder.fs" Version="2.1.0" />
<PackageReference Update="FSharp.Core" Version="4.7.1" />
<PackageReference Update="FSharp.Core" Version="4.7.2" />
<PackageReference Include="UACHelper" Version="1.3.0.5" />
</ItemGroup>

Expand Down
38 changes: 24 additions & 14 deletions model.fs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,13 @@ module ModelImpl =
// by default add to grid #1
(*if id <> 1 then*)
(*ignore <| grids.[1].AddChild id r c*)


let destroy_grid(id) =
match grids.TryGetValue id with
| false, _ -> ()
| true, grid ->
ignore <| grids.Remove id
grid.Detach()

let add_window(win: IWindow) =
let id = win.RootId
Expand All @@ -67,11 +73,10 @@ module ModelImpl =
if RuntimeInformation.IsOSPlatform(OSPlatform.OSX) then id
else Observable.throttle(TimeSpan.FromMilliseconds 10.0)

let redraw cmd =
let rec redraw cmd =
match cmd with
// Global
| UnknownCommand x -> trace "unknown command %A" x
| GridDestroy id -> trace "GridDestroy %d" id //TODO
| SetTitle title -> setTitle 1 title
| SetIcon icon -> trace "icon: %s" icon // TODO
| Bell -> bell true
Expand All @@ -87,25 +92,30 @@ module ModelImpl =
| Busy _ | Mouse _
| ModeChange _ | Flush
| GridCursorGoto(_,_,_)
-> broadcast cmd
-> broadcast cmd
// Unicast
| GridClear id | GridScroll(id,_,_,_,_,_,_)
-> unicast id cmd
| MsgSetPos(id, _, _, _) ->
if not(grids.ContainsKey id) then
add_grid <| grids.[1].AddChild id 1 grids.[1].GridWidth
unicast id cmd
| GridClear id | GridScroll(id,_,_,_,_,_,_) ->
unicast id cmd
| WinFloatPos(id, _, _, _, _, _, _) ->
trace "win_float_pos %A" cmd
unicast id cmd
| MsgSetPos(id, _, _, _) ->
if not(grids.ContainsKey id) then
add_grid <| grids.[1].AddChild id 1 grids.[1].GridWidth
unicast id cmd
| WinPos(id, _, _, _, w, h)
| GridResize(id, w, h)
->
| GridResize(id, w, h) ->
if not(grids.ContainsKey id) then
add_grid <| grids.[1].AddChild id h w
unicast id cmd
| GridLine lines ->
| GridLine lines ->
lines
|> Array.groupBy (fun (line: GridLine) -> line.grid)
|> Array.iter (fun (id, lines) -> unicast id (GridLine lines))
| x -> trace "unimplemented command: %A" x
| GridDestroy id -> trace "GridDestroy %d" id; destroy_grid id
| WinClose id -> trace "WinClose %d (unimplemented)" id // TODO
| MultiRedrawCommand xs -> Array.iter redraw xs
| x -> trace "unimplemented command: %A" x

let onGridResize(gridui: IGridUI) =
trace "Grid #%d resized to %d %d" gridui.Id gridui.GridWidth gridui.GridHeight
Expand Down
2 changes: 2 additions & 0 deletions ui.fs
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,8 @@ type IGridUI =
abstract HasChildren: bool
abstract Redraw: RedrawCommand -> unit
abstract AddChild: int -> int -> int -> IGridUI
abstract RemoveChild: IGridUI -> unit
abstract Detach: unit -> unit

type IWindow =
abstract Title: string with get, set
Expand Down

0 comments on commit 830fe97

Please sign in to comment.