StatusView v2.0, some changes include:
* Items are now objects that can be retrieved and manipulated. * clip rect is used for left and right panes * initial support for items to do their own custom drawing by also doing a clip rect for them * a custom background color can be specified for the item. * a command or function can be executed on item click. * Introduced functionality to easily hide or show all or specific items. * Better handling of deprecated get_items() * Spacing is automatically added to items and cleaned on deprecated items. * Default items where separated and given the names: doc:file, doc:position, doc:indentation, doc:lines, doc:line-ending, core.commandview. * Some default right or left click actions where given to the default items. * Started adding required bits to support dragging to left and right panes when some items aren't visible. Note: should work well enough already but maybe some repetitive stuff can be cleaned out.
This commit is contained in:
parent
7cb4e93d14
commit
90983b22a4
|
@ -9,33 +9,28 @@ local LogView = require "core.logview"
|
||||||
local View = require "core.view"
|
local View = require "core.view"
|
||||||
local Object = require "core.object"
|
local Object = require "core.object"
|
||||||
|
|
||||||
|
|
||||||
---@alias StatusView.styledtext table<integer, renderer.font|renderer.color|string>
|
---@alias StatusView.styledtext table<integer, renderer.font|renderer.color|string>
|
||||||
---@alias StatusView.itemscb fun():StatusView.styledtext,StatusView.styledtext
|
|
||||||
---@alias StatusView.clickcb fun(button: string, x: number, y: number)
|
|
||||||
---@alias StatusView.predicate fun():boolean
|
|
||||||
|
|
||||||
---A status bar implementation for lite, check core.status_view.
|
---A status bar implementation for lite, check core.status_view.
|
||||||
---@class StatusView : View
|
---@class StatusView : View
|
||||||
---@field private items StatusView.item[]
|
---@field private items StatusView.Item[]
|
||||||
---@field private hovered_item StatusView.item
|
---@field private active_items StatusView.Item[]
|
||||||
|
---@field private hovered_item StatusView.Item
|
||||||
---@field private message_timeout number
|
---@field private message_timeout number
|
||||||
---@field private message StatusView.styledtext
|
---@field private message StatusView.styledtext
|
||||||
---@field private tooltip_mode boolean
|
---@field private tooltip_mode boolean
|
||||||
---@field private tooltip StatusView.styledtext
|
---@field private tooltip StatusView.styledtext
|
||||||
|
---@field private left_width number
|
||||||
|
---@field private right_width number
|
||||||
|
---@field private r_left_width number
|
||||||
|
---@field private r_right_width number
|
||||||
|
---@field private left_xoffset number
|
||||||
|
---@field private right_xoffset number
|
||||||
|
---@field private dragged_panel '"left"' | '"right"'
|
||||||
|
---@field private hide_messages boolean
|
||||||
local StatusView = View:extend()
|
local StatusView = View:extend()
|
||||||
|
|
||||||
---@class StatusView.item
|
|
||||||
---@field predicate StatusView.predicate
|
|
||||||
---@field items StatusView.itemscb
|
|
||||||
---@field onclick StatusView.clickcb
|
|
||||||
---@field tooltip string | nil
|
|
||||||
---@field lx number
|
|
||||||
---@field lw number
|
|
||||||
---@field rx number
|
|
||||||
---@field rw number
|
|
||||||
---@field active boolean
|
|
||||||
StatusView.item = {}
|
|
||||||
|
|
||||||
---Space separator
|
---Space separator
|
||||||
---@type string
|
---@type string
|
||||||
StatusView.separator = " "
|
StatusView.separator = " "
|
||||||
|
@ -44,6 +39,107 @@ StatusView.separator = " "
|
||||||
---@type string
|
---@type string
|
||||||
StatusView.separator2 = " | "
|
StatusView.separator2 = " | "
|
||||||
|
|
||||||
|
---@alias StatusView.Item.separator
|
||||||
|
---|>'StatusView.separator' # Space separator
|
||||||
|
---| 'StatusView.separator2' # Pipe separator
|
||||||
|
|
||||||
|
---@alias StatusView.Item.predicate fun():boolean
|
||||||
|
---@alias StatusView.Item.onclick fun(button: string, x: number, y: number)
|
||||||
|
---@alias StatusView.Item.getitem fun():StatusView.styledtext,StatusView.styledtext
|
||||||
|
---@alias StatusView.Item.ondraw fun(x, y, h, calc_only: boolean):number
|
||||||
|
|
||||||
|
---@class StatusView.Item : Object
|
||||||
|
---@field name string
|
||||||
|
---@field predicate StatusView.Item.predicate
|
||||||
|
---@field alignment StatusView.Item.alignment
|
||||||
|
---@field tooltip string | nil
|
||||||
|
---@field command string | nil @Command to perform when the item is clicked.
|
||||||
|
---@field on_click StatusView.Item.onclick | nil @Function called when item is clicked and no command is set.
|
||||||
|
---@field on_draw StatusView.Item.ondraw | nil @Custom drawing that when passed calc true should return the needed width for drawing and when false should draw.
|
||||||
|
---@field background_color renderer.color | nil
|
||||||
|
---@field visible boolean
|
||||||
|
---@field separator StatusView.Item.separator
|
||||||
|
---@field private active boolean
|
||||||
|
---@field private x number
|
||||||
|
---@field private w number
|
||||||
|
---@field private deprecated boolean
|
||||||
|
---@field private cached_item StatusView.styledtext
|
||||||
|
StatusView.Item = Object:extend()
|
||||||
|
|
||||||
|
---Flag to tell the item should me aligned on left side of status bar.
|
||||||
|
---@type number
|
||||||
|
StatusView.Item.LEFT = 1
|
||||||
|
|
||||||
|
---Flag to tell the item should me aligned on right side of status bar.
|
||||||
|
---@type number
|
||||||
|
StatusView.Item.RIGHT = 2
|
||||||
|
|
||||||
|
---@alias StatusView.Item.alignment
|
||||||
|
---|>'StatusView.Item.LEFT'
|
||||||
|
---| 'StatusView.Item.RIGHT'
|
||||||
|
|
||||||
|
---Constructor
|
||||||
|
---@param predicate string | table | StatusView.Item.predicate
|
||||||
|
---@param name string
|
||||||
|
---@param alignment StatusView.Item.alignment
|
||||||
|
---@param command string | StatusView.Item.onclick
|
||||||
|
---@param tooltip? string | nil
|
||||||
|
function StatusView.Item:new(predicate, name, alignment, command, tooltip)
|
||||||
|
self:set_predicate(predicate)
|
||||||
|
self.name = name
|
||||||
|
self.alignment = alignment or StatusView.Item.LEFT
|
||||||
|
self.command = type(command) == "string" and command or nil
|
||||||
|
self.tooltip = tooltip or ""
|
||||||
|
self.on_click = type(command) == "function" and command or nil
|
||||||
|
self.on_draw = nil
|
||||||
|
self.background_color = nil
|
||||||
|
self.visible = true
|
||||||
|
self.active = false
|
||||||
|
self.x = 0
|
||||||
|
self.w = 0
|
||||||
|
self.separator = StatusView.separator
|
||||||
|
end
|
||||||
|
|
||||||
|
---Called by the status bar each time that the item needs to be rendered,
|
||||||
|
---if on_draw() is set this function is obviated.
|
||||||
|
---@return StatusView.styledtext
|
||||||
|
function StatusView.Item:get_item() return {} end
|
||||||
|
|
||||||
|
---Do not show the item on the status bar.
|
||||||
|
function StatusView.Item:hide() self.visible = false end
|
||||||
|
|
||||||
|
---Show the item on the status bar.
|
||||||
|
function StatusView.Item:show() self.visible = true end
|
||||||
|
|
||||||
|
---Function assiged by default when user provides a nil predicate.
|
||||||
|
local function predicate_always_true() return true end
|
||||||
|
|
||||||
|
---A condition to evaluate if the item should be displayed. If a string
|
||||||
|
---is given it is treated as a require import that should return a valid object
|
||||||
|
---which is checked against the current active view, the sames applies if a
|
||||||
|
---table is given. A function that returns a boolean can be used instead to
|
||||||
|
---perform a custom evaluation, setting to nil means always evaluates to true.
|
||||||
|
---@param predicate string | table | StatusView.Item.predicate
|
||||||
|
function StatusView.Item:set_predicate(predicate)
|
||||||
|
predicate = predicate or predicate_always_true
|
||||||
|
if type(predicate) == "string" then
|
||||||
|
predicate = require(predicate)
|
||||||
|
end
|
||||||
|
if type(predicate) == "table" then
|
||||||
|
local class = predicate
|
||||||
|
predicate = function() return core.active_view:is(class) end
|
||||||
|
end
|
||||||
|
self.predicate = predicate
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
---Predicated used on the default docview widgets.
|
||||||
|
---@return boolean
|
||||||
|
local function predicate_docview()
|
||||||
|
return core.active_view:is(DocView)
|
||||||
|
and not core.active_view:is(CommandView)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
---Constructor
|
---Constructor
|
||||||
function StatusView:new()
|
function StatusView:new()
|
||||||
|
@ -53,57 +149,228 @@ function StatusView:new()
|
||||||
self.tooltip_mode = false
|
self.tooltip_mode = false
|
||||||
self.tooltip = {}
|
self.tooltip = {}
|
||||||
self.items = {}
|
self.items = {}
|
||||||
|
self.active_items = {}
|
||||||
self.hovered_item = {}
|
self.hovered_item = {}
|
||||||
self.pointer = {x = 0, y = 0}
|
self.pointer = {x = 0, y = 0}
|
||||||
|
self.left_width = 0
|
||||||
|
self.right_width = 0
|
||||||
|
self.r_left_width = 0
|
||||||
|
self.r_right_width = 0
|
||||||
|
self.left_xoffset = 0
|
||||||
|
self.right_xoffset = 0
|
||||||
|
self.dragged_panel = ""
|
||||||
|
self.hide_messages = false
|
||||||
|
|
||||||
|
self:register_docview_items()
|
||||||
|
self:register_command_items()
|
||||||
|
end
|
||||||
|
|
||||||
|
---The predefined status bar items displayed when a document view is active.
|
||||||
|
function StatusView:register_docview_items()
|
||||||
|
if self:get_item("doc:file") then return end
|
||||||
|
|
||||||
self:add_item(
|
self:add_item(
|
||||||
|
predicate_docview,
|
||||||
|
"doc:file",
|
||||||
|
StatusView.Item.LEFT,
|
||||||
function()
|
function()
|
||||||
return core.active_view:is(DocView) and
|
local dv = core.active_view
|
||||||
not core.active_view:is(CommandView)
|
return {
|
||||||
end,
|
dv.doc:is_dirty() and style.accent or style.text, style.icon_font, "f",
|
||||||
self.get_doc_items
|
style.dim, style.font, self.separator2, style.text,
|
||||||
|
dv.doc.filename and style.text or style.dim, dv.doc:get_name()
|
||||||
|
}
|
||||||
|
end
|
||||||
)
|
)
|
||||||
|
|
||||||
self:add_item("core.commandview", self.get_command_items)
|
self:add_item(
|
||||||
|
predicate_docview,
|
||||||
|
"doc:position",
|
||||||
|
StatusView.Item.LEFT,
|
||||||
|
function()
|
||||||
|
local dv = core.active_view
|
||||||
|
local line, col = dv.doc:get_selection()
|
||||||
|
return {
|
||||||
|
line,
|
||||||
|
":",
|
||||||
|
col > config.line_limit and style.accent or style.text, col,
|
||||||
|
style.text,
|
||||||
|
self.separator,
|
||||||
|
string.format("%.f%%", line / #dv.doc.lines * 100)
|
||||||
|
}
|
||||||
|
end,
|
||||||
|
"doc:go-to-line"
|
||||||
|
).tooltip = "line : column"
|
||||||
|
|
||||||
|
self:add_item(
|
||||||
|
predicate_docview,
|
||||||
|
"doc:indentation",
|
||||||
|
StatusView.Item.RIGHT,
|
||||||
|
function()
|
||||||
|
local dv = core.active_view
|
||||||
|
local indent_type, indent_size, indent_confirmed = dv.doc:get_indent_info()
|
||||||
|
local indent_label = (indent_type == "hard") and "tabs: " or "spaces: "
|
||||||
|
return {
|
||||||
|
style.text, indent_label, indent_size
|
||||||
|
}
|
||||||
|
end,
|
||||||
|
function(button, x, y)
|
||||||
|
if button == "left" then
|
||||||
|
command.perform "indent:set-file-indent-size"
|
||||||
|
elseif button == "right" then
|
||||||
|
command.perform "indent:set-file-indent-type"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
).separator = self.separator2
|
||||||
|
|
||||||
|
self:add_item(
|
||||||
|
predicate_docview,
|
||||||
|
"doc:lines",
|
||||||
|
StatusView.Item.RIGHT,
|
||||||
|
function()
|
||||||
|
local dv = core.active_view
|
||||||
|
return {
|
||||||
|
style.text,
|
||||||
|
style.icon_font, "g",
|
||||||
|
style.font, style.dim, self.separator2, style.text,
|
||||||
|
#dv.doc.lines, " lines",
|
||||||
|
}
|
||||||
|
end
|
||||||
|
).separator = self.separator2
|
||||||
|
|
||||||
|
self:add_item(
|
||||||
|
predicate_docview,
|
||||||
|
"doc:line-ending",
|
||||||
|
StatusView.Item.RIGHT,
|
||||||
|
function()
|
||||||
|
local dv = core.active_view
|
||||||
|
return {
|
||||||
|
dv.doc.crlf and "CRLF" or "LF"
|
||||||
|
}
|
||||||
|
end,
|
||||||
|
"doc:toggle-line-ending"
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
---The predefined status bar items displayed when a command view is active.
|
||||||
|
function StatusView:register_command_items()
|
||||||
|
if self:get_item("command:files") then return end
|
||||||
|
|
||||||
|
self:add_item(
|
||||||
|
"core.commandview",
|
||||||
|
"command:files",
|
||||||
|
StatusView.Item.RIGHT,
|
||||||
|
function()
|
||||||
|
return {
|
||||||
|
style.icon_font, "g",
|
||||||
|
style.font, style.dim, self.separator2,
|
||||||
|
#core.docs, style.text, " / ",
|
||||||
|
#core.project_files, " files"
|
||||||
|
}
|
||||||
|
end
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
---Adds an item to be rendered in the status bar.
|
---Adds an item to be rendered in the status bar.
|
||||||
---@param predicate string | table | StatusView.predicate :
|
---@param predicate string | table | StatusView.Item.predicate :
|
||||||
---A coindition to evaluate if the item should be displayed. If a string
|
---A condition to evaluate if the item should be displayed. If a string
|
||||||
---is given it is treated as a file that returns a valid object which is
|
---is given it is treated as a require import that should return a valid object
|
||||||
---checked against the current active view, the sames applies if a table is
|
---which is checked against the current active view, the sames applies if a
|
||||||
---given. A function can be used instead to perform a custom evaluation.
|
---table is given. A function that returns a boolean can be used instead to
|
||||||
---@param itemscb StatusView.itemscb :
|
---perform a custom evaluation, setting to nil means always evaluates to true.
|
||||||
---This function should return two tables of StatusView.styledtext elements
|
---@param name string A unique name to identify the item on the status bar.
|
||||||
---for both left and right, empty tables are allowed.
|
---@param alignment StatusView.Item.alignment
|
||||||
|
---@param getitem StatusView.Item.getitem :
|
||||||
|
---A function that should return a StatusView.styledtext element,
|
||||||
|
---returning empty table is allowed.
|
||||||
|
---@param command? string | StatusView.Item.onclick :
|
||||||
|
---The name of a valid registered command or a callback function to execute
|
||||||
|
---when the item is clicked.
|
||||||
---@param pos? integer :
|
---@param pos? integer :
|
||||||
---The position in which to insert the given item on the internal table,
|
---The position in which to insert the given item on the internal table,
|
||||||
---a value of -1 inserts the item at the end which is the default. A value
|
---a value of -1 inserts the item at the end which is the default. A value
|
||||||
---of 1 will insert the item at the beggining.
|
---of 1 will insert the item at the beggining.
|
||||||
---@param onclick? StatusView.clickcb Executed when user clicks the item
|
|
||||||
---@param tooltip? string Displayed when mouse hovers the item
|
---@param tooltip? string Displayed when mouse hovers the item
|
||||||
function StatusView:add_item(predicate, itemscb, pos, onclick, tooltip)
|
---@return StatusView.Item
|
||||||
predicate = predicate or always_true
|
function StatusView:add_item(predicate, name, alignment, getitem, command, pos, tooltip)
|
||||||
if type(predicate) == "string" then
|
assert(self:get_item(name) == nil, "status item already exists: " .. name)
|
||||||
predicate = require(predicate)
|
---@type StatusView.Item
|
||||||
end
|
local item = StatusView.Item(predicate, name, alignment, command, tooltip)
|
||||||
if type(predicate) == "table" then
|
item.get_item = getitem
|
||||||
local class = predicate
|
pos = type(pos) == "nil" and -1 or tonumber(pos)
|
||||||
predicate = function() return core.active_view:is(class) end
|
|
||||||
end
|
|
||||||
---@type StatusView.item
|
|
||||||
local item = {
|
|
||||||
predicate = predicate,
|
|
||||||
items = itemscb,
|
|
||||||
onclick = onclick,
|
|
||||||
tooltip = tooltip
|
|
||||||
}
|
|
||||||
pos = type(pos) == "nil" and -1 or math.abs(tonumber(pos))
|
|
||||||
if pos == -1 then
|
if pos == -1 then
|
||||||
table.insert(self.items, item)
|
table.insert(self.items, item)
|
||||||
else
|
else
|
||||||
table.insert(self.items, pos, item)
|
table.insert(self.items, math.abs(pos), item)
|
||||||
|
end
|
||||||
|
return item
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
---Get an item associated object or nil if not found.
|
||||||
|
---@param name string
|
||||||
|
---@return StatusView.Item | nil
|
||||||
|
function StatusView:get_item(name)
|
||||||
|
for _, item in ipairs(self.items) do
|
||||||
|
if item.name == name then return item end
|
||||||
|
end
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
---Get a list of items.
|
||||||
|
---@param alignment? StatusView.Item.alignment
|
||||||
|
---@return StatusView.Item[]
|
||||||
|
function StatusView:get_items_list(alignment)
|
||||||
|
if alignment then
|
||||||
|
local items = {}
|
||||||
|
for _, item in ipairs(self.items) do
|
||||||
|
if item.alignment == alignment then
|
||||||
|
table.insert(items, item)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return items
|
||||||
|
end
|
||||||
|
return self.items
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
---Hides the given items from the status view or all if no names given.
|
||||||
|
---@param names table<integer, string> | string | nil
|
||||||
|
function StatusView:hide_items(names)
|
||||||
|
if type(names) == "string" then
|
||||||
|
names = {names}
|
||||||
|
end
|
||||||
|
if not names then
|
||||||
|
for _, item in ipairs(self.items) do
|
||||||
|
item:hide()
|
||||||
|
end
|
||||||
|
return
|
||||||
|
end
|
||||||
|
for _, name in ipairs(names) do
|
||||||
|
local item = self:get_item(name)
|
||||||
|
if item then item:hide() end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
---Shows the given items from the status view or all if no names given.
|
||||||
|
---@param names table<integer, string> | string | nil
|
||||||
|
function StatusView:show_items(names)
|
||||||
|
if type(names) == "string" then
|
||||||
|
names = {names}
|
||||||
|
end
|
||||||
|
if not names then
|
||||||
|
for _, item in ipairs(self.items) do
|
||||||
|
item:show()
|
||||||
|
end
|
||||||
|
return
|
||||||
|
end
|
||||||
|
for _, name in ipairs(names) do
|
||||||
|
local item = self:get_item(name)
|
||||||
|
if item then item:show() end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -113,6 +380,7 @@ end
|
||||||
---@param icon_color renderer.color
|
---@param icon_color renderer.color
|
||||||
---@param text string
|
---@param text string
|
||||||
function StatusView:show_message(icon, icon_color, text)
|
function StatusView:show_message(icon, icon_color, text)
|
||||||
|
if self.hide_messages then return end
|
||||||
self.message = {
|
self.message = {
|
||||||
icon_color, style.icon_font, icon,
|
icon_color, style.icon_font, icon,
|
||||||
style.dim, style.font, StatusView.separator2, style.text, text
|
style.dim, style.font, StatusView.separator2, style.text, text
|
||||||
|
@ -121,6 +389,13 @@ function StatusView:show_message(icon, icon_color, text)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
---Enable or disable system wide messages on the status bar.
|
||||||
|
---@param enable boolean
|
||||||
|
function StatusView:display_messages(enable)
|
||||||
|
self.hide_messages = not enable
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
---Activates tooltip mode displaying only the given
|
---Activates tooltip mode displaying only the given
|
||||||
---text until StatusView:remove_tooltip() is called.
|
---text until StatusView:remove_tooltip() is called.
|
||||||
---@param text string | StatusView.styledtext
|
---@param text string | StatusView.styledtext
|
||||||
|
@ -174,8 +449,9 @@ end
|
||||||
---@param items StatusView.styledtext
|
---@param items StatusView.styledtext
|
||||||
---@param right_align boolean
|
---@param right_align boolean
|
||||||
---@param yoffset number
|
---@param yoffset number
|
||||||
function StatusView:draw_items(items, right_align, yoffset)
|
function StatusView:draw_items(items, right_align, xoffset, yoffset)
|
||||||
local x, y = self:get_content_offset()
|
local x, y = self:get_content_offset()
|
||||||
|
x = x + (xoffset or 0)
|
||||||
y = y + (yoffset or 0)
|
y = y + (yoffset or 0)
|
||||||
if right_align then
|
if right_align then
|
||||||
local w = draw_items(self, items, 0, 0, text_width)
|
local w = draw_items(self, items, 0, 0, text_width)
|
||||||
|
@ -189,7 +465,7 @@ end
|
||||||
|
|
||||||
|
|
||||||
---Draw the tooltip of a given status bar item.
|
---Draw the tooltip of a given status bar item.
|
||||||
---@param item StatusView.item
|
---@param item StatusView.Item
|
||||||
function StatusView:draw_item_tooltip(item)
|
function StatusView:draw_item_tooltip(item)
|
||||||
core.root_view:defer_draw(function()
|
core.root_view:defer_draw(function()
|
||||||
local text = item.tooltip
|
local text = item.tooltip
|
||||||
|
@ -203,7 +479,7 @@ function StatusView:draw_item_tooltip(item)
|
||||||
end
|
end
|
||||||
|
|
||||||
renderer.draw_rect(
|
renderer.draw_rect(
|
||||||
x,
|
x + style.padding.x,
|
||||||
self.position.y - h - (style.padding.y * 2),
|
self.position.y - h - (style.padding.y * 2),
|
||||||
w + (style.padding.x * 2),
|
w + (style.padding.x * 2),
|
||||||
h + (style.padding.y * 2),
|
h + (style.padding.y * 2),
|
||||||
|
@ -213,7 +489,7 @@ function StatusView:draw_item_tooltip(item)
|
||||||
renderer.draw_text(
|
renderer.draw_text(
|
||||||
style.font,
|
style.font,
|
||||||
text,
|
text,
|
||||||
x + style.padding.x,
|
x + (style.padding.x * 2),
|
||||||
self.position.y - h - style.padding.y,
|
self.position.y - h - style.padding.y,
|
||||||
style.text
|
style.text
|
||||||
)
|
)
|
||||||
|
@ -221,141 +497,6 @@ function StatusView:draw_item_tooltip(item)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
---The predefined status bar items displayed when a document view is active.
|
|
||||||
---@return table left
|
|
||||||
---@return table right
|
|
||||||
function StatusView:get_doc_items()
|
|
||||||
local dv = core.active_view
|
|
||||||
local line, col = dv.doc:get_selection()
|
|
||||||
local dirty = dv.doc:is_dirty()
|
|
||||||
local indent_type, indent_size, indent_confirmed = dv.doc:get_indent_info()
|
|
||||||
local indent_label = (indent_type == "hard") and "tabs: " or "spaces: "
|
|
||||||
local indent_size_str = tostring(indent_size) .. (indent_confirmed and "" or "*") or "unknown"
|
|
||||||
|
|
||||||
return {
|
|
||||||
dirty and style.accent or style.text, style.icon_font, "f",
|
|
||||||
style.dim, style.font, self.separator2, style.text,
|
|
||||||
dv.doc.filename and style.text or style.dim, dv.doc:get_name(),
|
|
||||||
style.text,
|
|
||||||
self.separator,
|
|
||||||
"line: ", line,
|
|
||||||
self.separator,
|
|
||||||
col > config.line_limit and style.accent or style.text, "col: ", col,
|
|
||||||
style.text,
|
|
||||||
self.separator,
|
|
||||||
string.format("%.f%%", line / #dv.doc.lines * 100),
|
|
||||||
}, {
|
|
||||||
style.text, indent_label, indent_size,
|
|
||||||
style.dim, self.separator2, style.text,
|
|
||||||
style.icon_font, "g",
|
|
||||||
style.font, style.dim, self.separator2, style.text,
|
|
||||||
#dv.doc.lines, " lines",
|
|
||||||
self.separator,
|
|
||||||
dv.doc.crlf and "CRLF" or "LF"
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
---The predefined status bar items displayed when a command view is active.
|
|
||||||
---@return table left
|
|
||||||
---@return table right
|
|
||||||
function StatusView:get_command_items()
|
|
||||||
return {}, {
|
|
||||||
style.icon_font, "g",
|
|
||||||
style.font, style.dim, self.separator2,
|
|
||||||
#core.docs, style.text, " / ",
|
|
||||||
#core.project_files, " files"
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
---Helper function to copy a styled text table into another.
|
|
||||||
---@param t1 StatusView.styledtext
|
|
||||||
---@param t2 StatusView.styledtext
|
|
||||||
local function table_add(t1, t2)
|
|
||||||
for i, value in ipairs(t2) do
|
|
||||||
table.insert(t1, value)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
---Get the styled text that will be displayed on the left side or
|
|
||||||
---right side of the status bar checking their predicates and performing
|
|
||||||
---positioning calculations for proper functioning of tooltips and clicks.
|
|
||||||
---@return StatusView.styledtext left
|
|
||||||
---@return StatusView.styledtext right
|
|
||||||
function StatusView:get_items_from_list()
|
|
||||||
local left, right = {}, {}
|
|
||||||
|
|
||||||
local x = self:get_content_offset()
|
|
||||||
|
|
||||||
local rx = x + self.size.x
|
|
||||||
local lx = x + style.padding.x
|
|
||||||
local rw, lw = 0, 0
|
|
||||||
|
|
||||||
---@class items
|
|
||||||
---@field item StatusView.item
|
|
||||||
local items = {}
|
|
||||||
|
|
||||||
-- calculate left and right width
|
|
||||||
for _, item in ipairs(self.items) do
|
|
||||||
if item.predicate(self) then
|
|
||||||
item.active = true
|
|
||||||
|
|
||||||
local litems, ritems = item.items(self)
|
|
||||||
|
|
||||||
if #litems > 0 then
|
|
||||||
item.lw = draw_items(self, litems, 0, 0, text_width)
|
|
||||||
item.lx = lx
|
|
||||||
lw = lw + item.lw
|
|
||||||
lx = lx + item.lw
|
|
||||||
end
|
|
||||||
|
|
||||||
if #ritems > 0 then
|
|
||||||
item.rw = draw_items(self, ritems, 0, 0, text_width)
|
|
||||||
item.rx = rx
|
|
||||||
rw = rw + item.rw
|
|
||||||
rx = rx + item.rw
|
|
||||||
end
|
|
||||||
|
|
||||||
if #litems > 0 or #ritems > 0 then
|
|
||||||
table.insert(items, {
|
|
||||||
item = item,
|
|
||||||
left = litems,
|
|
||||||
right = ritems
|
|
||||||
})
|
|
||||||
end
|
|
||||||
else
|
|
||||||
item.active = false
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- load deprecated items for compatibility
|
|
||||||
local dleft, dright = self:get_items(true)
|
|
||||||
if #dright > 0 then
|
|
||||||
rw = rw + draw_items(self, dright, 0, 0, text_width)
|
|
||||||
end
|
|
||||||
|
|
||||||
rw = rw < (self.size.x / 2) and rw or self.size.x / 2
|
|
||||||
|
|
||||||
for _, item in ipairs(items) do
|
|
||||||
if #item.left > 0 then
|
|
||||||
table_add(left, item.left)
|
|
||||||
end
|
|
||||||
if #item.right > 0 then
|
|
||||||
-- re-calculate x position now that we have the total width
|
|
||||||
item.item.rx = item.item.rx - rw - style.padding.x
|
|
||||||
table_add(right, item.right)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
table_add(left, dleft)
|
|
||||||
table_add(right, dright)
|
|
||||||
|
|
||||||
return left, right
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
---Older method of retrieving the status bar items and which is now
|
---Older method of retrieving the status bar items and which is now
|
||||||
---deprecated in favour of core.status_view:add_item().
|
---deprecated in favour of core.status_view:add_item().
|
||||||
---@deprecated
|
---@deprecated
|
||||||
|
@ -370,7 +511,210 @@ function StatusView:get_items(nowarn)
|
||||||
)
|
)
|
||||||
self.get_items_warn = true
|
self.get_items_warn = true
|
||||||
end
|
end
|
||||||
return {}, {}
|
return {"{:dummy:}"}, {"{:dummy:}"}
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
---Helper function to copy a styled text table into another.
|
||||||
|
---@param t1 StatusView.styledtext
|
||||||
|
---@param t2 StatusView.styledtext
|
||||||
|
local function table_add(t1, t2)
|
||||||
|
for i, value in ipairs(t2) do
|
||||||
|
table.insert(t1, value)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
---Helper function to merge deprecated items to a temp items table.
|
||||||
|
---@param destination table
|
||||||
|
---@param items StatusView.styledtext
|
||||||
|
---@param alignment StatusView.Item.alignment
|
||||||
|
local function merge_deprecated_items(destination, items, alignment)
|
||||||
|
local start = true
|
||||||
|
local items_start, items_end = {}, {}
|
||||||
|
for i, value in ipairs(items) do
|
||||||
|
if value ~= "{:dummy:}" then
|
||||||
|
if start then
|
||||||
|
table.insert(items_start, i, value)
|
||||||
|
else
|
||||||
|
table.insert(items_end, value)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
start = false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local position = alignment == StatusView.Item.LEFT and "left" or "right"
|
||||||
|
|
||||||
|
local item_start = StatusView.Item(
|
||||||
|
predicate_always_true,
|
||||||
|
"deprecated:"..position.."-start",
|
||||||
|
alignment
|
||||||
|
)
|
||||||
|
item_start.get_item = items_start
|
||||||
|
|
||||||
|
local item_end = StatusView.Item(
|
||||||
|
predicate_always_true,
|
||||||
|
"deprecated:"..position.."-end",
|
||||||
|
alignment
|
||||||
|
)
|
||||||
|
item_end.get_item = items_end
|
||||||
|
|
||||||
|
table.insert(destination, 1, item_start)
|
||||||
|
table.insert(destination, item_end)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
---@param self StatusView
|
||||||
|
---@param styled_text StatusView.styledtext
|
||||||
|
---@param separator string
|
||||||
|
local function add_spacing(self, styled_text, separator)
|
||||||
|
if
|
||||||
|
Object.is(styled_text[1], renderer.font)
|
||||||
|
or
|
||||||
|
(
|
||||||
|
styled_text[2] ~= self.separator
|
||||||
|
or
|
||||||
|
styled_text[2] ~= self.separator2
|
||||||
|
)
|
||||||
|
then
|
||||||
|
if separator == self.separator2 then
|
||||||
|
table.insert(styled_text, 1, style.dim)
|
||||||
|
else
|
||||||
|
table.insert(styled_text, 1, style.text)
|
||||||
|
end
|
||||||
|
table.insert(styled_text, 2, separator)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
---@param self StatusView
|
||||||
|
---@param styled_text StatusView.styledtext
|
||||||
|
local function remove_spacing(self, styled_text)
|
||||||
|
if
|
||||||
|
not Object.is(styled_text[1], renderer.font)
|
||||||
|
and
|
||||||
|
type(styled_text[1]) == "table"
|
||||||
|
and
|
||||||
|
(
|
||||||
|
styled_text[2] == self.separator
|
||||||
|
or
|
||||||
|
styled_text[2] == self.separator2
|
||||||
|
)
|
||||||
|
then
|
||||||
|
table.remove(styled_text, 1)
|
||||||
|
table.remove(styled_text, 1)
|
||||||
|
end
|
||||||
|
|
||||||
|
if
|
||||||
|
not Object.is(styled_text[#styled_text-1], renderer.font)
|
||||||
|
and
|
||||||
|
type(styled_text[#styled_text-1]) == "table"
|
||||||
|
and
|
||||||
|
(
|
||||||
|
styled_text[#styled_text] == self.separator
|
||||||
|
or
|
||||||
|
styled_text[#styled_text] == self.separator2
|
||||||
|
)
|
||||||
|
then
|
||||||
|
table.remove(styled_text, #styled_text)
|
||||||
|
table.remove(styled_text, #styled_text)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
---Get the styled text that will be displayed on the left side or
|
||||||
|
---right side of the status bar checking their predicates and performing
|
||||||
|
---positioning calculations for proper functioning of tooltips and clicks.
|
||||||
|
---@return StatusView.styledtext left
|
||||||
|
---@return StatusView.styledtext right
|
||||||
|
function StatusView:update_active_items()
|
||||||
|
local left, right = {}, {}
|
||||||
|
|
||||||
|
local x = self:get_content_offset()
|
||||||
|
|
||||||
|
local rx = x + self.size.x
|
||||||
|
local lx = x
|
||||||
|
local rw, lw = 0, 0
|
||||||
|
|
||||||
|
self.active_items = {}
|
||||||
|
|
||||||
|
---@type StatusView.Item[]
|
||||||
|
local combined_items = {}
|
||||||
|
table_add(combined_items, self.items)
|
||||||
|
|
||||||
|
-- load deprecated items for compatibility
|
||||||
|
local dleft, dright = self:get_items(true)
|
||||||
|
merge_deprecated_items(combined_items, dleft, StatusView.Item.LEFT)
|
||||||
|
merge_deprecated_items(combined_items, dright, StatusView.Item.RIGHT)
|
||||||
|
|
||||||
|
local lfirst, rfirst = true, true
|
||||||
|
|
||||||
|
-- calculate left and right width
|
||||||
|
for _, item in ipairs(combined_items) do
|
||||||
|
item.cached_item = {}
|
||||||
|
if item.visible and item.predicate(self) then
|
||||||
|
local styled_text = type(item.get_item) == "function"
|
||||||
|
and item.get_item(self) or item.get_item
|
||||||
|
|
||||||
|
if #styled_text > 0 then
|
||||||
|
remove_spacing(self, styled_text)
|
||||||
|
end
|
||||||
|
|
||||||
|
if #styled_text > 0 or item.on_draw then
|
||||||
|
item.active = true
|
||||||
|
if item.alignment == StatusView.Item.LEFT then
|
||||||
|
if not lfirst then
|
||||||
|
add_spacing(self, styled_text, item.separator, true)
|
||||||
|
else
|
||||||
|
lfirst = false
|
||||||
|
end
|
||||||
|
item.w = item.on_draw and
|
||||||
|
item.on_draw(lx, self.position.y, self.size.y, true)
|
||||||
|
or
|
||||||
|
draw_items(self, styled_text, 0, 0, text_width)
|
||||||
|
item.x = lx
|
||||||
|
lw = lw + item.w
|
||||||
|
lx = lx + item.w
|
||||||
|
else
|
||||||
|
if not rfirst then
|
||||||
|
add_spacing(self, styled_text, item.separator, true)
|
||||||
|
else
|
||||||
|
rfirst = false
|
||||||
|
end
|
||||||
|
item.w = item.on_draw and
|
||||||
|
item.on_draw(rx, self.position.y, self.size.y, true)
|
||||||
|
or
|
||||||
|
draw_items(self, styled_text, 0, 0, text_width)
|
||||||
|
item.x = rx
|
||||||
|
rw = rw + item.w
|
||||||
|
rx = rx + item.w
|
||||||
|
end
|
||||||
|
item.cached_item = styled_text
|
||||||
|
table.insert(self.active_items, item)
|
||||||
|
else
|
||||||
|
item.active = false
|
||||||
|
end
|
||||||
|
else
|
||||||
|
item.active = false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
self.r_left_width, self.r_right_width = lw, rw
|
||||||
|
|
||||||
|
if lw + rw + (style.padding.x * 2) > self.size.x then
|
||||||
|
lw = self.size.x / 2 - (style.padding.x * 2)
|
||||||
|
rw = self.size.x / 2 - (style.padding.x * 2)
|
||||||
|
end
|
||||||
|
|
||||||
|
self.left_width, self.right_width = lw, rw
|
||||||
|
|
||||||
|
for _, item in ipairs(self.active_items) do
|
||||||
|
if item.alignment == StatusView.Item.RIGHT then
|
||||||
|
-- re-calculate x position now that we have the total width
|
||||||
|
item.x = item.x - rw - (style.padding.x * 2)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
@ -380,6 +724,7 @@ function StatusView:on_mouse_pressed()
|
||||||
and not core.active_view:is(LogView) then
|
and not core.active_view:is(LogView) then
|
||||||
command.perform "core:open-log"
|
command.perform "core:open-log"
|
||||||
end
|
end
|
||||||
|
self.mouse_pressed = true
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -387,15 +732,18 @@ end
|
||||||
function StatusView:on_mouse_moved(x, y, dx, dy)
|
function StatusView:on_mouse_moved(x, y, dx, dy)
|
||||||
StatusView.super.on_mouse_moved(self, x, y, dx, dy)
|
StatusView.super.on_mouse_moved(self, x, y, dx, dy)
|
||||||
|
|
||||||
if y < self.position.y then self.hovered_item = {} return end
|
if y < self.position.y or system.get_time() <= self.message_timeout then
|
||||||
|
self.hovered_item = {} return
|
||||||
|
end
|
||||||
|
|
||||||
for _, item in ipairs(self.items) do
|
for _, item in ipairs(self.items) do
|
||||||
if item.onclick and item.active then
|
if
|
||||||
if
|
item.visible and item.active
|
||||||
(item.lx and x > item.lx and (item.lx + item.lw) > x)
|
and
|
||||||
or
|
(item.command or item.on_click or item.tooltip ~= "")
|
||||||
(item.rx and x > item.rx and (item.rx + item.rw) > x)
|
then
|
||||||
then
|
local item_x = item.x + style.padding.x
|
||||||
|
if x > item_x and (item_x + item.w) > x then
|
||||||
self.pointer.x = x
|
self.pointer.x = x
|
||||||
self.pointer.y = y
|
self.pointer.y = y
|
||||||
if self.hovered_item ~= item then
|
if self.hovered_item ~= item then
|
||||||
|
@ -412,15 +760,18 @@ end
|
||||||
function StatusView:on_mouse_released(button, x, y)
|
function StatusView:on_mouse_released(button, x, y)
|
||||||
StatusView.super.on_mouse_released(self, button, x, y)
|
StatusView.super.on_mouse_released(self, button, x, y)
|
||||||
|
|
||||||
if y < self.position.y or not self.hovered_item.onclick then return end
|
self.mouse_pressed = false
|
||||||
|
|
||||||
|
if y < self.position.y or not self.hovered_item.active then return end
|
||||||
|
|
||||||
local item = self.hovered_item
|
local item = self.hovered_item
|
||||||
if
|
local item_x = item.x + style.padding.x
|
||||||
(item.lx and x > item.lx and (item.lx + item.lw) > x)
|
if x > item_x and (item_x + item.w) > x then
|
||||||
or
|
if item.command then
|
||||||
(item.rx and x > item.rx and (item.rx + item.rw) > x)
|
command.perform(item.command)
|
||||||
then
|
elseif item.on_click then
|
||||||
self.hovered_item.onclick(button, x, y)
|
self.hovered_item.on_click(button, x, y)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -435,24 +786,74 @@ function StatusView:update()
|
||||||
end
|
end
|
||||||
|
|
||||||
StatusView.super.update(self)
|
StatusView.super.update(self)
|
||||||
|
|
||||||
|
self:update_active_items()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
function StatusView:draw()
|
function StatusView:draw()
|
||||||
self:draw_background(style.background2)
|
self:draw_background(style.background2)
|
||||||
|
|
||||||
if self.message then
|
if self.message and system.get_time() <= self.message_timeout then
|
||||||
self:draw_items(self.message, false, self.size.y)
|
self:draw_items(self.message, false, 0, self.size.y)
|
||||||
end
|
elseif self.tooltip_mode then
|
||||||
|
|
||||||
if self.tooltip_mode then
|
|
||||||
self:draw_items(self.tooltip)
|
self:draw_items(self.tooltip)
|
||||||
else
|
else
|
||||||
local left, right = self:get_items_from_list()
|
if #self.active_items > 0 then
|
||||||
self:draw_items(left)
|
--- draw left pane
|
||||||
self:draw_items(right, true)
|
core.push_clip_rect(
|
||||||
if self.hovered_item.tooltip then
|
0, self.position.y,
|
||||||
self:draw_item_tooltip(self.hovered_item)
|
self.left_width + style.padding.x, self.size.y
|
||||||
|
)
|
||||||
|
for _, item in ipairs(self.active_items) do
|
||||||
|
if item.alignment == StatusView.Item.LEFT then
|
||||||
|
if type(item.background_color) == "table" then
|
||||||
|
renderer.draw_rect(
|
||||||
|
item.x + style.padding.x, self.position.y,
|
||||||
|
item.w, self.size.y, item.background_color
|
||||||
|
)
|
||||||
|
end
|
||||||
|
if item.on_draw then
|
||||||
|
core.push_clip_rect(item.x + style.padding.x, self.position.y, item.w, self.size.y)
|
||||||
|
item.on_draw(item.x + style.padding.x, self.position.y, self.size.y)
|
||||||
|
core.pop_clip_rect()
|
||||||
|
else
|
||||||
|
self:draw_items(item.cached_item, false, item.x)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
core.pop_clip_rect()
|
||||||
|
|
||||||
|
--- draw right pane
|
||||||
|
core.push_clip_rect(
|
||||||
|
self.size.x - (self.right_width + style.padding.x), self.position.y,
|
||||||
|
self.right_width + style.padding.x, self.size.y
|
||||||
|
)
|
||||||
|
local rx = 0
|
||||||
|
for _, item in ipairs(self.active_items) do
|
||||||
|
if item.alignment == StatusView.Item.RIGHT then
|
||||||
|
if type(item.background_color) == "table" then
|
||||||
|
renderer.draw_rect(
|
||||||
|
item.x + style.padding.x, self.position.y,
|
||||||
|
item.w, self.size.y, item.background_color
|
||||||
|
)
|
||||||
|
end
|
||||||
|
if item.on_draw then
|
||||||
|
core.push_clip_rect(item.x + style.padding.x, self.position.y, item.w, self.size.y)
|
||||||
|
item.on_draw(item.x + style.padding.x, self.position.y, self.size.y)
|
||||||
|
core.pop_clip_rect()
|
||||||
|
else
|
||||||
|
self:draw_items(item.cached_item, false, item.x)
|
||||||
|
end
|
||||||
|
rx = rx + item.w
|
||||||
|
end
|
||||||
|
end
|
||||||
|
core.pop_clip_rect()
|
||||||
|
|
||||||
|
-- draw tooltip
|
||||||
|
if self.hovered_item.tooltip ~= "" and self.hovered_item.active then
|
||||||
|
self:draw_item_tooltip(self.hovered_item)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in New Issue