diff --git a/button.lua b/button.lua index 4d98d86..24f3c38 100644 --- a/button.lua +++ b/button.lua @@ -41,8 +41,6 @@ function Button:new(args) self.text = self.text or "" self.align = self.align or 'center' self.valign = self.valign or 'center' - self.color = self.color or core.theme.color - self.cornerRadius = self.cornerRadius or core.theme.cornerRadius self.active = false if not self.notranslate then self.text = T(self.text) @@ -70,10 +68,10 @@ end function Button:draw() local x,y,w,h = self.x,self.y,self.w,self.h - local font = self.font or love.graphics.getFont() - local c = self:colorForState() + local color, font, cornerRadius = core.themeForWidget(self) + local c = core.colorForWidgetState(self, color) - core.drawBox(x,y,w,h, c, self.cornerRadius) + core.drawBox(x,y,w,h, c, cornerRadius) love.graphics.setColor(c.fg) love.graphics.setFont(font) diff --git a/checkbox.lua b/checkbox.lua index 7316149..62a0668 100644 --- a/checkbox.lua +++ b/checkbox.lua @@ -27,7 +27,6 @@ Checkbox.__index = Checkbox -- @field text (string) text displayed inside the Checkbox -- @field[opt='center'] valign (string) vertical alignment 'top', 'bottom', 'center' -- @field[opt='center'] align (string) horizontal alignment, 'left', 'center', 'right' --- @field cornerRadius (number) radius for rounded corner -- @field notranslate (boolean) don't translate text -- @table CheckboxAttributes @@ -41,8 +40,6 @@ function Checkbox:new(args) self.text = self.notranslate and self.text or T(self.text) self.align = self.align or 'left' self.valign = self.valign or 'center' - self.color = self.color or core.theme.color - self.cornerRadius = self.cornerRadius or core.theme.cornerRadius self.checked = self.checked or false return self end @@ -64,11 +61,11 @@ end function Checkbox:draw() local x,y,w,h = self.x,self.y,self.w,self.h - local c = self:colorForState() - local font = self.font or love.graphics.getFont() + local color, font, cornerRadius = core.themeForWidget(self) + local c = core.colorForWidgetState(self, color) -- Draw checkbox - core.drawBox(x+h/10,y+h/10,h*.8,h*.8, c, self.cornerRadius) + core.drawBox(x+h/10,y+h/10,h*.8,h*.8, c, cornerRadius) love.graphics.setColor(c.fg) if self.checked then love.graphics.setLineStyle('smooth') diff --git a/choice.lua b/choice.lua index 2f820ce..1081a21 100644 --- a/choice.lua +++ b/choice.lua @@ -27,7 +27,6 @@ Choice.__index = Choice -- @field nowrap (boolean) disable choices wrapping -- @field[opt='center'] valign (string) vertical alignment 'top', 'bottom', 'center' -- @field[opt='center'] align (string) horizontal alignment, 'left', 'center', 'right' --- @field cornerRadius (number) radius for rounded corners -- @field notranslate (boolean) don't translate text -- @table ChoiceAttributes @@ -39,8 +38,6 @@ function Choice:new(args) self.align = self.align or 'center' self.valign = self.valign or 'center' - self.cornerRadius = self.cornerRadius or core.theme.cornerRadius - self.color = self.color or core.theme.color self.hovered = false self.choices = self.choices or { "" } self.nowrap = self.nowrap or #self.choices < 2 @@ -134,14 +131,14 @@ end function Choice:draw() local x,y,w,h = self.x,self.y,self.w,self.h - local font = self.font or love.graphics.getFont() - local c = self:colorForState() + local color, font, cornerRadius = core.themeForWidget(self) + local c = core.colorForWidgetState(self, color) - core.drawBox(x,y,w,h, c, self.cornerRadius) + core.drawBox(x,y,w,h, c, cornerRadius) if self.ui.focused == self then -- draw < and > arrows, desaturate color if arrow is disabled - local cc = self.color.hovered + local cc = color.hovered love.graphics.setLineStyle('smooth') love.graphics.setLineWidth(3) diff --git a/core.lua b/core.lua index fbee686..fb3e65d 100644 --- a/core.lua +++ b/core.lua @@ -1,7 +1,10 @@ -local BASE = (...):gsub('core$', '') +--- Local drawing helpers +-- +-- @module yui.core +-- @copyright 2022, The DoubleFourteen Code Forge +-- @author Lorenzo Cogotti, Andrea Pasquini -local core = { theme = require(BASE..'theme') } -core.__index = core +local core = {} -- Helpers for drawing function core.verticalOffsetForAlign(valign, font, h) @@ -10,10 +13,33 @@ function core.verticalOffsetForAlign(valign, font, h) elseif valign == 'bottom' then return h - font:getHeight() end - -- else: "middle" + -- else: 'middle' return (h - font:getHeight()) / 2 end +function core.themeForWidget(widget) + local uiTheme = widget.ui.theme + local theme = widget.theme or uiTheme + + local color = theme.color or uiTheme.color + local font = theme.font or uiTheme.font or love.graphics.getFont() + local cornerRadius = theme.cornerRadius or uiTheme.cornerRadius + + return color, font, cornerRadius +end + +function core.colorForWidgetState(widget, color) + color = color or widget.theme.color or widget.ui.theme.color + + if widget.active then + return color.active + elseif widget:isFocused() then + return color.hovered + else + return color.normal + end +end + function core.drawBox(x,y,w,h, color, cornerRadius) w = math.max(cornerRadius/2, w) if h < cornerRadius/2 then diff --git a/init.lua b/init.lua index 0747590..20b5b1c 100644 --- a/init.lua +++ b/init.lua @@ -1,3 +1,14 @@ +--- Yui is Yet another User Interface library +-- +-- @module yui +-- @copyright 2022, The DoubleFourteen Code Forge +-- @author Lorenzo Cogotti, Andrea Pasquini +-- +-- This module exposes every module in Yui, +-- except the local @{yui.core} module and the +-- global @{yui.theme} (still accessible from @{yui.Ui}). +-- Refer to each module documentation. + local BASE = (...)..'.' return { @@ -13,6 +24,4 @@ return { Spacer = require(BASE..'spacer'), Ui = require(BASE..'ui'), Widget = require(BASE..'widget'), - - theme = require(BASE..'theme') } diff --git a/input.lua b/input.lua index eae7c8c..1d9bfdb 100644 --- a/input.lua +++ b/input.lua @@ -31,7 +31,6 @@ end -- and @{yui.Widget.WidgetCallbacks|callbacks}. -- -- @field text (string) text displayed inside the Input --- @field cornerRadius (number) radius for rounded corners -- @table InputAttributes @@ -41,8 +40,6 @@ function Input:new(args) self = setmetatable(args, self) self.text = self.text or "" - self.color = self.color or core.theme.color - self.cornerRadius = self.cornerRadius or core.theme.cornerRadius self.cursor = math.max(1, math.min(utf8.len(self.text)+1, self.cursor or utf8.len(self.text)+1)) self.candidate = { text = "", start = 0, length = 0 } @@ -155,7 +152,7 @@ function Input:draw() -- position 6: hello| local x,y,w,h = self.x,self.y,self.w,self.h - local font = self.font or love.graphics.getFont() + local color, font, cornerRadius = core.themeForWidget(self) local th = font:getHeight() local tw = font:getWidth(self.text) @@ -199,7 +196,7 @@ function Input:draw() end -- Perform actual draw - core.drawBox(x,y,w,h, self.color.normal, self.cornerRadius) + core.drawBox(x,y,w,h, color.normal, cornerRadius) -- Apply text margins x = math.min(x + 3, x + w) @@ -212,7 +209,7 @@ function Input:draw() x = x - self.drawofs -- Text - love.graphics.setColor(self.color.normal.fg) + love.graphics.setColor(color.normal.fg) love.graphics.setFont(font) love.graphics.print(self.text, x, y + (h-th)/2) @@ -220,7 +217,7 @@ function Input:draw() -- Candidate text local ctw = font:getWidth(self.candidate.text) - love.graphics.setColor(self.color.normal.fg) + love.graphics.setColor(color.normal.fg) love.graphics.print(self.candidate.text, x + tw, y + (h-th)/2) -- Candidate text rectangle box diff --git a/label.lua b/label.lua index 7cc68b4..06ef6f2 100644 --- a/label.lua +++ b/label.lua @@ -36,18 +36,16 @@ function Label:new(args) self.text = self.notranslate and self.text or T(self.text) self.align = self.align or 'center' self.valign = self.valign or 'center' - self.color = self.color or core.theme.color return self end function Label:draw() local x,y,w,h = self.x,self.y,self.w,self.h - local font = self.font or love.graphics.getFont() - local c = self.color.normal + local color, font, _ = core.themeForWidget(self) y = y + core.verticalOffsetForAlign(self.valign, font, h) - love.graphics.setColor(c.fg) + love.graphics.setColor(color.normal.fg) love.graphics.setFont(font) shadowtext.printf(self.text, x+2, y, w-4, self.align) end diff --git a/slider.lua b/slider.lua index d9c1dc9..fffda2c 100644 --- a/slider.lua +++ b/slider.lua @@ -24,7 +24,6 @@ Slider.__index = Slider -- @field vertical (boolean) true for vertical slider, false or nil for horizontal slider -- @field value (number) default value -- @field step (number) number of slider's steps --- @field cornerRadius (number) radius for rounded corners -- @table SliderAttributes @@ -33,8 +32,6 @@ Slider.__index = Slider function Slider:new(args) self = setmetatable(args, self) - self.color = self.color or core.theme.color - self.cornerRadius = self.cornerRadius or core.theme.cornerRadius self.vertical = self.vertical or false self.min = self.min or 0 self.max = self.max or 1 @@ -87,7 +84,8 @@ end function Slider:draw() local x,y,w,h = self.x,self.y,self.w,self.h local r = math.min(w,h) / 2.1 - local c = self:colorForState() + local color, _, cornerRadius = core.themeForWidget(self) + local c = core.colorForWidgetState(self, color) local fraction = (self.value - self.min) / (self.max - self.min) local xb, yb, wb, hb -- size of the progress bar @@ -99,8 +97,8 @@ function Slider:draw() xb, yb, wb, hb = x,y, w*fraction, h end - core.drawBox(x,y,w,h, c, self.cornerRadius) - core.drawBox(xb,yb,wb,hb, {bg=c.fg}, self.cornerRadius) + core.drawBox(x,y,w,h, c, cornerRadius) + core.drawBox(xb,yb,wb,hb, { bg = c.fg }, cornerRadius) if self:isFocused() then love.graphics.setColor(c.fg) diff --git a/theme.lua b/theme.lua index 6bda54e..e65eb07 100644 --- a/theme.lua +++ b/theme.lua @@ -1,4 +1,4 @@ ---- Global visual theme settings +--- Visual theme settings -- -- @module yui.theme -- @copyright 2022, The DoubleFourteen Code Forge @@ -7,8 +7,9 @@ --- Defines common visual attributes and colors applied to every @{yui.Widget|Widget}. -- -- @field cornerRadius (number) radius for rounded corners +-- @field font (love.graphics.Font) font used for text (defaults to love.graphics.getFont()) -- @field color (@{ColorPalette}) default @{yui.Widget|Widget} color theme --- @table theme +-- @table Theme --- Defines which color corresponds to each @{yui.Widget|Widget} state. -- @@ -22,9 +23,12 @@ -- @field bg (table) background color -- @field fg (table) foreground color (typically used for text) -- @table Color + local theme = { cornerRadius = 4, + -- font = nil defaults to love.graphics.getFont() + color = { normal = {bg = { 0.25, 0.25, 0.25}, fg = {0.73, 0.73, 0.73}}, hovered = {bg = { 0.19, 0.6, 0.73}, fg = {1, 1, 1}}, diff --git a/ui.lua b/ui.lua index a362e21..3a810f0 100644 --- a/ui.lua +++ b/ui.lua @@ -14,14 +14,16 @@ local Widget = require(BASE..'widget') local Layout = require(BASE..'layout') local Columns = require(BASE..'columns') local Rows = require(BASE..'rows') - +local theme = require(BASE..'theme') local gear = require 'lib.gear' local Timer = gear.Timer local isinstance = gear.meta.isinstance local pointinrect = gear.rect.pointinside -local Ui = {} +local Ui = { + theme = theme -- fallback theme +} Ui.__index = Ui @@ -66,6 +68,7 @@ end -- -- @field x (number) x position of the Ui -- @field y (number) y position of the Ui +-- @field theme (@{yui.theme.Theme|Theme}) custom global Ui theme, defaults to @{yui.theme} -- @table UiAttributes diff --git a/widget.lua b/widget.lua index f82a3b3..3388fbf 100644 --- a/widget.lua +++ b/widget.lua @@ -13,6 +13,7 @@ --- Attributes accepted by all the Widget classes -- @field w (number) widget width -- @field h (number) widget height +-- @field theme (@{yui.theme.Theme|Theme}) widget specific theme -- @field color (@{yui.theme.ColorPalette|ColorPalette}) widget color -- @table WidgetAttributes local rectunion = require('lib.gear.rect').union @@ -105,17 +106,6 @@ function Widget:recalculateBounds() end end --- Helper for drawing -function Widget:colorForState() - if self.active then - return self.color.active - elseif self:isFocused() then - return self.color.hovered - else - return self.color.normal - end -end - -- NOP hooks for UI internal use function Widget:loseFocus() end function Widget:gainFocus() end