2019-12-28 12:16:32 +01:00
local core = require " core "
local common = require " core.common "
2020-05-12 15:44:29 +02:00
local command = require " core.command "
2019-12-28 12:16:32 +01:00
local config = require " core.config "
local style = require " core.style "
local DocView = require " core.docview "
2022-02-21 08:40:25 +01:00
local CommandView = require " core.commandview "
2020-05-12 15:44:29 +02:00
local LogView = require " core.logview "
2019-12-28 12:16:32 +01:00
local View = require " core.view "
2021-10-13 05:24:52 +02:00
local Object = require " core.object "
2019-12-28 12:16:32 +01:00
2022-02-23 10:15:14 +01:00
2022-06-08 02:09:48 +02:00
---@alias core.statusview.styledtext table<integer, renderer.font|renderer.color|string>
2022-02-21 08:40:25 +01:00
---A status bar implementation for lite, check core.status_view.
2022-06-08 02:09:48 +02:00
---@class core.statusview : core.view
---@field public super core.view
---@field private items core.statusview.item[]
---@field private active_items core.statusview.item[]
---@field private hovered_item core.statusview.item
2022-02-21 08:40:25 +01:00
---@field private message_timeout number
2022-06-08 02:09:48 +02:00
---@field private message core.statusview.styledtext
2022-02-21 08:40:25 +01:00
---@field private tooltip_mode boolean
2022-06-08 02:09:48 +02:00
---@field private tooltip core.statusview.styledtext
2022-02-23 10:15:14 +01:00
---@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"'
2022-02-24 00:25:25 +01:00
---@field private hovered_panel '"left"' | '"right"'
2022-02-23 10:15:14 +01:00
---@field private hide_messages boolean
2019-12-28 12:16:32 +01:00
local StatusView = View : extend ( )
2022-02-21 08:40:25 +01:00
---Space separator
---@type string
2020-05-03 23:44:49 +02:00
StatusView.separator = " "
2022-02-21 08:40:25 +01:00
---Pipe separator
---@type string
2020-05-03 23:44:49 +02:00
StatusView.separator2 = " | "
2019-12-28 12:16:32 +01:00
2022-06-08 02:09:48 +02:00
---@alias core.statusview.item.separator
---|>'core.statusview.separator' # Space separator
---| 'core.statusview.separator2' # Pipe separator
2022-02-23 10:15:14 +01:00
2022-06-08 02:09:48 +02:00
---@alias core.statusview.item.predicate fun():boolean
---@alias core.statusview.item.onclick fun(button: string, x: number, y: number)
2022-07-13 06:31:17 +02:00
---@alias core.statusview.item.get_item fun():core.statusview.styledtext,core.statusview.styledtext
2022-06-08 02:09:48 +02:00
---@alias core.statusview.item.ondraw fun(x, y, h, hovered: boolean, calc_only?: boolean):number
2022-02-23 10:15:14 +01:00
2022-06-08 02:09:48 +02:00
---@class core.statusview.item : core.object
2022-02-23 10:15:14 +01:00
---@field name string
2022-06-08 02:09:48 +02:00
---@field predicate core.statusview.item.predicate
---@field alignment core.statusview.item.alignment
2022-02-23 10:15:14 +01:00
---@field tooltip string | nil
---@field command string | nil @Command to perform when the item is clicked.
2022-06-08 02:09:48 +02:00
---@field on_click core.statusview.item.onclick | nil @Function called when item is clicked and no command is set.
---@field on_draw core.statusview.item.ondraw | nil @Custom drawing that when passed calc true should return the needed width for drawing and when false should draw.
2022-02-23 10:15:14 +01:00
---@field background_color renderer.color | nil
2022-02-28 22:56:17 +01:00
---@field background_color_hover renderer.color | nil
2022-02-23 10:15:14 +01:00
---@field visible boolean
2022-06-08 02:09:48 +02:00
---@field separator core.statusview.item.separator
2022-02-23 10:15:14 +01:00
---@field private active boolean
---@field private x number
---@field private w number
2022-06-08 02:09:48 +02:00
---@field private cached_item core.statusview.styledtext
local StatusViewItem = Object : extend ( )
2022-02-23 10:15:14 +01:00
2022-07-13 06:31:17 +02:00
---Available StatusViewItem options.
---@class core.statusview.item.options : table
---@field predicate string | table | core.statusview.item.predicate
---@field name string
---@field alignment core.statusview.item.alignment
---@field get_item core.statusview.item.get_item
---@field command? string | core.statusview.item.onclick
---@field position? integer
---@field tooltip? string
---@field separator? core.statusview.item.separator
local StatusViewItemOptions = {
---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.
predicate = nil ,
---A unique name to identify the item on the status bar.
name = nil ,
alignment = nil ,
---A function that should return a core.statusview.styledtext element,
---returning empty table is allowed.
get_item = nil ,
---The name of a valid registered command or a callback function to execute
---when the item is clicked.
command = nil ,
---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
---of 1 will insert the item at the beggining.
position = nil ,
---Displayed when mouse hovers the item
tooltip = nil ,
separator = nil ,
}
StatusViewItem.options = StatusViewItemOptions
2022-02-23 10:15:14 +01:00
---Flag to tell the item should me aligned on left side of status bar.
---@type number
2022-06-08 02:09:48 +02:00
StatusViewItem.LEFT = 1
2022-02-23 10:15:14 +01:00
---Flag to tell the item should me aligned on right side of status bar.
---@type number
2022-06-08 02:09:48 +02:00
StatusViewItem.RIGHT = 2
2022-02-23 10:15:14 +01:00
2022-06-08 02:09:48 +02:00
---@alias core.statusview.item.alignment
---|>'core.statusview.item.LEFT'
---| 'core.statusview.item.RIGHT'
2022-02-23 10:15:14 +01:00
---Constructor
2022-07-13 06:31:17 +02:00
---@param options core.statusview.item.options
function StatusViewItem : new ( options )
self : set_predicate ( options.predicate )
self.name = options.name
self.alignment = options.alignment or StatusView.Item . LEFT
self.command = type ( options.command ) == " string " and options.command or nil
self.tooltip = options.tooltip or " "
self.on_click = type ( options.command ) == " function " and options.command or nil
2022-02-23 10:15:14 +01:00
self.on_draw = nil
self.background_color = nil
2022-02-28 22:56:17 +01:00
self.background_color_hover = nil
2022-07-13 06:31:17 +02:00
self.visible = options.visible == nil and true or options.visible
2022-02-23 10:15:14 +01:00
self.active = false
self.x = 0
self.w = 0
2022-07-13 06:31:17 +02:00
self.separator = options.separator or StatusView.separator
self.get_item = options.get_item
2022-02-23 10:15:14 +01:00
end
---Called by the status bar each time that the item needs to be rendered,
---if on_draw() is set this function is obviated.
2022-06-08 02:09:48 +02:00
---@return core.statusview.styledtext
function StatusViewItem : get_item ( ) return { } end
2022-02-23 10:15:14 +01:00
---Do not show the item on the status bar.
2022-06-08 02:09:48 +02:00
function StatusViewItem : hide ( ) self.visible = false end
2022-02-23 10:15:14 +01:00
---Show the item on the status bar.
2022-06-08 02:09:48 +02:00
function StatusViewItem : show ( ) self.visible = true end
2022-02-23 10:15:14 +01:00
---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.
2022-06-08 02:09:48 +02:00
---@param predicate string | table | core.statusview.item.predicate
function StatusViewItem : set_predicate ( predicate )
2022-05-13 04:15:29 +02:00
self.predicate = command.generate_predicate ( predicate )
2022-02-23 10:15:14 +01:00
end
2022-06-08 02:09:48 +02:00
---@type core.statusview.item
StatusView.Item = StatusViewItem
2022-02-23 10:15:14 +01:00
---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
2019-12-28 12:16:32 +01:00
2022-02-21 08:40:25 +01:00
---Constructor
2019-12-28 12:16:32 +01:00
function StatusView : new ( )
StatusView.super . new ( self )
self.message_timeout = 0
self.message = { }
2021-02-21 11:09:51 +01:00
self.tooltip_mode = false
self.tooltip = { }
2022-02-21 08:40:25 +01:00
self.items = { }
2022-02-23 10:15:14 +01:00
self.active_items = { }
2022-02-21 08:40:25 +01:00
self.hovered_item = { }
self.pointer = { x = 0 , y = 0 }
2022-02-23 10:15:14 +01:00
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 = " "
2022-02-24 00:25:25 +01:00
self.hovered_panel = " "
2022-02-23 10:15:14 +01:00
self.hide_messages = false
2022-03-10 14:34:46 +01:00
self.visible = true
2022-02-23 10:15:14 +01:00
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
2022-07-13 06:31:17 +02:00
self : add_item ( {
predicate = predicate_docview ,
name = " doc:file " ,
alignment = StatusView.Item . LEFT ,
get_item = function ( )
2022-02-23 10:15:14 +01:00
local dv = core.active_view
return {
dv.doc : is_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 ( )
}
end
2022-07-13 06:31:17 +02:00
} )
2022-02-23 10:15:14 +01:00
2022-07-13 06:31:17 +02:00
self : add_item ( {
predicate = predicate_docview ,
name = " doc:position " ,
alignment = StatusView.Item . LEFT ,
get_item = function ( )
2022-02-23 10:15:14 +01:00
local dv = core.active_view
local line , col = dv.doc : get_selection ( )
2022-07-22 19:01:54 +02:00
local _ , indent_size = dv.doc : get_indent_info ( )
-- Calculating tabs when the doc is using the "hard" indent type.
local ntabs = 0
local last_idx = 0
while last_idx < col do
local s , e = string.find ( dv.doc . lines [ line ] , " \t " , last_idx , true )
if s and s < col then
ntabs = ntabs + 1
last_idx = e + 1
else
break
end
end
col = col + ntabs * ( indent_size - 1 )
2022-02-23 10:15:14 +01:00
return {
2022-02-24 07:57:39 +01:00
style.text , line , " : " ,
2022-02-23 10:15:14 +01:00
col > config.line_limit and style.accent or style.text , col ,
style.text ,
self.separator ,
string.format ( " %.f%% " , line / # dv.doc . lines * 100 )
}
end ,
2022-07-13 06:31:17 +02:00
command = " doc:go-to-line " ,
tooltip = " line : column "
} )
self : add_item ( {
predicate = predicate_docview ,
name = " doc:indentation " ,
alignment = StatusView.Item . RIGHT ,
get_item = function ( )
2022-02-23 10:15:14 +01:00
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 {
2022-02-24 19:22:52 +01:00
style.text , indent_label , indent_size ,
indent_confirmed and " " or " * "
2022-02-23 10:15:14 +01:00
}
2022-02-21 08:40:25 +01:00
end ,
2022-07-13 06:31:17 +02:00
command = function ( button , x , y )
2022-02-23 10:15:14 +01:00
if button == " left " then
command.perform " indent:set-file-indent-size "
elseif button == " right " then
command.perform " indent:set-file-indent-type "
end
2022-07-13 06:31:17 +02:00
end ,
separator = self.separator2
} )
self : add_item ( {
predicate = predicate_docview ,
name = " doc:lines " ,
alignment = StatusView.Item . RIGHT ,
get_item = function ( )
2022-02-23 10:15:14 +01:00
local dv = core.active_view
return {
style.text ,
style.icon_font , " g " ,
2022-02-24 07:57:39 +01:00
style.font , style.dim , self.separator2 ,
style.text , # dv.doc . lines , " lines " ,
2022-02-23 10:15:14 +01:00
}
2022-07-13 06:31:17 +02:00
end ,
separator = self.separator2
} )
self : add_item ( {
predicate = predicate_docview ,
name = " doc:line-ending " ,
alignment = StatusView.Item . RIGHT ,
get_item = function ( )
2022-02-23 10:15:14 +01:00
local dv = core.active_view
return {
2022-02-24 07:57:39 +01:00
style.text , dv.doc . crlf and " CRLF " or " LF "
2022-02-23 10:15:14 +01:00
}
end ,
2022-07-13 06:31:17 +02:00
command = " doc:toggle-line-ending "
} )
2022-02-23 10:15:14 +01:00
end
2022-02-21 08:40:25 +01:00
2022-02-23 10:15:14 +01:00
---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
2022-07-13 06:31:17 +02:00
self : add_item ( {
predicate = " core.commandview " ,
name = " command:files " ,
alignment = StatusView.Item . RIGHT ,
get_item = function ( )
2022-02-23 10:15:14 +01:00
return {
style.icon_font , " g " ,
style.font , style.dim , self.separator2 ,
2022-02-24 07:57:39 +01:00
style.text , # core.docs , style.text , " / " ,
2022-02-23 10:15:14 +01:00
# core.project_files , " files "
}
end
2022-07-13 06:31:17 +02:00
} )
2019-12-28 12:16:32 +01:00
end
2022-03-21 23:40:14 +01:00
---Set a position to the best match according to total available items.
2022-06-08 02:09:48 +02:00
---@param self core.statusview
2022-03-21 23:40:14 +01:00
---@param position integer
2022-06-08 02:09:48 +02:00
---@param alignment core.statusview.item.alignment
2022-03-21 23:40:14 +01:00
---@return integer position
local function normalize_position ( self , position , alignment )
local offset = 0
local items_count = 0
local left = self : get_items_list ( 1 )
local right = self : get_items_list ( 2 )
if alignment == 2 then
items_count = # right
offset = # left
else
items_count = # left
end
if position == 0 then
position = offset + 1
elseif position < 0 then
position = offset + items_count + ( position + 2 )
else
position = offset + position
end
if position < 1 then
position = offset + 1
elseif position > # left + # right then
position = offset + items_count + 1
end
return position
end
2022-02-21 08:40:25 +01:00
---Adds an item to be rendered in the status bar.
2022-07-13 06:31:17 +02:00
---@param options core.statusview.item.options
2022-06-08 02:09:48 +02:00
---@return core.statusview.item
2022-07-13 06:31:17 +02:00
function StatusView : add_item ( options )
assert ( self : get_item ( options.name ) == nil , " status item already exists: " .. options.name )
2022-06-08 02:09:48 +02:00
---@type core.statusview.item
2022-07-13 06:31:17 +02:00
local item = StatusView.Item ( options )
table.insert ( self.items , normalize_position ( self , options.position or - 1 , options.alignment ) , item )
2022-02-23 10:15:14 +01:00
return item
end
2022-02-24 07:57:39 +01:00
---Get an item object associated to a name or nil if not found.
2022-02-23 10:15:14 +01:00
---@param name string
2022-06-08 02:09:48 +02:00
---@return core.statusview.item | nil
2022-02-23 10:15:14 +01:00
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.
2022-06-08 02:09:48 +02:00
---@param alignment? core.statusview.item.alignment
---@return core.statusview.item[]
2022-02-23 10:15:14 +01:00
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
2022-03-21 23:40:14 +01:00
---Move an item to a different position.
---@param name string
---@param position integer Can be negative value to position in reverse order
2022-06-08 02:09:48 +02:00
---@param alignment? core.statusview.item.alignment
2022-03-21 23:40:14 +01:00
---@return boolean moved
function StatusView : move_item ( name , position , alignment )
assert ( name , " no name provided " )
assert ( position , " no position provided " )
local item = nil
for pos , it in ipairs ( self.items ) do
if it.name == name then
item = table.remove ( self.items , pos )
break
end
end
if item then
if alignment then
item.alignment = alignment
end
position = normalize_position ( self , position , item.alignment )
table.insert ( self.items , position , item )
return true
end
return false
end
---Remove an item from the status view.
---@param name string
2022-06-08 02:09:48 +02:00
---@return core.statusview.item removed_item
2022-03-21 23:40:14 +01:00
function StatusView : remove_item ( name )
local item = nil
for pos , it in ipairs ( self.items ) do
if it.name == name then
item = table.remove ( self.items , pos )
break
end
end
return item
end
---Order the items by name
---@param names table<integer, string>
function StatusView : order_items ( names )
local removed_items = { }
for _ , name in ipairs ( names ) do
local item = self : remove_item ( name )
if item then table.insert ( removed_items , item ) end
end
for i , item in ipairs ( removed_items ) do
table.insert ( self.items , i , item )
end
end
2022-03-10 14:34:46 +01:00
---Hide the status bar
function StatusView : hide ( )
self.visible = false
end
---Show the status bar
function StatusView : show ( )
self.visible = true
end
---Toggle the visibility of the status bar
function StatusView : toggle ( )
self.visible = not self.visible
end
2022-02-23 10:15:14 +01:00
---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
2020-05-12 15:44:29 +02:00
end
end
2022-02-21 08:40:25 +01:00
---Shows a message for a predefined amount of time.
---@param icon string
---@param icon_color renderer.color
---@param text string
2019-12-28 12:16:32 +01:00
function StatusView : show_message ( icon , icon_color , text )
2022-03-10 14:34:46 +01:00
if not self.visible or self.hide_messages then return end
2019-12-28 12:16:32 +01:00
self.message = {
icon_color , style.icon_font , icon ,
2020-05-03 23:44:49 +02:00
style.dim , style.font , StatusView.separator2 , style.text , text
2019-12-28 12:16:32 +01:00
}
self.message_timeout = system.get_time ( ) + config.message_timeout
end
2022-02-23 10:15:14 +01:00
---Enable or disable system wide messages on the status bar.
---@param enable boolean
function StatusView : display_messages ( enable )
self.hide_messages = not enable
end
2022-02-21 08:40:25 +01:00
---Activates tooltip mode displaying only the given
2022-06-08 02:09:48 +02:00
---text until core.statusview:remove_tooltip() is called.
---@param text string | core.statusview.styledtext
2021-02-19 11:50:20 +01:00
function StatusView : show_tooltip ( text )
2022-02-21 08:40:25 +01:00
self.tooltip = type ( text ) == " table " and text or { text }
2021-02-21 11:09:51 +01:00
self.tooltip_mode = true
2021-02-19 11:50:20 +01:00
end
2022-02-21 08:40:25 +01:00
---Deactivates tooltip mode.
2021-02-19 11:50:20 +01:00
function StatusView : remove_tooltip ( )
2021-02-21 11:09:51 +01:00
self.tooltip_mode = false
2021-02-19 11:50:20 +01:00
end
2022-02-21 08:40:25 +01:00
---Helper function to draw the styled text.
2022-06-08 02:09:48 +02:00
---@param self core.statusview
---@param items core.statusview.styledtext
2022-02-21 08:40:25 +01:00
---@param x number
---@param y number
---@param draw_fn fun(font,color,text,align, x,y,w,h):number
2020-05-03 19:42:52 +02:00
local function draw_items ( self , items , x , y , draw_fn )
2019-12-28 12:16:32 +01:00
local font = style.font
local color = style.text
2020-05-03 19:42:52 +02:00
for _ , item in ipairs ( items ) do
2021-10-13 05:24:52 +02:00
if Object.is ( item , renderer.font ) then
2019-12-28 12:16:32 +01:00
font = item
elseif type ( item ) == " table " then
color = item
else
2020-05-03 19:42:52 +02:00
x = draw_fn ( font , color , item , nil , x , y , 0 , self.size . y )
2019-12-28 12:16:32 +01:00
end
2020-05-03 19:42:52 +02:00
end
return x
end
2022-02-21 08:40:25 +01:00
---Helper function to calculate the width of text by using it as part of
---the helper function draw_items().
---@param font renderer.font
---@param text string
---@param x number
2020-05-03 19:42:52 +02:00
local function text_width ( font , _ , text , _ , x )
return x + font : get_width ( text )
end
2019-12-28 12:16:32 +01:00
2022-02-21 08:40:25 +01:00
---Draws a table of styled text on the status bar starting on the left or right.
2022-06-08 02:09:48 +02:00
---@param items core.statusview.styledtext
---@param right_align? boolean
2022-02-24 07:57:39 +01:00
---@param xoffset? number
---@param yoffset? number
2022-02-23 10:15:14 +01:00
function StatusView : draw_items ( items , right_align , xoffset , yoffset )
2020-05-03 19:42:52 +02:00
local x , y = self : get_content_offset ( )
2022-02-23 10:15:14 +01:00
x = x + ( xoffset or 0 )
2020-05-03 19:42:52 +02:00
y = y + ( yoffset or 0 )
if right_align then
local w = draw_items ( self , items , 0 , 0 , text_width )
x = x + self.size . x - w - style.padding . x
draw_items ( self , items , x , y , common.draw_text )
else
x = x + style.padding . x
draw_items ( self , items , x , y , common.draw_text )
2019-12-28 12:16:32 +01:00
end
end
2022-02-21 08:40:25 +01:00
---Draw the tooltip of a given status bar item.
2022-06-08 02:09:48 +02:00
---@param item core.statusview.item
2022-02-21 08:40:25 +01:00
function StatusView : draw_item_tooltip ( item )
core.root_view : defer_draw ( function ( )
local text = item.tooltip
local w = style.font : get_width ( text )
local h = style.font : get_height ( )
local x = self.pointer . x - ( w / 2 ) - ( style.padding . x * 2 )
if x < 0 then x = 0 end
if x + w + ( style.padding . x * 2 ) > self.size . x then
x = self.size . x - w - ( style.padding . x * 2 )
end
renderer.draw_rect (
2022-02-23 10:15:14 +01:00
x + style.padding . x ,
2022-02-21 08:40:25 +01:00
self.position . y - h - ( style.padding . y * 2 ) ,
w + ( style.padding . x * 2 ) ,
h + ( style.padding . y * 2 ) ,
style.background3
)
renderer.draw_text (
style.font ,
text ,
2022-02-23 10:15:14 +01:00
x + ( style.padding . x * 2 ) ,
2022-02-21 08:40:25 +01:00
self.position . y - h - style.padding . y ,
style.text
)
end )
end
2022-02-23 10:15:14 +01:00
---Older method of retrieving the status bar items and which is now
---deprecated in favour of core.status_view:add_item().
---@deprecated
---@param nowarn boolean
2022-02-21 08:40:25 +01:00
---@return table left
---@return table right
2022-02-23 10:15:14 +01:00
function StatusView : get_items ( nowarn )
if not nowarn and not self.get_items_warn then
2022-06-11 05:13:37 +02:00
core.warn (
2022-02-23 10:15:14 +01:00
" Overriding StatusView:get_items() is deprecated, "
.. " use core.status_view:add_item() instead. "
)
self.get_items_warn = true
end
return { " {:dummy:} " } , { " {:dummy:} " }
2019-12-28 12:16:32 +01:00
end
2022-02-21 08:40:25 +01:00
---Helper function to copy a styled text table into another.
2022-06-08 02:09:48 +02:00
---@param t1 core.statusview.styledtext
---@param t2 core.statusview.styledtext
2022-02-21 08:40:25 +01:00
local function table_add ( t1 , t2 )
2022-06-08 02:09:48 +02:00
for _ , value in ipairs ( t2 ) do
2022-02-21 08:40:25 +01:00
table.insert ( t1 , value )
end
end
2022-02-23 10:15:14 +01:00
---Helper function to merge deprecated items to a temp items table.
---@param destination table
2022-06-08 02:09:48 +02:00
---@param items core.statusview.styledtext
---@param alignment core.statusview.item.alignment
2022-02-23 10:15:14 +01:00
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 "
2022-07-13 06:31:17 +02:00
local item_start = StatusView.Item ( {
name = " deprecated: " .. position .. " -start " ,
alignment = alignment ,
get_item = items_start
} )
2022-02-23 10:15:14 +01:00
2022-07-13 06:31:17 +02:00
local item_end = StatusView.Item ( {
name = " deprecated: " .. position .. " -end " ,
alignment = alignment ,
get_item = items_end
} )
2022-02-23 10:15:14 +01:00
table.insert ( destination , 1 , item_start )
table.insert ( destination , item_end )
end
2022-02-24 07:57:39 +01:00
---Append a space item into the given items list.
2022-06-08 02:09:48 +02:00
---@param self core.statusview
---@param destination core.statusview.item[]
2022-02-23 10:15:14 +01:00
---@param separator string
2022-06-08 02:09:48 +02:00
---@param alignment core.statusview.item.alignment
---@return core.statusview.item
2022-02-24 07:57:39 +01:00
local function add_spacing ( self , destination , separator , alignment , x )
2022-06-08 02:09:48 +02:00
---@type core.statusview.item
2022-07-13 06:31:17 +02:00
local space = StatusView.Item ( { name = " space " , alignment = alignment } )
2022-02-24 07:57:39 +01:00
space.cached_item = separator == self.separator and {
style.text , separator
} or {
style.dim , separator
}
space.x = x
space.w = draw_items ( self , space.cached_item , 0 , 0 , text_width )
table.insert ( destination , space )
return space
2022-02-23 10:15:14 +01:00
end
2022-02-24 07:57:39 +01:00
---Remove starting and ending separators.
2022-06-08 02:09:48 +02:00
---@param self core.statusview
---@param styled_text core.statusview.styledtext
2022-02-23 10:15:14 +01:00
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
2022-02-24 07:57:39 +01:00
---Set the active items that will be displayed on the left or right side
---of the status bar checking their predicates and performing positioning
---calculations for proper functioning of tooltips and clicks.
2022-02-23 10:15:14 +01:00
function StatusView : update_active_items ( )
2022-02-21 08:40:25 +01:00
local x = self : get_content_offset ( )
local rx = x + self.size . x
2022-02-23 10:15:14 +01:00
local lx = x
2022-02-21 08:40:25 +01:00
local rw , lw = 0 , 0
2022-02-23 10:15:14 +01:00
self.active_items = { }
2022-02-21 08:40:25 +01:00
2022-06-08 02:09:48 +02:00
---@type core.statusview.item[]
2022-02-23 10:15:14 +01:00
local combined_items = { }
table_add ( combined_items , self.items )
2022-02-21 08:40:25 +01:00
2022-02-23 10:15:14 +01:00
-- 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 )
2022-02-21 08:40:25 +01:00
2022-02-23 10:15:14 +01:00
local lfirst , rfirst = true , true
2022-02-21 08:40:25 +01:00
2022-02-23 10:15:14 +01:00
-- calculate left and right width
for _ , item in ipairs ( combined_items ) do
item.cached_item = { }
2022-06-08 02:09:48 +02:00
if item.visible and item : predicate ( ) then
2022-02-23 10:15:14 +01:00
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 )
2022-02-21 08:40:25 +01:00
end
2022-02-23 10:15:14 +01:00
if # styled_text > 0 or item.on_draw then
item.active = true
2022-02-28 22:56:17 +01:00
local hovered = self.hovered_item == item
2022-02-23 10:15:14 +01:00
if item.alignment == StatusView.Item . LEFT then
if not lfirst then
2022-02-24 07:57:39 +01:00
local space = add_spacing (
self , self.active_items , item.separator , item.alignment , lx
)
lw = lw + space.w
lx = lx + space.w
2022-02-23 10:15:14 +01:00
else
lfirst = false
end
item.w = item.on_draw and
2022-02-28 22:56:17 +01:00
item.on_draw ( lx , self.position . y , self.size . y , hovered , true )
2022-02-23 10:15:14 +01:00
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
2022-02-24 07:57:39 +01:00
local space = add_spacing (
self , self.active_items , item.separator , item.alignment , rx
)
rw = rw + space.w
rx = rx + space.w
2022-02-23 10:15:14 +01:00
else
rfirst = false
end
item.w = item.on_draw and
2022-02-28 22:56:17 +01:00
item.on_draw ( rx , self.position . y , self.size . y , hovered , true )
2022-02-23 10:15:14 +01:00
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
2022-02-21 08:40:25 +01:00
end
else
item.active = false
end
end
2022-02-23 10:15:14 +01:00
self.r_left_width , self.r_right_width = lw , rw
2022-02-24 05:21:21 +01:00
-- try to calc best size for left and right
if lw + rw + ( style.padding . x * 4 ) > self.size . x then
if lw + ( style.padding . x * 2 ) < self.size . x / 2 then
rw = self.size . x - lw - ( style.padding . x * 3 )
if rw > self.r_right_width then
lw = lw + ( rw - self.r_right_width )
rw = self.r_right_width
end
elseif rw + ( style.padding . x * 2 ) < self.size . x / 2 then
lw = self.size . x - rw - ( style.padding . x * 3 )
else
lw = self.size . x / 2 - ( style.padding . x + style.padding . x / 2 )
rw = self.size . x / 2 - ( style.padding . x + style.padding . x / 2 )
end
2022-02-28 22:04:37 +01:00
-- reposition left and right offsets when window is resized
if rw >= self.r_right_width then
self.right_xoffset = 0
elseif rw > self.right_xoffset + self.r_right_width then
self.right_xoffset = rw - self.r_right_width
end
if lw >= self.r_left_width then
self.left_xoffset = 0
elseif lw > self.left_xoffset + self.r_left_width then
self.left_xoffset = lw - self.r_left_width
end
2022-02-24 00:25:25 +01:00
else
self.left_xoffset = 0
self.right_xoffset = 0
2022-02-21 08:40:25 +01:00
end
2022-02-23 10:15:14 +01:00
self.left_width , self.right_width = lw , rw
2022-02-21 08:40:25 +01:00
2022-02-23 10:15:14 +01:00
for _ , item in ipairs ( self.active_items ) do
if item.alignment == StatusView.Item . RIGHT then
2022-02-21 08:40:25 +01:00
-- re-calculate x position now that we have the total width
2022-02-23 10:15:14 +01:00
item.x = item.x - rw - ( style.padding . x * 2 )
2022-02-21 08:40:25 +01:00
end
end
end
2022-02-24 00:25:25 +01:00
---Drag the given panel if possible.
---@param panel '"left"' | '"right"'
2022-02-24 05:21:21 +01:00
---@param dx number
2022-02-24 00:25:25 +01:00
function StatusView : drag_panel ( panel , dx )
if panel == " left " and self.r_left_width > self.left_width then
local nonvisible_w = self.r_left_width - self.left_width
local new_offset = self.left_xoffset + dx
if new_offset >= 0 - nonvisible_w and new_offset <= 0 then
self.left_xoffset = new_offset
2022-02-24 05:21:21 +01:00
elseif dx < 0 then
self.left_xoffset = 0 - nonvisible_w
else
self.left_xoffset = 0
2022-02-24 00:25:25 +01:00
end
elseif panel == " right " and self.r_right_width > self.right_width then
local nonvisible_w = self.r_right_width - self.right_width
local new_offset = self.right_xoffset + dx
if new_offset >= 0 - nonvisible_w and new_offset <= 0 then
self.right_xoffset = new_offset
2022-02-24 05:21:21 +01:00
elseif dx < 0 then
self.right_xoffset = 0 - nonvisible_w
else
self.right_xoffset = 0
2022-02-24 00:25:25 +01:00
end
end
end
---Return the currently hovered panel or empty string if none.
---@param x number
---@param y number
---@return string
function StatusView : get_hovered_panel ( x , y )
if y >= self.position . y and x <= self.left_width + style.padding . x then
return " left "
else
return " right "
end
return " "
end
2022-06-08 02:09:48 +02:00
---@param item core.statusview.item
2022-02-24 07:23:29 +01:00
---@return number x
---@return number w
function StatusView : get_item_visible_area ( item )
local item_ox = item.alignment == StatusView.Item . LEFT and
self.left_xoffset or self.right_xoffset
local item_x = item_ox + item.x + style.padding . x
local item_w = item.w
if item.alignment == StatusView.Item . LEFT then
if self.left_width - item_x > 0 and self.left_width - item_x < item.w then
item_w = ( self.left_width + style.padding . x ) - item_x
elseif self.left_width - item_x < 0 then
item_x = 0
item_w = 0
end
else
local rx = self.size . x - self.right_width - style.padding . x
if item_x < rx then
if item_x + item.w > rx then
item_x = rx
item_w = ( item_x + item.w ) - rx
else
item_x = 0
item_w = 0
end
end
end
return item_x , item_w
end
2022-02-24 00:25:25 +01:00
function StatusView : on_mouse_pressed ( button , x , y , clicks )
2022-03-10 14:34:46 +01:00
if not self.visible then return end
2022-02-21 08:40:25 +01:00
core.set_active_view ( core.last_active_view )
2022-02-24 00:25:25 +01:00
if
system.get_time ( ) < self.message_timeout
and
not core.active_view : is ( LogView )
then
2022-02-21 08:40:25 +01:00
command.perform " core:open-log "
2022-02-24 00:25:25 +01:00
else
if y >= self.position . y and button == " left " and clicks == 1 then
self.position . dx = x
2022-03-10 14:34:46 +01:00
if
self.r_left_width > self.left_width
or
self.r_right_width > self.right_width
then
2022-02-24 00:25:25 +01:00
self.dragged_panel = self : get_hovered_panel ( x , y )
self.cursor = " hand "
end
end
2022-02-21 08:40:25 +01:00
end
return true
end
function StatusView : on_mouse_moved ( x , y , dx , dy )
2022-03-10 14:34:46 +01:00
if not self.visible then return end
2022-02-21 08:40:25 +01:00
StatusView.super . on_mouse_moved ( self , x , y , dx , dy )
2022-02-24 00:25:25 +01:00
self.hovered_panel = self : get_hovered_panel ( x , y )
if self.dragged_panel ~= " " then
self : drag_panel ( self.dragged_panel , dx )
return
end
2022-02-23 10:15:14 +01:00
if y < self.position . y or system.get_time ( ) <= self.message_timeout then
2022-02-23 10:56:59 +01:00
self.cursor = " arrow "
self.hovered_item = { }
return
2022-02-23 10:15:14 +01:00
end
2022-02-21 08:40:25 +01:00
for _ , item in ipairs ( self.items ) do
2022-02-23 10:15:14 +01:00
if
item.visible and item.active
and
( item.command or item.on_click or item.tooltip ~= " " )
then
2022-02-24 07:23:29 +01:00
local item_x , item_w = self : get_item_visible_area ( item )
2022-02-24 00:25:25 +01:00
2022-02-24 07:23:29 +01:00
if x > item_x and ( item_x + item_w ) > x then
2022-02-21 08:40:25 +01:00
self.pointer . x = x
self.pointer . y = y
if self.hovered_item ~= item then
self.hovered_item = item
end
2022-02-23 10:56:59 +01:00
if item.command or item.on_click then
self.cursor = " hand "
end
2022-02-21 08:40:25 +01:00
return
end
end
end
2022-02-23 10:56:59 +01:00
self.cursor = " arrow "
2022-02-21 08:40:25 +01:00
self.hovered_item = { }
end
function StatusView : on_mouse_released ( button , x , y )
2022-03-10 14:34:46 +01:00
if not self.visible then return end
2022-02-21 08:40:25 +01:00
StatusView.super . on_mouse_released ( self , button , x , y )
2022-02-24 00:25:25 +01:00
if self.dragged_panel ~= " " then
self.dragged_panel = " "
self.cursor = " arrow "
if self.position . dx ~= x then
return
end
end
2022-02-23 10:15:14 +01:00
if y < self.position . y or not self.hovered_item . active then return end
2022-02-21 08:40:25 +01:00
local item = self.hovered_item
2022-02-24 07:23:29 +01:00
local item_x , item_w = self : get_item_visible_area ( item )
if x > item_x and ( item_x + item_w ) > x then
2022-02-23 10:15:14 +01:00
if item.command then
command.perform ( item.command )
elseif item.on_click then
2022-02-24 00:25:25 +01:00
item.on_click ( button , x , y )
2022-02-23 10:15:14 +01:00
end
2022-02-21 08:40:25 +01:00
end
end
2022-02-24 00:25:25 +01:00
function StatusView : on_mouse_wheel ( y )
2022-03-10 14:34:46 +01:00
if not self.visible then return end
2022-02-24 00:25:25 +01:00
self : drag_panel ( self.hovered_panel , y * self.left_width / 10 )
end
2022-02-21 08:40:25 +01:00
function StatusView : update ( )
2022-03-10 14:34:46 +01:00
if not self.visible and self.size . y <= 0 then
return
elseif not self.visible and self.size . y > 0 then
2022-04-26 02:35:35 +02:00
self : move_towards ( self.size , " y " , 0 , nil , " statusbar " )
2022-03-10 14:34:46 +01:00
return
end
local height = style.font : get_height ( ) + style.padding . y * 2 ;
if self.size . y + 1 < height then
2022-04-26 02:35:35 +02:00
self : move_towards ( self.size , " y " , height , nil , " statusbar " )
2022-03-10 14:34:46 +01:00
else
self.size . y = height
end
2022-02-21 08:40:25 +01:00
if system.get_time ( ) < self.message_timeout then
self.scroll . to.y = self.size . y
else
self.scroll . to.y = 0
end
StatusView.super . update ( self )
2022-02-23 10:15:14 +01:00
self : update_active_items ( )
2022-02-21 08:40:25 +01:00
end
2022-02-28 22:56:17 +01:00
---Retrieve the hover status and proper background color if any.
2022-06-08 02:09:48 +02:00
---@param self core.statusview
---@param item core.statusview.item
2022-02-28 22:56:17 +01:00
---@return boolean is_hovered
---@return renderer.color | nil color
local function get_item_bg_color ( self , item )
local hovered = self.hovered_item == item
local item_bg = hovered
and item.background_color_hover or item.background_color
return hovered , item_bg
end
2019-12-28 12:16:32 +01:00
function StatusView : draw ( )
2022-03-10 14:34:46 +01:00
if not self.visible and self.size . y <= 0 then return end
2019-12-28 12:16:32 +01:00
self : draw_background ( style.background2 )
2022-02-23 10:15:14 +01:00
if self.message and system.get_time ( ) <= self.message_timeout then
self : draw_items ( self.message , false , 0 , self.size . y )
2021-02-21 11:09:51 +01:00
else
2022-08-09 20:53:30 +02:00
if self.tooltip_mode then
self : draw_items ( self.tooltip )
end
2022-02-23 10:15:14 +01:00
if # self.active_items > 0 then
--- draw left pane
core.push_clip_rect (
0 , self.position . y ,
self.left_width + style.padding . x , self.size . y
)
for _ , item in ipairs ( self.active_items ) do
2022-02-24 00:25:25 +01:00
local item_x = self.left_xoffset + item.x + style.padding . x
2022-02-28 22:56:17 +01:00
local hovered , item_bg = get_item_bg_color ( self , item )
2022-08-09 20:53:30 +02:00
if item.alignment == StatusView.Item . LEFT and not self.tooltip_mode then
2022-02-28 22:56:17 +01:00
if type ( item_bg ) == " table " then
2022-02-23 10:15:14 +01:00
renderer.draw_rect (
2022-02-24 00:25:25 +01:00
item_x , self.position . y ,
2022-02-28 22:56:17 +01:00
item.w , self.size . y , item_bg
2022-02-23 10:15:14 +01:00
)
end
if item.on_draw then
2022-02-24 00:25:25 +01:00
core.push_clip_rect ( item_x , self.position . y , item.w , self.size . y )
2022-02-28 22:56:17 +01:00
item.on_draw ( item_x , self.position . y , self.size . y , hovered )
2022-02-23 10:15:14 +01:00
core.pop_clip_rect ( )
else
2022-02-24 00:25:25 +01:00
self : draw_items ( item.cached_item , false , item_x - style.padding . x )
2022-02-23 10:15:14 +01:00
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
)
for _ , item in ipairs ( self.active_items ) do
2022-02-24 00:25:25 +01:00
local item_x = self.right_xoffset + item.x + style.padding . x
2022-02-28 22:56:17 +01:00
local hovered , item_bg = get_item_bg_color ( self , item )
2022-02-23 10:15:14 +01:00
if item.alignment == StatusView.Item . RIGHT then
2022-02-28 22:56:17 +01:00
if type ( item_bg ) == " table " then
2022-02-23 10:15:14 +01:00
renderer.draw_rect (
2022-02-24 00:25:25 +01:00
item_x , self.position . y ,
2022-02-28 22:56:17 +01:00
item.w , self.size . y , item_bg
2022-02-23 10:15:14 +01:00
)
end
if item.on_draw then
2022-02-24 00:25:25 +01:00
core.push_clip_rect ( item_x , self.position . y , item.w , self.size . y )
2022-02-28 22:56:17 +01:00
item.on_draw ( item_x , self.position . y , self.size . y , hovered )
2022-02-23 10:15:14 +01:00
core.pop_clip_rect ( )
else
2022-02-24 00:25:25 +01:00
self : draw_items ( item.cached_item , false , item_x - style.padding . x )
2022-02-23 10:15:14 +01:00
end
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
2022-02-21 08:40:25 +01:00
end
2021-02-21 11:09:51 +01:00
end
2019-12-28 12:16:32 +01:00
end
return StatusView