Commit 395734d4f0a5a45070456273341063c5c4fd3d95

Authored by dg
1 parent 6fcb9bf8

Added .. mouse gestures. Yes right! Find them in the keybinder menu. You can now…

… bind mouse gestures to any bindable actions!
Module makers can use gestures easily with the engine.ui.Gesture class


git-svn-id: http://svn.net-core.org/repos/t-engine4@3381 51575b47-30f0-44d4-a5cc-537603b46e54
... ... @@ -83,17 +83,23 @@ function _M:saveRemap(file)
83 83 end
84 84
85 85 local f = fs.open(file, "w")
  86 +
  87 + local k1, k2, k3
86 88
87 89 for virtual, keys in pairs(_M.binds_remap) do
88   - if keys[1] and not keys[2] then
89   - f:write(("%s = {%q,nil}\n"):format(virtual, keys[1]))
90   - elseif not keys[1] and keys[2] then
91   - f:write(("%s = {nil,%q}\n"):format(virtual, keys[2]))
92   - elseif keys[1] and keys[2] then
93   - f:write(("%s = {%q,%q}\n"):format(virtual, keys[1], keys[2]))
94   - elseif not keys[1] and not keys[2] then
95   - f:write(("%s = {nil,nil}\n"):format(virtual))
  90 + k1 = "nil"
  91 + k2 = "nil"
  92 + k3 = "nil"
  93 + if keys[1] then
  94 + k1 = ("%q"):format(keys[1])
96 95 end
  96 + if keys[2] then
  97 + k2 = ("%q"):format(keys[2])
  98 + end
  99 + if keys[3] then
  100 + k3 = ("%q"):format(keys[3])
  101 + end
  102 + f:write(("%s = {%s,%s,%s}\n"):format(virtual, k1, k2, k3))
97 103 end
98 104
99 105 f:close()
... ... @@ -138,6 +144,10 @@ function _M:makeKeyString(sym, ctrl, shift, alt, meta, unicode)
138 144 return ("sym:%s:%s:%s:%s:%s"):format(tostring(sym), tostring(ctrl), tostring(shift), tostring(alt), tostring(meta)), unicode and "uni:"..unicode
139 145 end
140 146
  147 +function _M:makeGestureString(gesture)
  148 + return ("gest:%s"):format(tostring(gesture))
  149 +end
  150 +
141 151 function _M:makeMouseString(button, ctrl, shift, alt, meta)
142 152 return ("mouse:%s:%s:%s:%s:%s"):format(tostring(button), tostring(ctrl), tostring(shift), tostring(alt), tostring(meta))
143 153 end
... ... @@ -181,6 +191,10 @@ function _M:formatKeyString(ks)
181 191 if meta then sym = "[meta]+"..sym end
182 192
183 193 return sym
  194 + elseif ks:find("^gest:") then
  195 + local i, j, sym = ks:find("^gest:([a-zA-Z0-9]+)$")
  196 + if not i then return "--" end
  197 + return sym
