diff --git a/core.lua b/core.lua index 6c32fbd..3b92337 100644 --- a/core.lua +++ b/core.lua @@ -36,6 +36,10 @@ function suit.getOptionsAndSize(opt, ...) end -- gui state +function suit:setHovered(id) + return self.hovered ~= id +end + function suit:anyHovered() return self.hovered ~= nil end @@ -48,6 +52,10 @@ function suit:wasHovered(id) return id == self.hovered_last end +function suit:setActive(id) + return self.active ~= nil +end + function suit:anyActive() return self.active ~= nil end @@ -56,6 +64,15 @@ function suit:isActive(id) return id == self.active end + +function suit:setHit(id) + self.hit = id + -- simulate mouse release on button -- see suit:mouseReleasedOn() + self.mouse_button_down = false + self.active = id + self.hovered = id +end + function suit:anyHit() return self.hit ~= nil end @@ -182,7 +199,7 @@ end function suit:draw() self:exitFrame() love.graphics.push('all') - for i = 1,self.draw_queue.n do + for i = self.draw_queue.n,1,-1 do self.draw_queue[i]() end love.graphics.pop() diff --git a/docs/_static/different-ids.gif b/docs/_static/different-ids.gif new file mode 100644 index 0000000..bb53636 Binary files /dev/null and b/docs/_static/different-ids.gif differ diff --git a/docs/_static/same-ids.gif b/docs/_static/same-ids.gif new file mode 100644 index 0000000..9dab1fe Binary files /dev/null and b/docs/_static/same-ids.gif differ diff --git a/docs/gettingstarted.rst b/docs/gettingstarted.rst index 2d87691..51605f3 100644 --- a/docs/gettingstarted.rst +++ b/docs/gettingstarted.rst @@ -231,6 +231,71 @@ available space (but you have to tell how much space there is beforehand). Refer to the :doc:`Layout ` documentation for more information. +Widget ids +---------- + +Each widget is identified by an ``id`` [4]_. Internally, this ``id`` is used t +figure out which widget should handle user input like mouse clicks and keyboard +presses. +Unless specified otherwise, the ``id`` is the same as the first argument, i.e., +the ``id`` of ``Button("Hello, World!", ...)`` will be the string +``"Hello, World!"``. +In almost all of the cases, this will work fine and you don't have to worry about +this ``id`` business. + +Well, almost. Problems arise when two widgets share the same id, like here:: + + local suit = require 'suit' + + function love.update() + suit.layout:reset(100, 100) + suit.layout:padding(10) + + if suit.Button("Button", suit.layout:row(200, 30)).hit then + love.graphics.setBackgroundColor(255,255,255) + end + if suit.Button("Button", suit.layout:row()).hit then + love.graphics.setBackgroundColor(0,0,0) + end + end + + function love.draw() + suit:draw() + end + +.. image:: _static/same-ids.gif + +If the first button is hovered, both buttons will be highlighted, and if it pressed, +both actions will be carried out. +Hovering the second button will not affect the first, and clicking it will highlight +both buttons, but only execute the action of the second button [5]_. + +Luckily, there is a fix: you can specify the ``id`` of any widget using the ``id`` +option, like so:: + + local suit = require 'suit' + + function love.update() + suit.layout:reset(100, 100) + suit.layout:padding(10) + + if suit.Button("Button", {id=1}, suit.layout:row(200, 30)).hit then + love.graphics.setBackgroundColor(255,255,255) + end + if suit.Button("Button", {id=2}, suit.layout:row()).hit then + love.graphics.setBackgroundColor(0,0,0) + end + end + + function love.draw() + suit:draw() + end + +.. image:: _static/different-ids.gif + +Now, events from one button will not propagate to the other. Here, the both ``id`` s +are numbers, but you can use any Lua value except ``nil`` and ``false``. + Themeing -------- @@ -325,3 +390,6 @@ table or use some metatable magic:: .. [1] But it thinks you can handle that. .. [2] Proportion determined by rigorous scientific experiments [3]_. .. [3] And theoretic reasoning. Mostly that, actually. +.. [4] Welcome to the tautology club! +.. [5] Immediate mode is to blame: When the second button is processed, the first + one is already fully evaluated. Time can not be reversed, not even by love. diff --git a/docs/layout.rst b/docs/layout.rst index dfadf06..3faebd9 100644 --- a/docs/layout.rst +++ b/docs/layout.rst @@ -67,6 +67,8 @@ according to the size of the popped layout. Used for nested row/column layouts. +.. _layout-row: + .. function:: row(w,h) :param mixed w,h: Cell width and height (optional). @@ -91,14 +93,20 @@ Used to provide the last four arguments to a widget, e.g.:: suit.Button("Options", suit.layout:row()) suit.Button("Quit", suit.layout:row(nil, "median")) +.. function:: down(w,h) + +An alias for :ref:`layout:row() `. + +.. _layout-col: + .. function:: col(w,h) :param mixed w,h: Cell width and height (optional). :returns: Position and size of the cell: ``x,y,w,h``. -Creates a new cell right to the current cell with width ``w`` and height ``h``. -If either ``w`` or ``h`` is omitted, the value is set the last used value. Both -``w`` and ``h`` can be a string, which takes the following meaning: +Creates a new cell to the right of the current cell with width ``w`` and height +``h``. If either ``w`` or ``h`` is omitted, the value is set the last used +value. Both ``w`` and ``h`` can be a string, which takes the following meaning: ``max`` Maximum of all values since the last reset. @@ -114,6 +122,59 @@ Used to provide the last four arguments to a widget, e.g.:: suit.Button("OK", suit.layout:col(100,30)) suit.Button("Cancel", suit.layout:col("max")) +.. function:: right(w,h) + +An alias for :ref:`layout:col() `. + +.. function:: up(w,h) + + :param mixed w,h: Cell width and height (optional). + :returns: Position and size of the cell: ``x,y,w,h``. + +Creates a new cell above the current cell with width ``w`` and height ``h``. If +either ``w`` or ``h`` is omitted, the value is set the last used value. Both +``w`` and ``h`` can be a string, which takes the following meaning: + +``max`` + Maximum of all values since the last reset. + +``min`` + Mimimum of all values since the last reset. + +``median`` + Median of all values since the last reset. + +Be careful when mixing ``up()`` and :ref:`layout:row() `, as suit +does no checking to make sure cells don't overlap. e.g.:: + + suit.Button("A", suit.layout:row(100,30)) + suit.Button("B", suit.layout:row()) + suit.Button("Also A", suit.layout:up()) + +.. function:: left(w,h) + + :param mixed w,h: Cell width and height (optional). + :returns: Position and size of the cell: ``x,y,w,h``. + +Creates a new cell to the left of the current cell with width ``w`` and height +``h``. If either ``w`` or ``h`` is omitted, the value is set the last used +value. Both ``w`` and ``h`` can be a string, which takes the following meaning: + +``max`` + Maximum of all values since the last reset. + +``min`` + Mimimum of all values since the last reset. + +``median`` + Median of all values since the last reset. + +Be careful when mixing ``left()`` and :ref:`layout:col() `, as suit +does no checking to make sure cells don't overlap. e.g.:: + + suit.Button("A", suit.layout:col(100,30)) + suit.Button("B", suit.layout:col()) + suit.Button("Also A", suit.layout:left()) Precomputed Layouts ------------------- diff --git a/docs/widgets.rst b/docs/widgets.rst index 5c6c230..c0f18eb 100644 --- a/docs/widgets.rst +++ b/docs/widgets.rst @@ -30,7 +30,7 @@ Creates a label at position ``(x,y)`` with width ``w`` and height ``h``. .. function:: ImageButton(normal, options, x,y) - :param Image notmal: Image of the button in normal state. + :param Image normal: Image of the button in normal state. :param table options: Widget options. :param numbers x,y: Upper left corner of the widget. :returns: Return state (see below). diff --git a/init.lua b/init.lua index 511cdfa..3779c90 100644 --- a/init.lua +++ b/init.lua @@ -11,11 +11,14 @@ return setmetatable({ getOptionsAndSize = suit.getOptionsAndSize, -- core functions + setHovered = function(...) return instance:setHovered(...) end, anyHovered = function(...) return instance:anyHovered(...) end, isHovered = function(...) return instance:isHovered(...) end, wasHovered = function(...) return instance:wasHovered(...) end, anyActive = function(...) return instance:anyActive(...) end, + setActive = function(...) return instance:setActive(...) end, isActive = function(...) return instance:isActive(...) end, + setHit = function(...) return instance:setHit(...) end, anyHit = function(...) return instance:anyHit(...) end, isHit = function(...) return instance:isHit(...) end, diff --git a/input.lua b/input.lua index cab9522..5c9f29d 100644 --- a/input.lua +++ b/input.lua @@ -49,6 +49,11 @@ return function(core, input, ...) end -- user interaction + if input.forcefocus ~= nil and input.forcefocus then + core.active = opt.id + input.forcefocus = false + end + opt.state = core:registerHitbox(opt.id, x,y,w,h) opt.hasKeyboardFocus = core:grabKeyboardFocus(opt.id) diff --git a/layout.lua b/layout.lua index 58c7034..fd6b08c 100644 --- a/layout.lua +++ b/layout.lua @@ -37,20 +37,12 @@ end Layout.nextDown = Layout.nextRow -function Layout:nextUp() - return self._x, self._y - self._h - self._pady -end - function Layout:nextCol() return self._x + self._w + self._padx, self._y end Layout.nextRight = Layout.nextCol -function Layout:nextLeft() - return self._x - self._w - self._padx, self._y -end - function Layout:push(x,y) self._stack[#self._stack+1] = { self._x, self._y, @@ -152,7 +144,7 @@ Layout.down = Layout.row function Layout:up(w, h) w,h = calc_width_height(self, w, h) - local x,y = self._x, self._y - (self._h or 0) + local x,y = self._x, self._y - (self._h and h or 0) if not self._isFirstCell then y = y - self._pady @@ -184,7 +176,7 @@ Layout.right = Layout.col function Layout:left(w, h) w,h = calc_width_height(self, w, h) - local x,y = self._x - (self._w or 0), self._y + local x,y = self._x - (self._w and w or 0), self._y if not self._isFirstCell then x = x - self._padx diff --git a/theme.lua b/theme.lua index 53bdbf4..71081fa 100644 --- a/theme.lua +++ b/theme.lua @@ -19,7 +19,7 @@ function theme.getColorForState(opt) end function theme.drawBox(x,y,w,h, colors, cornerRadius) - local colors = colors or theme.getColorForState(opt) + colors = colors or theme.getColorForState(opt) cornerRadius = cornerRadius or theme.cornerRadius w = math.max(cornerRadius/2, w) if h < cornerRadius/2 then