247 lines
7.0 KiB
Lua
Executable File
247 lines
7.0 KiB
Lua
Executable File
local core = require "core"
|
|
local command = require "core.command"
|
|
local common = require "core.common"
|
|
local config = require "core.config"
|
|
local View = require "core.view"
|
|
local style = require "core.style"
|
|
|
|
local BORDER_WIDTH = common.round(1 * SCALE)
|
|
local UNDERLINE_WIDTH = common.round(2 * SCALE)
|
|
local UNDERLINE_MARGIN = common.round(1 * SCALE)
|
|
|
|
local noop = function() end
|
|
|
|
local NagView = View:extend()
|
|
|
|
function NagView:new()
|
|
NagView.super.new(self)
|
|
self.size.y = 0
|
|
self.force_focus = false
|
|
self.queue = {}
|
|
end
|
|
|
|
function NagView:get_title()
|
|
return self.title
|
|
end
|
|
|
|
-- The two methods below are duplicated from DocView
|
|
function NagView:get_line_height()
|
|
return math.floor(style.font:get_height() * config.line_height)
|
|
end
|
|
|
|
function NagView:get_line_text_y_offset()
|
|
local lh = self:get_line_height()
|
|
local th = style.font:get_height()
|
|
return (lh - th) / 2
|
|
end
|
|
|
|
-- Buttons height without padding
|
|
function NagView:get_buttons_height()
|
|
local lh = style.font:get_height()
|
|
local bt_padding = lh / 2
|
|
return lh + 2 * BORDER_WIDTH + 2 * bt_padding
|
|
end
|
|
|
|
function NagView:get_target_height()
|
|
return self.target_height + 2 * style.padding.y
|
|
end
|
|
|
|
function NagView:update()
|
|
NagView.super.update(self)
|
|
|
|
if core.active_view == self and self.title then
|
|
self:move_towards(self.size, "y", self:get_target_height())
|
|
self:move_towards(self, "underline_progress", 1)
|
|
else
|
|
self:move_towards(self.size, "y", 0)
|
|
end
|
|
end
|
|
|
|
function NagView:draw_overlay()
|
|
local ox, oy = self:get_content_offset()
|
|
oy = oy + self.size.y
|
|
local w, h = core.root_view.size.x, core.root_view.size.y - oy
|
|
core.root_view:defer_draw(function()
|
|
renderer.draw_rect(ox, oy, w, h, style.nagbar_dim)
|
|
end)
|
|
end
|
|
|
|
function NagView:change_hovered(i)
|
|
if i ~= self.hovered_item then
|
|
self.hovered_item = i
|
|
self.underline_progress = 0
|
|
core.redraw = true
|
|
end
|
|
end
|
|
|
|
function NagView:each_option()
|
|
return coroutine.wrap(function()
|
|
if not self.options then return end
|
|
local opt, bw,bh,ox,oy
|
|
bh = self:get_buttons_height()
|
|
ox,oy = self:get_content_offset()
|
|
ox = ox + self.size.x
|
|
oy = oy + self.size.y - bh - style.padding.y
|
|
|
|
for i = #self.options, 1, -1 do
|
|
opt = self.options[i]
|
|
bw = opt.font:get_width(opt.text) + 2 * BORDER_WIDTH + style.padding.x
|
|
|
|
ox = ox - bw - style.padding.x
|
|
coroutine.yield(i, opt, ox,oy,bw,bh)
|
|
end
|
|
end)
|
|
end
|
|
|
|
function NagView:on_mouse_moved(mx, my, ...)
|
|
NagView.super.on_mouse_moved(self, mx, my, ...)
|
|
for i, _, x,y,w,h in self:each_option() do
|
|
if mx >= x and my >= y and mx < x + w and my < y + h then
|
|
self:change_hovered(i)
|
|
break
|
|
end
|
|
end
|
|
end
|
|
|
|
function NagView:on_mouse_pressed(button, mx, my, clicks)
|
|
if NagView.super.on_mouse_pressed(self, button, mx, my, clicks) then return end
|
|
for i, _, x,y,w,h in self:each_option() do
|
|
if mx >= x and my >= y and mx < x + w and my < y + h then
|
|
self:change_hovered(i)
|
|
command.perform "dialog:select"
|
|
break
|
|
end
|
|
end
|
|
end
|
|
|
|
function NagView:on_text_input(text)
|
|
if text:lower() == "y" then
|
|
command.perform "dialog:select-yes"
|
|
elseif text:lower() == "n" then
|
|
command.perform "dialog:select-no"
|
|
end
|
|
end
|
|
|
|
|
|
function NagView:draw()
|
|
if self.size.y <= 0 or not self.title then return end
|
|
|
|
self:draw_overlay()
|
|
self:draw_background(style.nagbar)
|
|
|
|
local ox, oy = self:get_content_offset()
|
|
ox = ox + style.padding.x
|
|
|
|
-- if there are other items, show it
|
|
if #self.queue > 0 then
|
|
local str = string.format("[%d]", #self.queue)
|
|
ox = common.draw_text(style.font, style.nagbar_text, str, "left", ox, oy, self.size.x, self.size.y)
|
|
ox = ox + style.padding.x
|
|
end
|
|
|
|
-- draw message
|
|
local lh = style.font:get_height() * config.line_height
|
|
oy = oy + style.padding.y + (self.target_height - self:get_message_height()) / 2
|
|
for msg_line in self.message:gmatch("(.-)\n") do
|
|
local ty = oy + self:get_line_text_y_offset()
|
|
renderer.draw_text(style.font, msg_line, ox, ty, style.nagbar_text)
|
|
oy = oy + lh
|
|
end
|
|
|
|
-- draw buttons
|
|
for i, opt, bx,by,bw,bh in self:each_option() do
|
|
local fw,fh = bw - 2 * BORDER_WIDTH, bh - 2 * BORDER_WIDTH
|
|
local fx,fy = bx + BORDER_WIDTH, by + BORDER_WIDTH
|
|
|
|
-- draw the button
|
|
renderer.draw_rect(bx,by,bw,bh, style.nagbar_text)
|
|
renderer.draw_rect(fx,fy,fw,fh, style.nagbar)
|
|
|
|
if i == self.hovered_item then -- draw underline
|
|
local uw = fw - 2 * UNDERLINE_MARGIN
|
|
local halfuw = uw / 2
|
|
local lx = fx + UNDERLINE_MARGIN + halfuw - (halfuw * self.underline_progress)
|
|
local ly = fy + fh - UNDERLINE_MARGIN - UNDERLINE_WIDTH
|
|
uw = uw * self.underline_progress
|
|
renderer.draw_rect(lx,ly,uw,UNDERLINE_WIDTH, style.nagbar_text)
|
|
end
|
|
|
|
common.draw_text(opt.font, style.nagbar_text, opt.text, "center", fx,fy,fw,fh)
|
|
end
|
|
end
|
|
|
|
local function findindex(tbl, prop)
|
|
for i, o in ipairs(tbl) do
|
|
if o[prop] then return i end
|
|
end
|
|
end
|
|
|
|
function NagView:get_message_height()
|
|
local h = 0
|
|
for str in string.gmatch(self.message, "(.-)\n") do
|
|
h = h + style.font:get_height() * config.line_height
|
|
end
|
|
return h
|
|
end
|
|
|
|
|
|
function NagView:next()
|
|
local opts = table.remove(self.queue, 1) or {}
|
|
self.title = opts.title
|
|
self.message = opts.message and opts.message .. "\n"
|
|
self.options = opts.options
|
|
self.on_selected = opts.on_selected
|
|
if self.message and self.options then
|
|
local message_height = self:get_message_height()
|
|
-- self.target_height is the nagview height needed to display the message and
|
|
-- the buttons, excluding the top and bottom padding space.
|
|
self.target_height = math.max(message_height, self:get_buttons_height())
|
|
self:change_hovered(findindex(self.options, "default_yes"))
|
|
end
|
|
self.force_focus = self.message ~= nil
|
|
core.set_active_view(self.message ~= nil and self or core.last_active_view)
|
|
end
|
|
|
|
function NagView:show(title, message, options, on_select)
|
|
local opts = {}
|
|
opts.title = assert(title, "No title")
|
|
opts.message = assert(message, "No message")
|
|
opts.options = assert(options, "No options")
|
|
opts.on_selected = on_select or noop
|
|
table.insert(self.queue, opts)
|
|
if #self.queue > 0 and not self.title then self:next() end
|
|
end
|
|
|
|
command.add(NagView, {
|
|
["dialog:previous-entry"] = function()
|
|
local v = core.active_view
|
|
local hover = v.hovered_item or 1
|
|
v:change_hovered(hover == 1 and #v.options or hover - 1)
|
|
end,
|
|
["dialog:next-entry"] = function()
|
|
local v = core.active_view
|
|
local hover = v.hovered_item or 1
|
|
v:change_hovered(hover == #v.options and 1 or hover + 1)
|
|
end,
|
|
["dialog:select-yes"] = function()
|
|
local v = core.active_view
|
|
if v ~= core.nag_view then return end
|
|
v:change_hovered(findindex(v.options, "default_yes"))
|
|
command.perform "dialog:select"
|
|
end,
|
|
["dialog:select-no"] = function()
|
|
local v = core.active_view
|
|
if v ~= core.nag_view then return end
|
|
v:change_hovered(findindex(v.options, "default_no"))
|
|
command.perform "dialog:select"
|
|
end,
|
|
["dialog:select"] = function()
|
|
local v = core.active_view
|
|
if v.hovered_item then
|
|
v.on_selected(v.options[v.hovered_item])
|
|
v:next()
|
|
end
|
|
end,
|
|
})
|
|
|
|
return NagView |