diff --git a/data/core/init.lua b/data/core/init.lua index 0732a3e8..1934c8a4 100644 --- a/data/core/init.lua +++ b/data/core/init.lua @@ -7,6 +7,7 @@ local keymap local RootView local StatusView local CommandView +local NagView local DocView local Doc @@ -350,6 +351,7 @@ function core.init() RootView = require "core.rootview" StatusView = require "core.statusview" CommandView = require "core.commandview" + NagView = require "core.nagview" DocView = require "core.docview" Doc = require "core.doc" @@ -418,9 +420,12 @@ function core.init() core.root_view = RootView() core.command_view = CommandView() core.status_view = StatusView() + core.nag_view = NagView() local cur_node = core.root_view.root_node cur_node.is_primary_node = true + cur_node:split("up", core.nag_view, {y = true}) + cur_node = cur_node.b cur_node = cur_node:split("down", core.command_view, {y = true}) cur_node = cur_node:split("down", core.status_view, {y = true}) @@ -449,7 +454,7 @@ function core.init() end -function core.confirm_close_all() +function core.confirm_close_all(close_fn, ...) local dirty_count = 0 local dirty_name for _, doc in ipairs(core.docs) do @@ -465,10 +470,17 @@ function core.confirm_close_all() else text = string.format("%d docs have unsaved changes. Quit anyway?", dirty_count) end - local confirm = system.show_confirm_dialog("Unsaved Changes", text) - if not confirm then return false end + local args = {...} + local opt = { + { font = style.font, text = "Yes" }, + { font = style.font, text = "No" } + } + core.nag_view:show("Unsaved Changes", text, opt, function(item) + if item.text == "Yes" then close_fn(table.unpack(args)) end + end) + else + close_fn(...) end - return true end local temp_uid = (system.get_time() * 1000) % 0xffffffff @@ -517,9 +529,7 @@ local function quit_with_function(quit_fn, force) save_session() quit_fn() else - if core.confirm_close_all() then - quit_with_function(quit_fn, true) - end + core.confirm_close_all(quit_with_function, quit_fn, true) end end @@ -595,6 +605,7 @@ end function core.set_active_view(view) assert(view, "Tried to set active view to nil") + if core.active_view and core.active_view.force_focus then return end if view ~= core.active_view then if view.doc and view.doc.filename then core.set_visited(view.doc.filename) diff --git a/data/core/nagview.lua b/data/core/nagview.lua new file mode 100644 index 00000000..69dd50f0 --- /dev/null +++ b/data/core/nagview.lua @@ -0,0 +1,149 @@ +local core = require "core" +local config = require "core.config" +local common = require "core.common" +local View = require "core.view" +local style = require "core.style" + +local BORDER_WIDTH = common.round(2 * SCALE) +local BORDER_PADDING = common.round(5 * 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.title = "Warning" + self.message = "" + self.options = {} + self.submit = noop +end + +function NagView:get_title() + return self.title +end + +function NagView:each_option() + return coroutine.wrap(function() + for i = #self.options, 1, -1 do + coroutine.yield(i, self.options[i]) + end + end) +end + +function NagView:get_options_height() + local max = 0 + for _, opt in ipairs(self.options) do + local lh = style.font:get_height(opt.text) + if lh > max then max = lh end + end + return max +end + +function NagView:get_line_height() + local maxlh = math.max(style.font:get_height(self.message), self:get_options_height()) + return 2 * BORDER_WIDTH + 2 * BORDER_PADDING + maxlh + 2 * style.padding.y +end + +function NagView:update() + NagView.super.update(self) + + local dest = core.active_view == self and self:get_line_height() or 0 + self:move_towards(self.size, "y", dest) +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:each_visible_option() + return coroutine.wrap(function() + local halfh = math.floor(self.size.y / 2) + local ox, oy = self:get_content_offset() + ox = ox + self.size.x - style.padding.x + + for i, opt in self:each_option() do + local lw, lh = opt.font:get_width(opt.text), opt.font:get_height(opt.text) + local bw, bh = (lw + 2 * BORDER_WIDTH + 2 * BORDER_PADDING), (lh + 2 * BORDER_WIDTH + 2 * BORDER_PADDING) + local halfbh = math.floor(bh / 2) + local bx, by = math.max(0, ox - bw), math.max(0, oy + halfh - halfbh) + local fw, fh = bw - 2 * BORDER_WIDTH, bh - 2 * BORDER_WIDTH + local fx, fy = bx + BORDER_WIDTH, by + BORDER_WIDTH + coroutine.yield(i, opt, bx,by,bw,bh, fx,fy,fw,fh) + ox = ox - bw - style.padding.x + end + end) +end + +function NagView:on_mouse_moved(mx, my, ...) + NagView.super.on_mouse_moved(self, mx, my, ...) + local selected = false + for i, _, x,y,w,h in self:each_visible_option() do + if mx >= x and my >= y and mx < x + w and my < y + h then + self.selected = i + selected = true + break + end + end + + if not selected then self.selected = nil end +end + +function NagView:on_mouse_pressed(...) + if not NagView.super.on_mouse_pressed(self, ...) and self.selected then + self.force_focus = false + core.set_active_view(core.last_active_view) + self.submit(self.options[self.selected]) + end +end + +function NagView:draw() + if self.size.y <= 0 then return end + + self:draw_overlay() + self:draw_background(style.nagbar) + + -- draw message + local ox, oy = self:get_content_offset() + common.draw_text(style.font, style.nagbar_text, self.message, "left", ox + style.padding.x, oy, self.size.x, self.size.y) + + -- draw buttons + for i, opt, bx,by,bw,bh, fx,fy,fw,fh in self:each_visible_option() do + local fill = i == self.selected and style.nagbar_text or style.nagbar + local text_color = i == self.selected and style.nagbar or style.nagbar_text + + renderer.draw_rect(bx,by,bw,bh, style.nagbar_text) + + if i ~= self.selected then + renderer.draw_rect(fx,fy,fw,fh, fill) + end + + common.draw_text(opt.font, text_color, opt.text, "center", fx,fy,fw,fh) + end +end + +function NagView:show(title, message, options, on_select, on_cancel) + self.title = title or "Warning" + self.message = message or "Empty?" + self.options = options or {} + if on_cancel then table.insert(options, { key = "cancel", font = style.icon_font, text = "C" }) end + self.force_focus = true + self.submit = function(item) + self.submit = noop -- reset the submit function + if item.key == "cancel" and on_cancel then + on_cancel() + elseif on_select then + on_select(item) + end + end + core.set_active_view(self) +end + +return NagView \ No newline at end of file diff --git a/data/core/rootview.lua b/data/core/rootview.lua index 7650912e..3de9feb8 100644 --- a/data/core/rootview.lua +++ b/data/core/rootview.lua @@ -5,6 +5,7 @@ local keymap = require "core.keymap" local Object = require "core.object" local View = require "core.view" local CommandView = require "core.commandview" +local NagView = require "core.nagview" local DocView = require "core.docview" @@ -622,6 +623,12 @@ end function RootView:on_mouse_moved(x, y, dx, dy) + if core.active_view == core.nag_view then + system.set_cursor("arrow") + core.active_view:on_mouse_moved(x, y, dx, dy) + return + end + if self.dragged_divider then local node = self.dragged_divider if node.type == "hsplit" then diff --git a/data/core/style.lua b/data/core/style.lua index 02dd5e79..ab72bde5 100644 --- a/data/core/style.lua +++ b/data/core/style.lua @@ -41,6 +41,9 @@ style.line_number2 = { common.color "#83838f" } style.line_highlight = { common.color "#343438" } style.scrollbar = { common.color "#414146" } style.scrollbar2 = { common.color "#4b4b52" } +style.nagbar = { common.color "#FF0000" } +style.nagbar_text = { common.color "#FFFFFF" } +style.nagbar_dim = { common.color "rgba(0, 0, 0, 0.45)" } style.syntax = {} style.syntax["normal"] = { common.color "#e1e1e6" }