]> git.gir.st - VimFx.git/blob - extension/lib/marker.coffee
Fix missing hint markers when re-entering Hints mode quickly
[VimFx.git] / extension / lib / marker.coffee
1 ###
2 # Copyright Anton Khodakivskiy 2012, 2013.
3 # Copyright Simon Lydell 2013, 2014, 2015, 2016.
4 #
5 # This file is part of VimFx.
6 #
7 # VimFx is free software: you can redistribute it and/or modify
8 # it under the terms of the GNU General Public License as published by
9 # the Free Software Foundation, either version 3 of the License, or
10 # (at your option) any later version.
11 #
12 # VimFx is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU General Public License for more details.
16 #
17 # You should have received a copy of the GNU General Public License
18 # along with VimFx. If not, see <http://www.gnu.org/licenses/>.
19 ###
20
21 # This file contains an abstraction for hint markers. It creates the UI for a
22 # marker and provides methods to manipulate the markers.
23
24 utils = require('./utils')
25
26 class Marker
27 # `@wrapper` is a stand-in for the element that the marker represents. See
28 # `MarkerContainer::injectHints` for more information.
29 constructor: (@wrapper, @document, {@isComplementary}) ->
30 @elementShape = @wrapper.shape
31 @markerElement = utils.createBox(@document, 'marker')
32 @markerElement.setAttribute('data-type', @wrapper.type)
33 @weight = @elementShape.area
34 @width = 0
35 @height = 0
36 @hint = ''
37 @hintIndex = 0
38 @visible = true
39 @zoom = 1
40 @viewport = null
41 @position = null
42 @originalPosition = null
43
44 reset: ->
45 @setHint(@hint)
46 @show()
47
48 show: -> @setVisibility(true)
49 hide: -> @setVisibility(false)
50
51 setVisibility: (@visible) ->
52 @markerElement.classList.toggle('marker--hidden', not @visible)
53
54 # To be called when the marker has been both assigned a hint and inserted
55 # into the DOM, and thus gotten a width and height.
56 setPosition: (@viewport, @zoom) ->
57 {
58 markerElement: {clientWidth, clientHeight}
59 elementShape: {nonCoveredPoint: {x: left, y: top, offset}}
60 } = this
61
62 @width = clientWidth / @zoom
63 @height = clientHeight / @zoom
64
65 # Center the marker vertically on the non-covered point.
66 top -= Math.ceil(@height / 2)
67
68 # Make the position relative to the top frame.
69 left += offset.left
70 top += offset.top
71
72 @originalPosition = {left, top}
73 @moveTo(left, top)
74
75 moveTo: (left, top) ->
76 # Make sure that the marker stays within the viewport.
77 left = Math.min(left, @viewport.right - @width)
78 top = Math.min(top, @viewport.bottom - @height)
79 left = Math.max(left, @viewport.left)
80 top = Math.max(top, @viewport.top)
81
82 # Take the current zoom into account.
83 left = Math.round(left * @zoom)
84 top = Math.round(top * @zoom)
85
86 # The positioning is absolute.
87 @markerElement.style.left = "#{left}px"
88 @markerElement.style.top = "#{top}px"
89
90 # For quick access.
91 @position = {
92 left, right: left + @width,
93 top, bottom: top + @height,
94 }
95
96 updatePosition: (dx, dy) ->
97 @moveTo(@originalPosition.left + dx, @originalPosition.top + dy)
98
99 setHint: (@hint) ->
100 @hintIndex = 0
101 @markerElement.textContent = ''
102 fragment = @document.createDocumentFragment()
103 utils.createBox(@document, 'marker-char', fragment, char) for char in @hint
104 @markerElement.appendChild(fragment)
105
106 matchHintChar: (char) ->
107 if char == @hint[@hintIndex]
108 @toggleLastHintChar(true)
109 @hintIndex += 1
110 return true
111 return false
112
113 deleteHintChar: ->
114 if @hintIndex > 0
115 @hintIndex -= 1
116 @toggleLastHintChar(false)
117
118 toggleLastHintChar: (visible) ->
119 @markerElement.children[@hintIndex]
120 .classList.toggle('marker-char--matched', visible)
121
122 isMatched: -> (@hintIndex == @hint.length)
123
124 markMatched: (matched) ->
125 @markerElement.classList.toggle('marker--matched', matched)
126
127 module.exports = Marker
Imprint / Impressum