local core = require "core" local command = require "core.command" local common = require "core.common" local style = require "core.style" local keymap = require "core.keymap" local Widget = require "widget" local Button = require "widget.button" local ListBox = require "widget.listbox" local Container = require "widget.container" ---@class core.emptyview : widget ---@field super widget local EmptyView = Widget:extend() function EmptyView:__tostring() return "EmptyView" end ---Font used to render the logo ---@type renderer.font local icon_huge_font ---Prevent the font getting scaled more than once from multiple instances ---@type boolean local icon_font_scaled = false ---Current font scale in case rescaling is required on EmptyView creation local icon_font_current_scale = SCALE local buttons = { { name = "new_file", icon = "f", cmd = "core:new-doc", label = "New File", tooltip = "Create a new file" }, { name = "open_file", icon = "D", cmd = "core:open-file", label = "Open File", tooltip = "Open an existing project file" }, { name = "open_folder", icon = "d", cmd = "core:open-project-folder", label = "Open Project", tooltip = "Open a project in another instance" }, { name = "change_folder", icon = "s", cmd = "core:change-project-folder", label = "Change Project", tooltip = "Change main project of current instance" }, { name = "find_file", icon = "L", cmd = "core:find-file", label = "Find File", tooltip = "Search for a file from current project" }, { name = "run_command", icon = "B", cmd = "core:find-command", label = "Run Command", tooltip = "Search for a command to run" }, { name = "settings", icon = "P", cmd = "ui:settings", label = "Settings", tooltip = "Open the settings interface" } } ---Constructor function EmptyView:new() EmptyView.super.new(self, nil, false) if not icon_huge_font or icon_font_current_scale ~= SCALE then icon_huge_font = style.icon_big_font:copy(110 * SCALE) end self.name = "Welcome" self.type_name = "core.emptyview" self.background_color = style.background self.border.width = 0 self.scrollable = true self.title = "Pragtical" self.version = "version " .. VERSION self.title_width = style.big_font:get_width(self.title) self.version_width = style.font:get_width(self.title) self.text_x = 0 self.text_y = 0 self.logo_width = 0 self.top_container = Container( self, Container.direction.HORIZONTAL, Container.alignment.CENTER ) for _, button in ipairs(buttons) do self[button.name] = Button(self.top_container, button.label) self[button.name]:set_icon(button.icon) self[button.name].border.width = 0 self[button.name]:set_tooltip(button.tooltip, button.cmd) self[button.name].on_click = function(_, pressed) if pressed == "left" then command.perform(button.cmd) end end end self.center_container = Container(self) self.website = Button(self.center_container, "Website") self.website:set_icon("G") self.website:set_tooltip("Visit the editor website") self.website.on_click = function(_, pressed) common.open_in_system("https://pragtical.dev") end self.docs = Button(self.center_container, "Documentation") self.docs:set_icon("?") self.docs:set_tooltip("Visit the editor documentation") self.docs.on_click = function(_, pressed) common.open_in_system("https://pragtical.dev/docs/intro") end self.first_draw = true self.plugin_manager_loaded = false self.plugins = Button(self.center_container, "Plugins") self.plugins:set_icon("p") self.plugins:set_tooltip("Open the plugin manager") self.plugins.on_click = function(_, pressed) command.perform("plugin-manager:show") end self.plugins:hide() self.recent_projects = ListBox(self) self.recent_projects:add_column("Recent Projects") self.recent_projects:hide() if core.recent_projects and type(core.recent_projects) == "table" then for _, path in ipairs(core.recent_projects) do self.recent_projects:add_row({common.home_encode(path)}, {path = path}) end end function self.recent_projects:on_mouse_pressed(button, x, y, clicks) self.super.on_mouse_pressed(self, button, x, y, clicks) local idx = self:get_selected() local data = self:get_row_data(idx) if clicks == 2 then core.open_project(data.path) end end self.prev_size = { x = self.size.x, y = self.size.y } self:show() self:update() end function EmptyView:on_scale_change(new_scale) if not icon_font_scaled then icon_font_scaled = true icon_huge_font = style.icon_big_font:copy(110 * new_scale) icon_font_scaled = false icon_font_current_scale = new_scale end end local function draw_text(self, x, y, calc_only) local th = style.big_font:get_height() local dh = 2 * th + style.padding.y * 2 local x1, y1 = x, y + (dh / 2) - (th - style.padding.y * 2) local xv = x1 if self.version_width > self.title_width then self.version = VERSION self.version_width = style.font:get_width(self.version) xv = x1 - (self.version_width - self.title_width) end if not calc_only then if self.logo_width < self.size.x then x = renderer.draw_text(style.big_font, self.title, x1, y1, style.dim) renderer.draw_text(style.font, self.version, xv, y1 + th, style.dim) x = x + style.padding.x renderer.draw_rect(x, y, math.ceil(1 * SCALE), dh, style.dim) x = x + style.padding.x renderer.draw_text(icon_huge_font, "5", x, y, style.background2) else x = x + (self.size.x / 2) - (icon_huge_font:get_width("9") / 2) - style.padding.x end renderer.draw_text(icon_huge_font, "5", x, y, style.background2) renderer.draw_text(icon_huge_font, "6", x, y, style.text) renderer.draw_text(icon_huge_font, "7", x, y, style.caret) renderer.draw_text(icon_huge_font, "8", x, y, common.lighten_color(style.dim, 25)) x = renderer.draw_text(icon_huge_font, "9", x, y, common.lighten_color(style.dim, 45)) else x, y = 0, 0 x = style.big_font:get_width(self.title) + (style.padding.x * 2) + icon_huge_font:get_width("9") end return x, dh end function EmptyView:draw() if not EmptyView.super.draw(self) then return end if self.first_draw then self.first_draw = false local plugin_manager_loaded = package.loaded["plugins.plugin_manager"] if plugin_manager_loaded then self.plugins:show() self.prev_size.x = -1 self.center_container.size.x = -1 end core.redraw = true end local _, oy = self:get_content_offset() draw_text(self, self.text_x, self.text_y + oy) self:draw_scrollbar() end function EmptyView:update() if not EmptyView.super.update(self) then return end self.background_color = style.background if self.prev_size.x ~= self.size.x or self.prev_size.y ~= self.size.y then self.recent_projects:update_position() self.top_container:set_position(0, 0) self.top_container:set_size(self:get_width()) self.top_container:update() self.title = "Pragtical" self.version = "version " .. VERSION self.title_width = style.big_font:get_width(self.title) self.version_width = style.font:get_width(self.version) -- calculate logo and version positioning local tw, th = draw_text(self, 0, 0, true) self.logo_width = tw local tx = self.position.x + math.max(style.padding.x, (self:get_width() - tw) / 2) local ty = self.position.y + (self.size.y - th) / 2 local items_h if #self.recent_projects.rows > 0 then self.recent_projects:set_size( self:get_width() - style.padding.x * 2, 200 * SCALE ) if not self.recent_projects:is_visible() then self.recent_projects:show() end items_h = th + self.top_container:get_height() + self.center_container:get_height() + self.recent_projects:get_height() + style.padding.y * 8 if items_h < self:get_height() then ty = ty - self.recent_projects:get_height() / 2 else ty = common.clamp(ty, self.top_container:get_bottom() + style.padding.y * 4, ty - self.recent_projects:get_height() ) end end if ty < self.top_container:get_bottom() + style.padding.y * 4 then ty = self.top_container:get_bottom() + style.padding.y * 4 end self.text_x = tx self.text_y = ty -- reposition web buttons and recent projects local web_buttons_y = ty + th + style.padding.y * 2 self.center_container:set_position(0, web_buttons_y) self.center_container:set_size(self:get_width()) self.center_container:update() if #self.recent_projects.rows > 0 then if items_h < self:get_height() then self.recent_projects:set_size( nil, self:get_height() - self.center_container:get_bottom() - style.padding.y * 8 ) end self.recent_projects:set_position( style.padding.x, self.center_container:get_bottom() + style.padding.y * 6 ) end self.prev_size.x = self.size.x self.prev_size.y = self.size.y end end return EmptyView