184 198 end
185 199 end
186 200
... ...
... ... @@ -50,12 +50,12 @@ function _M:generateList(actions)
50 50 resume = { "Resume", function() game:unregisterDialog(self) end },
51 51 keybinds = { "Key Bindings", function()
52 52 game:unregisterDialog(self)
53   - local menu = require("engine.dialogs.KeyBinder").new(game.normal_key)
  53 + local menu = require("engine.dialogs.KeyBinder").new(game.normal_key, nil, game.gestures)
54 54 game:registerDialog(menu)
55 55 end },
56 56 keybinds_all = { "Key Bindings", function()
57 57 game:unregisterDialog(self)
58   - local menu = require("engine.dialogs.KeyBinder").new(game.normal_key, true)
  58 + local menu = require("engine.dialogs.KeyBinder").new(game.normal_key, true, game.gestures)
59 59 game:registerDialog(menu)
60 60 end },
61 61 video = { "Video Options", function()
... ...
... ... @@ -25,11 +25,12 @@ local Textbox = require "engine.ui.Textbox"
25 25
26 26 module(..., package.seeall, class.inherit(Dialog))
27 27
28   -function _M:init(title, text, min, max, action, cancel)
  28 +function _M:init(title, text, min, max, action, cancel, absolute)
29 29 self.action = action
30 30 self.cancel = cancel
31 31 self.min = min or 2
32 32 self.max = max or 25
  33 + self.absolute = absolute
33 34
34 35 Dialog.init(self, title, 320, 110)
35 36
... ... @@ -53,11 +54,11 @@ end
53 54
54 55 function _M:okclick()
55 56 self.name = self.c_box.text
56   - if self.name:len() >= self.min then
  57 + if self.name:len() >= self.min and self.name:len() <= self.max then
57 58 game:unregisterDialog(self)
58 59 self.action(self.name)
59 60 else
60   - Dialog:simplePopup("Error", "Must be between 2 and 25 characters.")
  61 + Dialog:simplePopup("Error", ("Must be between %i and %i characters."):format(self.min, self.max))
61 62 end
62 63 end
63 64
... ...
... ... @@ -23,21 +23,36 @@ local TreeList = require "engine.ui.TreeList"
23 23 local Textzone = require "engine.ui.Textzone"
24 24 local Separator = require "engine.ui.Separator"
25 25 local KeyBind = require "engine.KeyBind"
  26 +local Gestures = require "engine.ui.Gestures"
  27 +local GetText = require "engine.dialogs.GetText"
26 28
27 29 module(..., package.seeall, class.inherit(Dialog))
28 30
29   -function _M:init(key_source, force_all)
30   - Dialog.init(self, "Key bindings", 800, game.h)
  31 +function _M:init(key_source, force_all, gesture_source)
  32 + Dialog.init(self, "Key bindings", 800, game.h * 0.9)
  33 + self.gesture = gesture_source
  34 + self.key_source = key_source
31 35
32 36 self:generateList(key_source, force_all)
33 37
34   - self.c_tree = TreeList.new{width=self.iw, height=self.ih, sel_by_col=true, scrollbar=true, columns={
35   - {width=40, display_prop="name"},
36   - {width=30, display_prop="b1"},
37   - {width=30, display_prop="b2"},
38   - }, tree=self.tree,
39   - fct=function(item, sel, v) self:use(item, sel, v) end,
40   - }
  38 + if self.gesture then
  39 + self.c_tree = TreeList.new{width=self.iw, height=self.ih, sel_by_col=true, scrollbar=true, columns={
  40 + {width=40, display_prop="name"},
  41 + {width=20, display_prop="b1"},
  42 + {width=20, display_prop="b2"},
  43 + {width=20, display_prop="g"},
  44 + }, tree=self.tree,
  45 + fct=function(item, sel, v) self:use(item, sel, v) end,
  46 + }
  47 + else
  48 + self.c_tree = TreeList.new{width=self.iw, height=self.ih, sel_by_col=true, scrollbar=true, columns={
  49 + {width=40, display_prop="name"},
  50 + {width=30, display_prop="b1"},
  51 + {width=30, display_prop="b2"},
  52 + }, tree=self.tree,
  53 + fct=function(item, sel, v) self:use(item, sel, v) end,
  54 + }
  55 + end
41 56
42 57 self:loadUI{
43 58 {left=0, top=0, ui=self.c_tree},
... ... @@ -56,63 +71,118 @@ end
56 71 function _M:use(item)
57 72 local t = item
58 73 local curcol = self.c_tree.cur_col - 1
59   - if not item or item.nodes or curcol < 1 or curcol > 2 then return end
  74 + if not item or item.nodes or curcol < 1 or curcol > 3 then return end
60 75
61 76 --
62 77 -- Make a dialog to ask for the key
63 78 --
64   - local title = "Press a key (or escape) for: "..tostring(t.name)
65   - local font = self.font
66   - local w, h = font:size(title)
67   - local d = engine.Dialog.new(title, w + 8, h + 25, nil, nil, nil, font)
68   - d:keyCommands{__DEFAULT=function(sym, ctrl, shift, alt, meta, unicode)
69   - -- Modifier keys are not treated
70   - if not t.single_key and (sym == KeyBind._LCTRL or sym == KeyBind._RCTRL or
71   - sym == KeyBind._LSHIFT or sym == KeyBind._RSHIFT or
72   - sym == KeyBind._LALT or sym == KeyBind._RALT or
73   - sym == KeyBind._LMETA or sym == KeyBind._RMETA) then
74   - return
75   - end
76   -
77   - if sym == KeyBind._BACKSPACE then
78   - KeyBind.binds_remap[t.type] = KeyBind.binds_remap[t.type] or t.k.default
79   - KeyBind.binds_remap[t.type][curcol] = nil
80   - elseif sym ~= KeyBind._ESCAPE then
81   - local ks = KeyBind:makeKeyString(sym, ctrl, shift, alt, meta, unicode)
82   - print("Binding", t.name, "to", ks, "::", curcol)
83   -
84   - KeyBind.binds_remap[t.type] = KeyBind.binds_remap[t.type] or t.k.default
85   - KeyBind.binds_remap[t.type][curcol] = ks
86   - end
87   - self.c_tree:drawItem(item)
88   - game:unregisterDialog(d)
89   - end}
90   -
91   - d:mouseZones{ norestrict=true,
92   - { x=0, y=0, w=game.w, h=game.h, fct=function(button, x, y, xrel, yrel, tx, ty)
93   - if xrel or yrel then return end
94   - if button == "left" then return end
95   -
96   - local ks = KeyBind:makeMouseString(
97   - button,
98   - core.key.modState("ctrl") and true or false,
99   - core.key.modState("shift") and true or false,
100   - core.key.modState("alt") and true or false,
101   - core.key.modState("meta") and true or false
102   - )
103   - print("Binding", t.name, "to", ks)
104   -
105   - KeyBind.binds_remap[t.type] = KeyBind.binds_remap[t.type] or t.k.default
106   - KeyBind.binds_remap[t.type][curcol] = ks
  79 + if curcol == 1 or curcol == 2 then
  80 + local title = "Press a key (or escape) for: "..tostring(t.name)
  81 + local font = self.font
  82 + local w, h = font:size(title)
  83 + local d = engine.Dialog.new(title, w + 8, h + 25, nil, nil, nil, font)
  84 + d:keyCommands{__DEFAULT=function(sym, ctrl, shift, alt, meta, unicode)
  85 + -- Modifier keys are not treated
  86 + if not t.single_key and (sym == KeyBind._LCTRL or sym == KeyBind._RCTRL or
  87 + sym == KeyBind._LSHIFT or sym == KeyBind._RSHIFT or
  88 + sym == KeyBind._LALT or sym == KeyBind._RALT or
  89 + sym == KeyBind._LMETA or sym == KeyBind._RMETA) then
  90 + return
  91 + end
  92 +
  93 + if sym == KeyBind._BACKSPACE then
  94 + KeyBind.binds_remap[t.type] = KeyBind.binds_remap[t.type] or t.k.default
  95 + KeyBind.binds_remap[t.type][curcol] = nil
  96 + elseif sym ~= KeyBind._ESCAPE then
  97 + local ks = KeyBind:makeKeyString(sym, ctrl, shift, alt, meta, unicode)
  98 + print("Binding", t.name, "to", ks, "::", curcol)
  99 +
  100 + KeyBind.binds_remap[t.type] = KeyBind.binds_remap[t.type] or t.k.default
  101 + KeyBind.binds_remap[t.type][curcol] = ks
  102 + end
107 103 self.c_tree:drawItem(item)
108 104 game:unregisterDialog(d)
109   - end },
110   - }
  105 + end}
  106 +
  107 + d:mouseZones{ norestrict=true,
  108 + { x=0, y=0, w=game.w, h=game.h, fct=function(button, x, y, xrel, yrel, tx, ty)
  109 + if xrel or yrel then return end
  110 + if button == "left" then return end
  111 +
  112 + local ks = KeyBind:makeMouseString(
  113 + button,
  114 + core.key.modState("ctrl") and true or false,
  115 + core.key.modState("shift") and true or false,
  116 + core.key.modState("alt") and true or false,
  117 + core.key.modState("meta") and true or false
  118 + )
  119 + print("Binding", t.name, "to", ks)
  120 +
  121 + KeyBind.binds_remap[t.type] = KeyBind.binds_remap[t.type] or t.k.default
  122 + KeyBind.binds_remap[t.type][curcol] = ks
  123 + self.c_tree:drawItem(item)
  124 + game:unregisterDialog(d)
  125 + end },
  126 + }
111 127
112   - d.drawDialog = function(self, s)
113   - s:drawColorStringBlendedCentered(self.font, curcol == 1 and "Bind key" or "Bind alternate key", 2, 2, self.iw - 2, self.ih - 2)
  128 + d.drawDialog = function(self, s)
  129 + s:drawColorStringBlendedCentered(self.font, curcol == 1 and "Bind key" or "Bind alternate key", 2, 2, self.iw - 2, self.ih - 2)
  130 + end
  131 + game:registerDialog(d)
  132 + elseif curcol == 3 then
  133 + local title = "Make gesture (using right mouse button) or type it (or escape) for: "..tostring(t.name)
  134 + local font = self.font
  135 + local w, h = font:size(title)
  136 + local d = GetText.new(title, "Gesture", 0, 5,
  137 + function(gesture)
  138 + if item.g and item.g ~= "--" then
  139 + self.gesture:removeGesture(item.g)
  140 + end
  141 + KeyBind.binds_remap[t.type] = KeyBind.binds_remap[t.type] or t.k.default
  142 + if gesture == "" then
  143 + KeyBind.binds_remap[t.type][curcol] = nil
  144 + else
  145 + KeyBind.binds_remap[t.type][curcol] = KeyBind:makeGestureString(gesture)
  146 + end
  147 + self.gesture:addGesture(gesture, function() self.key_source:triggerVirtual(t.type) end, t.sortname)
  148 + self.c_tree:drawItem(item)
  149 + end,
  150 + function()
  151 +
  152 + end,
  153 + true)
  154 +
  155 + d.c_box.filter = function(c)
  156 + c=string.upper(c)
  157 + local text = table.concat(d.c_box.tmp)
  158 + if (c =="U" or c=="D" or c=="L" or c=="R") and c:byte(1)~=text:byte(d.c_box.cursor-1) and c:byte(1)~=text:byte(d.c_box.cursor) then
  159 + return c
  160 + else
  161 + return nil
  162 + end
  163 + end
  164 + d.mouse:registerZone(0, 0, game.w, game.h, function(button, mx, my, xrel, yrel, bx, by, event)
  165 + if button == "right" then
  166 + if event == "motion" then
  167 + self.gesture:changeMouseButton(true)
  168 + self.gesture:mouseMove(mx, my)
  169 + local text = table.concat(d.c_box.tmp)
  170 + if #text < 5 and self.gesture.lastgesture~="" and self.gesture.lastgesture:byte(1)~=text:byte(d.c_box.cursor-1) and self.gesture.lastgesture:byte(1) ~= text:byte(d.c_box.cursor) then
  171 + table.insert(d.c_box.tmp, d.c_box.cursor, self.gesture.lastgesture)
  172 + d.c_box.cursor = d.c_box.cursor + 1
  173 + d.c_box:updateText()
  174 + end
  175 + elseif event == "button" then
  176 + self.gesture:changeMouseButton(false)
  177 + self.gesture:reset()
  178 + end
  179 + end
  180 +
  181 + d:mouseEvent(button, mx, my, xrel, yrel, bx - d.display_x, by - d.display_y, event)
  182 + end)
  183 +
  184 + game:registerDialog(d)
114 185 end
115   - game:registerDialog(d)
116 186 end
117 187
118 188 function _M:generateList(key_source, force_all)
... ... @@ -143,8 +213,10 @@ function _M:generateList(key_source, force_all)
143 213 single_key = k.single_key,
144 214 bind1 = function(item) return KeyBind:getBindTable(k)[1] end,
145 215 bind2 = function(item) return KeyBind:getBindTable(k)[2] end,
  216 + bind3 = function(item) return KeyBind:getBindTable(k)[3] end,
146 217 b1 = function(item) return KeyBind:formatKeyString(util.getval(item.bind1, item)) end,
147 218 b2 = function(item) return KeyBind:formatKeyString(util.getval(item.bind2, item)) end,
  219 + g = function(item) return KeyBind:formatKeyString(util.getval(item.bind3, item)) end,
148 220 }
149 221 groups[k.group] = groups[k.group] or {}
150 222 table.insert(groups[k.group], item)
... ... @@ -154,7 +226,7 @@ function _M:generateList(key_source, force_all)
154 226 tree[#tree+1] = {
155 227 name = tstring{{"font","bold"}, {"color","GOLD"}, group:capitalize(), {"font","normal"}},
156 228 sortname = group:capitalize(),
157   - b1 = "", b2 = "",
  229 + b1 = "", b2 = "", g = "",
158 230 shown = true,
159 231 nodes = data,
160 232 }
... ...
... ... @@ -169,6 +169,9 @@ function _M:resize(w, h, nogen)
169 169 self.ix, self.iy = 5, 8 + 3 + self.font_bold_h
170 170 self.iw, self.ih = w - 2 * 5, h - 8 - 8 - 3 - self.font_bold_h
171 171
  172 +-- self.display_x = util.bound(self.display_x, 0, game.w - (self.w+self.frame.ox2))
  173 +-- self.display_y = util.bound(self.display_y, 0, game.h - (self.h+self.frame.oy2))
  174 +
172 175 if not nogen then self:generate() end
173 176 end
174 177
... ... @@ -423,8 +426,6 @@ end
423 426
424 427 function _M:toScreen(x, y, nb_keyframes)
425 428 if self.__hidden then return end
426   - x = util.bound(x, 0, game.w - (self.w+self.frame.ox2))
427   - y = util.bound(y, 0, game.h - (self.h+self.frame.oy2))
428 429
429 430 local zoom = 1
430 431 if self.__showup then
... ...
  1 +-- TE4 - T-Engine 4
  2 +-- Copyright (C) 2009, 2010, 2011 Nicolas Casalini
  3 +--
  4 +-- This program is free software: you can redistribute it and/or modify
  5 +-- it under the terms of the GNU General Public License as published by
  6 +-- the Free Software Foundation, either version 3 of the License, or
  7 +-- (at your option) any later version.
  8 +--
  9 +-- This program is distributed in the hope that it will be useful,
  10 +-- but WITHOUT ANY WARRANTY; without even the implied warranty of
  11 +-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12 +-- GNU General Public License for more details.
  13 +--
  14 +-- You should have received a copy of the GNU General Public License
  15 +-- along with this program. If not, see <http://www.gnu.org/licenses/>.
  16 +--
  17 +-- Nicolas Casalini "DarkGod"
  18 +-- darkgod@te4.org
  19 +
  20 +require "engine.class"
  21 +local KeyBind = require "engine.KeyBind"
  22 +
  23 +module(..., package.seeall, class.make)
  24 +
  25 +function _M:init(text, key_source, force_all)
  26 + assert(key_source, "no key source")
  27 + self.text = text or "Gesture: "
  28 + self.gesture = ""
  29 + self.gestures = {}
  30 + self.font = core.display.newFont("/data/font/Vera.ttf", 12)
  31 +
  32 + local gesttext = self.text.."WWWWW"
  33 + self.fontmax_w, self.font_h = self.font:size(gesttext)
  34 +
  35 + self.surface = core.display.newSurface(self.fontmax_w, self.font_h)
  36 + self.surface:drawColorStringBlended(self.font, self.text, 0, 0, 255, 255, 255, true)
  37 + self.texture, self.texture_w, self.texture_h = self.surface:glTexture()
  38 + self.timeout = 1.7
  39 + self.lastupdate = os.time()
  40 + self.gesturing = false
  41 + self.mousebuttondown = false
  42 + self.distance = 0
  43 + self.lastgesture = ""
  44 + self.lastgesturename = ""
  45 +
  46 + self:loadGestures(key_source, force_all)
  47 +end
  48 +
  49 +function _M:loadGestures(key_source, force_all)
  50 + local l = {}
  51 +
  52 + for virtual, t in pairs(KeyBind.binds_def) do
  53 + if (force_all or key_source.virtuals[virtual]) and t.group ~= "debug" then
  54 + l[#l+1] = t
  55 + end
  56 + end
  57 + table.sort(l, function(a,b)
  58 + if a.group ~= b.group then
  59 + return a.group < b.group
  60 + else
  61 + return a.order < b.order
  62 + end
  63 + end)
  64 +
  65 + -- Makes up the list
  66 + local tree = {}
  67 + local groups = {}
  68 + for _, k in ipairs(l) do
  69 + local bind3 = KeyBind:getBindTable(k)[3]
  70 + local gesture = KeyBind:formatKeyString(util.getval(bind3))
  71 + if k.name ~= "" and k.name~= "--" then
  72 + self:addGesture(gesture, function() key_source:triggerVirtual(k.type) end, k.name)
  73 + end
  74 + end
  75 +end
  76 +
  77 +function _M:mouseMove(mx, my)
  78 + if #self.gesture >= 5 then return end
  79 + if self.omx and self.omy then
  80 + self.distance = self.distance + (self.omy - my)^2 + (self.omx - mx)^2
  81 + if math.abs(self.omx - mx) > math.abs(self.omy - my) and self.distance > 100 then
  82 + if self.omx > mx then
  83 + if self.lastgesture~="L" then
  84 + self.gesture = self.gesture.."L"
  85 + self.lastgesture = "L"
  86 + end
  87 + else
  88 + if self.lastgesture~="R" then
  89 + self.gesture = self.gesture.."R"
  90 + self.lastgesture = "R"
  91 + end
  92 + end
  93 + self.gesturing = true
  94 + self.lastupdate = os.time()
  95 + self.distance = 0
  96 + end
  97 + if math.abs(self.omx - mx) < math.abs(self.omy - my) and self.distance > 100 then
  98 + if self.omy > my then
  99 + if self.lastgesture~="U" then
  100 + self.gesture = self.gesture.."U"
  101 + self.lastgesture = "U"
  102 + end
  103 + else
  104 + if self.lastgesture~="D" then
  105 + self.gesture = self.gesture.."D"
  106 + self.lastgesture = "D"
  107 + end
  108 + end
  109 + self.gesturing = true
  110 + self.lastupdate = os.time()
  111 + self.distance = 0
  112 + end
  113 + end
  114 +
  115 + self.omx = mx
  116 + self.omy = my
  117 +end
  118 +
  119 +function _M:isGesturing()
  120 + return self.gesturing
  121 +end
  122 +
  123 +function _M:isMouseButtonDown()
  124 + return self.mousebuttondown
  125 +end
  126 +
  127 +function _M:changeMouseButton(isDown)
  128 + self.mousebuttondown = isDown
  129 +end
  130 +
  131 +function _M:useGesture()
  132 + if self.gestures[self.gesture] then
  133 + self.gestures[self.gesture].func()
  134 + end
  135 +end
  136 +
  137 +function _M:reset()
  138 + self.gesturing = false
  139 + self.omx = nil
  140 + self.omy = nil
  141 + self.gesture = ""
  142 + self.lastgesture = ""
  143 + self.distance = 0
  144 +end
  145 +
  146 +function _M:addGesture(gesture, func, name)
  147 + self.gestures[gesture] = {}
  148 + self.gestures[gesture].func = func
  149 + self.gestures[gesture].name = name
  150 +end
  151 +
  152 +function _M:removeGesture(gesture)
  153 + if not self.gestures[gesture] then return end
  154 + self.gestures[gesture] = nil
  155 +end
  156 +
  157 +function _M:empty()
  158 + self.gestures = {}
  159 + self:reset()
  160 +end
  161 +
  162 +function _M:setTimeout(timeout)
  163 + self.timeout = timeout
  164 +end
  165 +
  166 +function _M:getLastGesture()
  167 + return self.gesture
  168 +end
  169 +
  170 +function _M:update()
  171 + local gesttxt = ""
  172 +
  173 + if self.gesturing == true then
  174 + gesttxt = self.text
  175 + if os.difftime(os.time(), self.lastupdate) >= self.timeout then
  176 + self:reset()
  177 + end
  178 + end
  179 +
  180 + gesttxt = gesttxt..self.gesture
  181 +
  182 + self.surface:erase(0,0,0,1)
  183 + self.surface:drawColorStringBlended(self.font, gesttxt, 0, 0, 255, 255, 255, true)
  184 + self.surface:updateTexture(self.texture)
  185 +end
  186 +
  187 +function _M:display(display_x, display_y)
  188 + self.texture:toScreenFull(display_x, display_y, self.fontmax_w, self.font_h, self.texture_w, self.texture_h)
  189 +
  190 + if self.gestures[self.gesture] then
  191 + if self.gestures[self.gesture].name == self.lastgesturename and self.gesturenametexure then
  192 + self.gesturenametexure:toScreenFull(display_x + self.fontmax_w, display_y, self.gesturenamefont_w , self.font_h, self.gesturenametexure_w, self.gesturenametexure_h)
  193 + else
  194 + self.gesturenamefont_w, _ = self.font:size(self.gestures[self.gesture].name)
  195 + local s = core.display.newSurface(self.gesturenamefont_w, self.font_h)
  196 + s:drawColorStringBlended(self.font, self.gestures[self.gesture].name, 0, 0, 255, 255, 255, true)
  197 + self.gesturenametexure, self.gesturenametexure_w, self.gesturenametexure_h = s:glTexture()
  198 + self.gesturenametexure:toScreenFull(display_x, display_y, self.fontmax_w, self.font_h, self.texture_w, self.texture_h)
  199 + end
  200 + self.lastgesturename = self.gestures[self.gesture].name
  201 + end
  202 +end
... ...
... ... @@ -54,6 +54,7 @@ local DebugConsole = require "engine.DebugConsole"
54 54 local FlyingText = require "engine.FlyingText"
55 55 local Tooltip = require "engine.Tooltip"
56 56 local Calendar = require "engine.Calendar"
  57 +local Gestures = require "engine.ui.Gestures"
57 58
58 59 local Dialog = require "engine.ui.Dialog"
59 60 local MapMenu = require "mod.dialogs.MapMenu"
... ... @@ -854,6 +855,10 @@ function _M:display(nb_keyframes)
854 855 self.minimap_bg:toScreen(0, 35, 200, 200)
855 856 self.minimap_scroll_x, self.minimap_scroll_y = util.bound(self.player.x - 25, 0, map.w - 50), util.bound(self.player.y - 25, 0, map.h - 50)
856 857 map:minimapDisplay(0, 35, self.minimap_scroll_x, self.minimap_scroll_y, 50, 50, 1)
  858 +
  859 + -- Mouse gestures
  860 + self.gestures:update()
  861 + self.gestures:display(map.display_x, map.display_y + map.viewport.height - self.gestures.font_h - 5)
857 862 end
858 863
859 864 -- We display the player's interface
... ... @@ -907,6 +912,9 @@ function _M:setupCommands()
907 912 -- Activate profiler keybinds
908 913 self.key:setupProfiler()
909 914
  915 + -- Activate mouse gestures
  916 + self.gestures = Gestures.new("Gesture: ", self.key, true)
  917 +
910 918 -- Helper function to not allow some actions on the wilderness map
911 919 local not_wild = function(f) return function() if self.zone and not self.zone.wilderness then f() else self.logPlayer(self.player, "You cannot do that on the world map.") end end end
912 920
... ... @@ -1183,7 +1191,24 @@ function _M:setupMouse(reset)
1183 1191 if self:targetMouse(button, mx, my, xrel, yrel, event) then return end
1184 1192
1185 1193 -- Handle Use menu
1186   - if button == "right" and not xrel and not yrel and event == "button" then self:mouseRightClick(mx, my) return end
  1194 + if button == "right" then
  1195 + if event == "motion" then
  1196 + self.gestures:changeMouseButton(true)
  1197 + self.gestures:mouseMove(mx, my)
  1198 + elseif event == "button" then
  1199 + if not self.gestures:isGesturing() then
  1200 + if not xrel and not yrel then
  1201 + -- Handle Use menu
  1202 + self:mouseRightClick(mx, my)
  1203 + return
  1204 + end
  1205 + else
  1206 + self.gestures:changeMouseButton(false)
  1207 + self.gestures:useGesture()
  1208 + self.gestures:reset()
  1209 + end
  1210 + end
  1211 + end
1187 1212
1188 1213 -- Default left button action
1189 1214 if button == "left" and not xrel and not yrel and event == "button" and self.zone and not self.zone.wilderness then if self:mouseLeftClick(mx, my) then return end end
... ...