Skip to content

Commit

Permalink
improved hover info behaviour
Browse files Browse the repository at this point in the history
  • Loading branch information
Nimaoth committed Mar 10, 2024
1 parent d2f62c0 commit 18c4e36
Show file tree
Hide file tree
Showing 9 changed files with 105 additions and 21 deletions.
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -156,4 +156,7 @@ More details in [Getting Started](docs/getting_started.md)
- Search/Replace
- fix e.g. dw deleting one character to much because it's inclusive

## LSP
- Handle rust lsp paths with lowercase letter like d:/path/to/file

[Test report](https://nimaoth.github.io/Absytree/testresults.html)
15 changes: 10 additions & 5 deletions config/keybindings_vim.nim
Original file line number Diff line number Diff line change
Expand Up @@ -900,7 +900,6 @@ proc loadVimKeybindings*() {.scriptActionWasmNims("load-vim-keybindings").} =
addTextCommand "", "X", vimDeleteLeft

addTextCommand "", "u", "undo"
addTextCommand "", "U", "redo"
addTextCommand "", "<C-r>", "redo"
addTextCommand "", "p", "vim-paste"

Expand All @@ -914,9 +913,6 @@ proc loadVimKeybindings*() {.scriptActionWasmNims("load-vim-keybindings").} =
editor.vimChangeSelection(true)

# Insert mode
addTextCommand "insert", "<C-m>", "insert-text", "\n"
addTextCommand "insert", "<C-j>", "insert-text", "\n"

addTextCommand "insert", "<C-r>", "set-mode", "insert-register"
setHandleInputs "editor.text.insert-register", true
setTextInputHandler "insert-register", proc(editor: TextDocumentEditor, input: string): bool =
Expand Down Expand Up @@ -985,4 +981,13 @@ proc loadVimKeybindings*() {.scriptActionWasmNims("load-vim-keybindings").} =
addTextCommand "visual-line", "<?-count><text_object>", """vim-select-move <text_object> <#count>"""

addTextCommand "visual-line", "\\>", "indent"
addTextCommand "visual-line", "\\<", "unindent"
addTextCommand "visual-line", "\\<", "unindent"

# todo: not really vim keybindings
addTextCommand "", "gd", "goto-definition"
addTextCommand "", "gs", "goto-symbol"
addTextCommand "", "<C-SPACE>", "get-completions"
addTextCommand "", "<C-k>", "show-hover-for-current"
addTextCommand "", "<C-r>", "select-prev"
addTextCommand "", "<C-m>", "select-next"
addTextCommand "", "U", "redo"
6 changes: 6 additions & 0 deletions scripting/absytree_internal.nim
Original file line number Diff line number Diff line change
Expand Up @@ -297,6 +297,12 @@ proc editor_text_showHoverForCurrent_void_TextDocumentEditor_impl*(
discard
proc editor_text_hideHover_void_TextDocumentEditor_impl*(self: TextDocumentEditor) =
discard
proc editor_text_cancelDelayedHideHover_void_TextDocumentEditor_impl*(
self: TextDocumentEditor) =
discard
proc editor_text_hideHoverDelayed_void_TextDocumentEditor_impl*(
self: TextDocumentEditor) =
discard
proc editor_text_isRunningSavedCommands_bool_TextDocumentEditor_impl*(
self: TextDocumentEditor): bool =
discard
Expand Down
4 changes: 4 additions & 0 deletions scripting/absytree_internal_wasm.nim
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,10 @@ proc editor_text_showHoverFor_void_TextDocumentEditor_Cursor_impl(
proc editor_text_showHoverForCurrent_void_TextDocumentEditor_impl(
self: TextDocumentEditor) {.importc.}
proc editor_text_hideHover_void_TextDocumentEditor_impl(self: TextDocumentEditor) {.importc.}
proc editor_text_cancelDelayedHideHover_void_TextDocumentEditor_impl(
self: TextDocumentEditor) {.importc.}
proc editor_text_hideHoverDelayed_void_TextDocumentEditor_impl(
self: TextDocumentEditor) {.importc.}
proc editor_text_isRunningSavedCommands_bool_TextDocumentEditor_impl(
self: TextDocumentEditor): bool {.importc.}
proc editor_text_runSavedCommands_void_TextDocumentEditor_impl(
Expand Down
5 changes: 5 additions & 0 deletions scripting/editor_text_api.nim
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,11 @@ proc showHoverForCurrent*(self: TextDocumentEditor) =
proc hideHover*(self: TextDocumentEditor) =
## Hides the hover information.
editor_text_hideHover_void_TextDocumentEditor_impl(self)
proc cancelDelayedHideHover*(self: TextDocumentEditor) =
editor_text_cancelDelayedHideHover_void_TextDocumentEditor_impl(self)
proc hideHoverDelayed*(self: TextDocumentEditor) =
## Hides the hover information after a delay.
editor_text_hideHoverDelayed_void_TextDocumentEditor_impl(self)
proc isRunningSavedCommands*(self: TextDocumentEditor): bool =
editor_text_isRunningSavedCommands_bool_TextDocumentEditor_impl(self)
proc runSavedCommands*(self: TextDocumentEditor) =
Expand Down
28 changes: 28 additions & 0 deletions scripting/editor_text_api_wasm.nim
Original file line number Diff line number Diff line change
Expand Up @@ -1975,6 +1975,34 @@ proc hideHover*(self: TextDocumentEditor) =
argsJsonString.cstring)


proc editor_text_cancelDelayedHideHover_void_TextDocumentEditor_wasm(
arg: cstring): cstring {.importc.}
proc cancelDelayedHideHover*(self: TextDocumentEditor) =
var argsJson = newJArray()
argsJson.add block:
when TextDocumentEditor is JsonNode:
self
else:
self.toJson()
let argsJsonString = $argsJson
let res {.used.} = editor_text_cancelDelayedHideHover_void_TextDocumentEditor_wasm(
argsJsonString.cstring)


proc editor_text_hideHoverDelayed_void_TextDocumentEditor_wasm(arg: cstring): cstring {.
importc.}
proc hideHoverDelayed*(self: TextDocumentEditor) =
var argsJson = newJArray()
argsJson.add block:
when TextDocumentEditor is JsonNode:
self
else:
self.toJson()
let argsJsonString = $argsJson
let res {.used.} = editor_text_hideHoverDelayed_void_TextDocumentEditor_wasm(
argsJsonString.cstring)


proc editor_text_isRunningSavedCommands_bool_TextDocumentEditor_wasm(
arg: cstring): cstring {.importc.}
proc isRunningSavedCommands*(self: TextDocumentEditor): bool =
Expand Down
25 changes: 24 additions & 1 deletion src/text/text_editor.nim
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ type TextDocumentEditor* = ref object of DocumentEditor

# hover
showHoverTask: DelayedTask # for showing hover info after a delay
hideHoverTask: DelayedTask # for hiding hover info after a delay
currentHoverLocation: Cursor # the location of the mouse hover
showHover*: bool # whether to show hover info in ui
hoverText*: string # the text to show in the hover info
Expand Down Expand Up @@ -1518,6 +1519,9 @@ proc applySelectedCompletion*(self: TextDocumentEditor) {.expose("editor.text").
self.hideCompletions()

proc showHoverForAsync(self: TextDocumentEditor, cursor: Cursor): Future[void] {.async.} =
if self.hideHoverTask.isNotNil:
self.hideHoverTask.pause()

let languageServer = await self.document.getLanguageServer()

if languageServer.getSome(ls):
Expand Down Expand Up @@ -1547,12 +1551,31 @@ proc hideHover*(self: TextDocumentEditor) {.expose("editor.text").} =
self.showHover = false
self.markDirty()

proc cancelDelayedHideHover*(self: TextDocumentEditor) {.expose("editor.text").} =
if self.hideHoverTask.isNotNil:
self.hideHoverTask.pause()

proc hideHoverDelayed*(self: TextDocumentEditor) {.expose("editor.text").} =
## Hides the hover information after a delay.
if self.showHoverTask.isNotNil:
self.showHoverTask.pause()

let hoverDelayMs = self.configProvider.getValue("text.hover-delay", 200)
if self.hideHoverTask.isNil:
self.hideHoverTask = startDelayed(hoverDelayMs, repeat=false):
self.hideHover()
else:
self.hideHoverTask.interval = hoverDelayMs
self.hideHoverTask.reschedule()

proc showHoverForDelayed*(self: TextDocumentEditor, cursor: Cursor) =
## Show hover information for the given cursor after a delay.
self.currentHoverLocation = cursor

let hoverDelayMs = self.configProvider.getValue("text.hover-delay", 200)
if self.hideHoverTask.isNotNil:
self.hideHoverTask.pause()

let hoverDelayMs = self.configProvider.getValue("text.hover-delay", 200)
if self.showHoverTask.isNil:
self.showHoverTask = startDelayed(hoverDelayMs, repeat=false):
self.showHoverFor(self.currentHoverLocation)
Expand Down
24 changes: 12 additions & 12 deletions src/ui/node.nim
Original file line number Diff line number Diff line change
Expand Up @@ -110,8 +110,8 @@ type
mHandlePressed: proc(node: UINode, button: MouseButton, modifiers: set[Modifier], pos: Vec2): bool
mHandleReleased: proc(node: UINode, button: MouseButton, modifiers: set[Modifier], pos: Vec2): bool
mHandleDrag: proc(node: UINode, button: MouseButton, modifiers: set[Modifier], pos: Vec2, delta: Vec2): bool
mHandleBeginHover: proc(node: UINode): bool
mHandleEndHover: proc(node: UINode): bool
mHandleBeginHover: proc(node: UINode, pos: Vec2): bool
mHandleEndHover: proc(node: UINode, pos: Vec2): bool
mHandleHover: proc(node: UINode, pos: Vec2): bool
mHandleScroll: proc(node: UINode, pos: Vec2, delta: Vec2, modifiers: set[Modifier]): bool

Expand Down Expand Up @@ -200,16 +200,16 @@ proc `textColor=`*(node: UINode, value: Color) {.inline.} = (let changed =
func handlePressed* (node: UINode): (proc(node: UINode, button: MouseButton, modifiers: set[Modifier], pos: Vec2): bool) {.inline.} = node.mHandlePressed
func handleReleased* (node: UINode): (proc(node: UINode, button: MouseButton, modifiers: set[Modifier], pos: Vec2): bool) {.inline.} = node.mHandleReleased
func handleDrag* (node: UINode): (proc(node: UINode, button: MouseButton, modifiers: set[Modifier], pos: Vec2, delta: Vec2): bool) {.inline.} = node.mHandleDrag
func handleBeginHover*(node: UINode): (proc(node: UINode): bool) {.inline.} = node.mHandleBeginHover
func handleEndHover* (node: UINode): (proc(node: UINode): bool) {.inline.} = node.mHandleEndHover
func handleBeginHover*(node: UINode): (proc(node: UINode, pos: Vec2): bool) {.inline.} = node.mHandleBeginHover
func handleEndHover* (node: UINode): (proc(node: UINode, pos: Vec2): bool) {.inline.} = node.mHandleEndHover
func handleHover* (node: UINode): (proc(node: UINode, pos: Vec2): bool) {.inline.} = node.mHandleHover
func handleScroll* (node: UINode): (proc(node: UINode, pos: Vec2, delta: Vec2, modifiers: set[Modifier]): bool) {.inline.} = node.mHandleScroll

func `handlePressed=`* (node: UINode, value: proc(node: UINode, button: MouseButton, modifiers: set[Modifier], pos: Vec2): bool) {.inline.} = node.mHandlePressed = value
func `handleReleased=`* (node: UINode, value: proc(node: UINode, button: MouseButton, modifiers: set[Modifier], pos: Vec2): bool) {.inline.} = node.mHandleReleased = value
func `handleDrag=`* (node: UINode, value: proc(node: UINode, button: MouseButton, modifiers: set[Modifier], pos: Vec2, delta: Vec2): bool) {.inline.} = node.mHandleDrag = value
func `handleBeginHover=`*(node: UINode, value: proc(node: UINode): bool) {.inline.} = node.mHandleBeginHover = value
func `handleEndHover=`* (node: UINode, value: proc(node: UINode): bool) {.inline.} = node.mHandleEndHover = value
func `handleBeginHover=`*(node: UINode, value: proc(node: UINode, pos: Vec2): bool) {.inline.} = node.mHandleBeginHover = value
func `handleEndHover=`* (node: UINode, value: proc(node: UINode, pos: Vec2): bool) {.inline.} = node.mHandleEndHover = value
func `handleHover=`* (node: UINode, value: proc(node: UINode, pos: Vec2): bool) {.inline.} = node.mHandleHover = value
func `handleScroll=`* (node: UINode, value: proc(node: UINode, pos: Vec2, delta: Vec2, modifiers: set[Modifier]): bool) {.inline.} = node.mHandleScroll = value

Expand Down Expand Up @@ -326,18 +326,18 @@ proc handleMouseMoved*(builder: UINodeBuilder, pos: Vec2, buttons: set[MouseButt
result = a.handleHover()(a, pos - a.boundsAbsolute.xy) or result
else:
if a.handleEndHover.isNotNil:
result = a.handleEndHover()(a) or result
result = a.handleEndHover()(a, pos - a.boundsAbsolute.xy) or result
if b.handleBeginHover.isNotNil:
result = b.handleBeginHover()(b) or result
result = b.handleBeginHover()(b, pos - b.boundsAbsolute.xy) or result
result = true

of (None(), Some(@b)):
if b.handleBeginHover.isNotNil:
result = b.handleBeginHover()(b) or result
result = b.handleBeginHover()(b, pos - b.boundsAbsolute.xy) or result
result = true
of (Some(@a), None()):
if a.handleEndHover.isNotNil:
result = a.handleEndHover()(a) or result
result = a.handleEndHover()(a, pos - a.boundsAbsolute.xy) or result
result = true
of (None(), None()):
discard
Expand Down Expand Up @@ -1288,11 +1288,11 @@ macro panel*(builder: UINodeBuilder, inFlags: UINodeFlags, args: varargs[untyped
onDragBody

template onBeginHover(onBody: untyped) {.used.} =
currentNode.handleBeginHover = proc(node: UINode): bool =
currentNode.handleBeginHover = proc(node: UINode, pos {.inject.}: Vec2): bool =
onBody

template onEndHover(onBody: untyped) {.used.} =
currentNode.handleEndHover = proc(node: UINode): bool =
currentNode.handleEndHover = proc(node: UINode, pos {.inject.}: Vec2): bool =
onBody

template onHover(onBody: untyped) {.used.} =
Expand Down
16 changes: 13 additions & 3 deletions src/ui/widget_builder_text_document.nim
Original file line number Diff line number Diff line change
Expand Up @@ -186,14 +186,18 @@ proc renderLine*(
self.app.tryActivateEditor(self)
self.markDirty()

onBeginHover:
let offset = self.getCursorPos(textRuneLen, line.index, startRune.RuneIndex, pos)
self.lastHoverLocationBounds = partNode.boundsAbsolute.some
self.showHoverForDelayed (line.index, offset)

onHover:
let offset = self.getCursorPos(textRuneLen, line.index, startRune.RuneIndex, pos)
self.lastHoverLocationBounds = partNode.boundsAbsolute.some
self.showHoverForDelayed (line.index, offset)

onEndHover:
# todo: hide after delay so the user has time to hover over the hover window for e.g. scrolling
self.hideHover()
self.hideHoverDelayed()

if addBackgroundAsChildren:
# Add separate background colors for selections/highlights
Expand Down Expand Up @@ -512,7 +516,7 @@ proc createHover(self: TextDocumentEditor, builder: UINodeBuilder, app: App, cur
clampedX = max(builder.root.w - totalWidth, 0)

var hoverPanel: UINode = nil
builder.panel(&{SizeToContentX, MaskContent, FillBackground, DrawBorder, MouseHover}, x = clampedX, y = top, h = height, pivot = vec2(0, 0), backgroundColor = backgroundColor, borderColor = scopeColor, userId = self.hoverId.newPrimaryId):
builder.panel(&{SizeToContentX, MaskContent, FillBackground, DrawBorder, MouseHover, SnapInitialBounds, AnimateBounds}, x = clampedX, y = top, h = height, pivot = vec2(0, 0), backgroundColor = backgroundColor, borderColor = scopeColor, userId = self.hoverId.newPrimaryId):
hoverPanel = currentNode
var textNode: UINode = nil
# todo: height
Expand All @@ -525,6 +529,12 @@ proc createHover(self: TextDocumentEditor, builder: UINodeBuilder, app: App, cur
self.hoverScrollOffset = clamp(self.hoverScrollOffset + delta.y * scrollSpeed, -1000, 0)
self.markDirty()

onBeginHover:
self.cancelDelayedHideHover()

onEndHover:
self.hideHoverDelayed()

hoverPanel.rawY = cursorBounds.y
hoverPanel.pivot = vec2(0, 1)

Expand Down

0 comments on commit 18c4e36

Please sign in to comment.