342 lines
9.6 KiB
Lua
342 lines
9.6 KiB
Lua
--
|
|
-- MessageBox Widget/Dialog.
|
|
-- @copyright Jefferson Gonzalez
|
|
-- @license MIT
|
|
--
|
|
|
|
local core = require "core"
|
|
local common = require "core.common"
|
|
local style = require "core.style"
|
|
local Widget = require "libraries.widget"
|
|
local Button = require "libraries.widget.button"
|
|
local Label = require "libraries.widget.label"
|
|
|
|
---@class widget.messagebox : widget
|
|
---@field private title widget.label
|
|
---@field private icon widget.label
|
|
---@field private message widget.label
|
|
---@field private buttons widget.button[]
|
|
local MessageBox = Widget:extend()
|
|
|
|
MessageBox.icon_huge_font = style.icon_font:copy(50 * SCALE)
|
|
|
|
MessageBox.ICON_ERROR = "X"
|
|
MessageBox.ICON_INFO = "i"
|
|
MessageBox.ICON_WARNING = "!"
|
|
|
|
---@alias widget.messagebox.icontype
|
|
---|>`MessageBox.ICON_ERROR`
|
|
---| `MessageBox.ICON_INFO`
|
|
---| `MessageBox.ICON_WARNING`
|
|
|
|
MessageBox.BUTTONS_OK = 1
|
|
MessageBox.BUTTONS_OK_CANCEL = 2
|
|
MessageBox.BUTTONS_YES_NO = 3
|
|
MessageBox.BUTTONS_YES_NO_CANCEL = 4
|
|
|
|
---@alias widget.messagebox.buttonstype
|
|
---|>`MessageBox.BUTTONS_OK`
|
|
---| `MessageBox.BUTTONS_OK_CANCEL`
|
|
---| `MessageBox.BUTTONS_YES_NO`
|
|
---| `MessageBox.BUTTONS_YES_NO_CANCEL`
|
|
|
|
---@alias widget.messagebox.onclosehandler fun(self: widget.messagebox, button_id: integer, button: widget.button)
|
|
|
|
---Constructor
|
|
---@param parent widget
|
|
---@param title string
|
|
---@param message string | widget.styledtext
|
|
---@param icon widget.messagebox.icontype
|
|
---@param icon_color renderer.color
|
|
function MessageBox:new(parent, title, message, icon, icon_color)
|
|
MessageBox.super.new(self, parent)
|
|
self.type_name = "widget.messagebox"
|
|
self.draggable = true
|
|
self.scrollable = true
|
|
self.title = Label(self, "")
|
|
self.icon = Label(self, "")
|
|
self.message = Label(self, "")
|
|
self.buttons = {}
|
|
self.last_scale = SCALE
|
|
|
|
self:set_title(title or "")
|
|
self:set_message(message or "")
|
|
self:set_icon(icon or "", icon_color)
|
|
end
|
|
|
|
---Change the message box title.
|
|
---@param text string | widget.styledtext
|
|
function MessageBox:set_title(text)
|
|
self.title:set_label(text)
|
|
end
|
|
|
|
---Change the message box icon.
|
|
---@param icon widget.messagebox.icontype
|
|
---@param color? renderer.color
|
|
function MessageBox:set_icon(icon, color)
|
|
if not color then
|
|
color = style.text
|
|
if icon == MessageBox.ICON_WARNING then
|
|
color = { common.color "#c7763e" }
|
|
elseif icon == MessageBox.ICON_ERROR then
|
|
color = { common.color "#c73e3e" }
|
|
end
|
|
end
|
|
self.icon:set_label({ MessageBox.icon_huge_font, color, icon })
|
|
end
|
|
|
|
---Change the message box message.
|
|
---@param text string | widget.styledtext
|
|
function MessageBox:set_message(text)
|
|
self.message:set_label(text)
|
|
end
|
|
|
|
---Adds a new button to the message box.
|
|
---@param button_or_label string|widget.button
|
|
function MessageBox:add_button(button_or_label)
|
|
if type(button_or_label) == "table" then
|
|
table.insert(self.buttons, button_or_label)
|
|
else
|
|
local button = Button(self, button_or_label)
|
|
table.insert(self.buttons, button)
|
|
end
|
|
|
|
local button_id = #self.buttons
|
|
local new_button = self.buttons[button_id]
|
|
local on_click = new_button.on_click
|
|
new_button.on_click = function(this, ...)
|
|
on_click(this, ...)
|
|
self:on_close(button_id, new_button)
|
|
end
|
|
end
|
|
|
|
---Calculate the width of all buttons combined.
|
|
---@return number width
|
|
function MessageBox:get_buttons_width()
|
|
local width = 0
|
|
if #self.buttons > 0 then
|
|
for _, button in ipairs(self.buttons) do
|
|
width = width + button:get_width()
|
|
end
|
|
-- add padding inbetween buttons
|
|
if #self.buttons > 1 then
|
|
width = width + ((style.padding.x) * (#self.buttons - 1))
|
|
end
|
|
end
|
|
return width
|
|
end
|
|
|
|
---Get the height of biggest button.
|
|
---@return number height
|
|
function MessageBox:get_buttons_height()
|
|
local height = 0
|
|
if #self.buttons > 0 then
|
|
for _, button in ipairs(self.buttons) do
|
|
height = math.max(height, button:get_height())
|
|
end
|
|
end
|
|
return height
|
|
end
|
|
|
|
---Position the buttons relative to the message.
|
|
function MessageBox:reposition_buttons()
|
|
local buttons_width = self:get_buttons_width()
|
|
local buttons_x = ((self.size.x / 2) - (buttons_width / 2))
|
|
local buttons_y = self.message:get_bottom() + (style.padding.y * 2)
|
|
if self.icon.label[3] ~= "" then
|
|
buttons_y = math.max(
|
|
buttons_y, self.icon:get_bottom() + (style.padding.y * 2)
|
|
)
|
|
end
|
|
|
|
for _, button in ipairs(self.buttons) do
|
|
button:set_position(buttons_x, buttons_y)
|
|
buttons_x = buttons_x + button:get_width() + style.padding.x
|
|
end
|
|
end
|
|
|
|
---Calculate the MessageBox size, centers it relative to screen and shows it.
|
|
function MessageBox:show()
|
|
MessageBox.super.show(self)
|
|
self:update()
|
|
self:centered()
|
|
end
|
|
|
|
---Called when the user clicks one of the buttons in the message box.
|
|
---@param button_id integer
|
|
---@param button widget.button
|
|
---@diagnostic disable-next-line
|
|
function MessageBox:on_close(button_id, button) self:hide() end
|
|
|
|
function MessageBox:update()
|
|
if not MessageBox.super.update(self) then return false end
|
|
|
|
if self.last_scale ~= SCALE then
|
|
MessageBox.icon_huge_font = style.icon_font:copy(50 * SCALE)
|
|
self.last_scale = SCALE
|
|
self.icon.label[1] = MessageBox.icon_huge_font
|
|
elseif self.updated then
|
|
self.updated = true
|
|
return
|
|
end
|
|
|
|
local width = math.max(self.title:get_width())
|
|
width = math.max(width, self.message:get_width() + self.icon:get_width())
|
|
width = math.max(width, self:get_buttons_width())
|
|
|
|
local height = self.title:get_height() + style.padding.y
|
|
if self.icon.label[3] == "" then
|
|
height = height + self.message:get_height() + (style.padding.y * 2)
|
|
else
|
|
height = height
|
|
+ math.max(self.icon:get_height(), self.message:get_height())
|
|
+ (style.padding.y * 2)
|
|
end
|
|
height = height + self:get_buttons_height()
|
|
|
|
self:set_size(width + style.padding.x * 2, height + style.padding.y * 2)
|
|
|
|
self.title:set_position(
|
|
style.padding.x / 2,
|
|
style.padding.y / 2
|
|
)
|
|
|
|
self.icon:set_position(
|
|
style.padding.x,
|
|
self.title:get_bottom() + style.padding.y
|
|
)
|
|
|
|
if self.icon.label[3] == "" then
|
|
self.message:set_position(
|
|
style.padding.x,
|
|
self.title:get_bottom() + style.padding.y
|
|
)
|
|
else
|
|
local msg_y = self.title:get_bottom() + style.padding.y + 10
|
|
if self.icon:get_height() > self.message:get_height() then
|
|
msg_y = (self.icon:get_height() / 2)
|
|
- (self.message:get_height() / 2)
|
|
+ self.title:get_bottom() + style.padding.y
|
|
end
|
|
self.message:set_position(
|
|
self.icon:get_width() + (style.padding.x * 2) - (style.padding.x / 2),
|
|
msg_y
|
|
)
|
|
end
|
|
|
|
self:reposition_buttons()
|
|
|
|
return true
|
|
end
|
|
|
|
---We overwrite default draw function to draw the title background.
|
|
function MessageBox:draw()
|
|
if not self:is_visible() then return false end
|
|
|
|
Widget.super.draw(self)
|
|
|
|
self:draw_border()
|
|
|
|
if self.background_color then
|
|
self:draw_background(self.background_color)
|
|
else
|
|
self:draw_background(
|
|
self.parent and style.background or style.background2
|
|
)
|
|
end
|
|
|
|
if #self.childs > 0 then
|
|
core.push_clip_rect(
|
|
self.position.x,
|
|
self.position.y,
|
|
self.size.x,
|
|
self.size.y
|
|
)
|
|
end
|
|
|
|
-- draw the title background
|
|
renderer.draw_rect(
|
|
self.position.x,
|
|
self.position.y,
|
|
self.size.x, self.title:get_height() + style.padding.y,
|
|
style.selection
|
|
)
|
|
|
|
for i=#self.childs, 1, -1 do
|
|
self.childs[i]:draw()
|
|
end
|
|
|
|
if #self.childs > 0 then
|
|
core.pop_clip_rect()
|
|
end
|
|
|
|
self:draw_scrollbar()
|
|
|
|
return true
|
|
end
|
|
|
|
---Wrapper to easily show a message box.
|
|
---@param title string | widget.styledtext
|
|
---@param message string | widget.styledtext
|
|
---@param icon widget.messagebox.icontype
|
|
---@param icon_color? renderer.color
|
|
---@param on_close? widget.messagebox.onclosehandler
|
|
---@param buttons? widget.messagebox.buttonstype
|
|
function MessageBox.alert(title, message, icon, icon_color, on_close, buttons)
|
|
buttons = buttons or MessageBox.BUTTONS_OK
|
|
---@type widget.messagebox
|
|
local msgbox = MessageBox(nil, title, message, icon, icon_color)
|
|
if buttons == MessageBox.BUTTONS_OK_CANCEL then
|
|
msgbox:add_button("Ok")
|
|
msgbox:add_button("Cancel")
|
|
elseif buttons == MessageBox.BUTTONS_YES_NO then
|
|
msgbox:add_button("Yes")
|
|
msgbox:add_button("No")
|
|
elseif buttons == MessageBox.BUTTONS_YES_NO_CANCEL then
|
|
msgbox:add_button("Yes")
|
|
msgbox:add_button("No")
|
|
msgbox:add_button("Cancel")
|
|
else
|
|
msgbox:add_button("Ok")
|
|
end
|
|
|
|
local msgbox_on_close = msgbox.on_close
|
|
msgbox.on_close = function(self, button_id, button)
|
|
if on_close then
|
|
on_close(self, button_id, button)
|
|
end
|
|
msgbox_on_close(self, button_id, button)
|
|
self:destroy()
|
|
end
|
|
msgbox:show()
|
|
end
|
|
|
|
---Wrapper to easily show a info message box.
|
|
---@param title string | widget.styledtext
|
|
---@param message string | widget.styledtext
|
|
---@param on_close? widget.messagebox.onclosehandler
|
|
---@param buttons? widget.messagebox.buttonstype
|
|
function MessageBox.info(title, message, on_close, buttons)
|
|
MessageBox.alert(title, message, MessageBox.ICON_INFO, nil, on_close, buttons)
|
|
end
|
|
|
|
---Wrapper to easily show a warning message box.
|
|
---@param title string | widget.styledtext
|
|
---@param message string | widget.styledtext
|
|
---@param on_close? widget.messagebox.onclosehandler
|
|
---@param buttons? widget.messagebox.buttonstype
|
|
function MessageBox.warning(title, message, on_close, buttons)
|
|
MessageBox.alert(title, message, MessageBox.ICON_WARNING, nil, on_close, buttons)
|
|
end
|
|
|
|
---Wrapper to easily show an error message box.
|
|
---@param title string | widget.styledtext
|
|
---@param message string | widget.styledtext
|
|
---@param on_close? widget.messagebox.onclosehandler
|
|
---@param buttons? widget.messagebox.buttonstype
|
|
function MessageBox.error(title, message, on_close, buttons)
|
|
MessageBox.alert(title, message, MessageBox.ICON_ERROR, nil, on_close, buttons)
|
|
end
|
|
|
|
|
|
return MessageBox
|