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-02-21 08:40:25 +01:00
---@alias StatusView.styledtext table<integer, renderer.font|renderer.color|string>
---A status bar implementation for lite, check core.status_view.
---@class StatusView : View
2022-02-23 10:15:14 +01:00
---@field private items StatusView.Item[]
---@field private active_items StatusView.Item[]
---@field private hovered_item StatusView.Item
2022-02-21 08:40:25 +01:00
---@field private message_timeout number
---@field private message StatusView.styledtext
---@field private tooltip_mode boolean
---@field private tooltip 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-02-23 10:15:14 +01:00
---@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
2022-02-28 22:56:17 +01:00
---@alias StatusView.Item.ondraw fun(x, y, h, hovered: boolean, calc_only: boolean):number
2022-02-23 10:15:14 +01:00
---@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
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
---@field separator StatusView.Item.separator
---@field private active boolean
---@field private x number
---@field private w number
---@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
2022-02-28 22:56:17 +01:00
self.background_color_hover = nil
2022-02-23 10:15:14 +01:00
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
---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 )
2022-05-13 04:15:29 +02:00
self.predicate = command.generate_predicate ( predicate )
2022-02-23 10:15:14 +01:00
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
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
self : add_item (
predicate_docview ,
" doc:file " ,
StatusView.Item . LEFT ,
function ( )
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
)
self : add_item (
predicate_docview ,
" doc:position " ,
StatusView.Item . LEFT ,
function ( )
local dv = core.active_view
local line , col = dv.doc : get_selection ( )
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 ,
" doc:go-to-line "
) . tooltip = " line : column "
2022-02-21 08:40:25 +01:00
self : add_item (
2022-02-23 10:15:14 +01:00
predicate_docview ,
" doc:indentation " ,
StatusView.Item . RIGHT ,
2022-02-21 08:40:25 +01:00
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-02-23 10:15:14 +01:00
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 " ,
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
}
end
) . separator = self.separator2
self : add_item (
predicate_docview ,
" doc:line-ending " ,
StatusView.Item . RIGHT ,
function ( )
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 ,
" doc:toggle-line-ending "
2022-02-21 08:40:25 +01:00
)
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
self : add_item (
" core.commandview " ,
" command:files " ,
StatusView.Item . RIGHT ,
function ( )
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
)
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.
---@param self StatusView
---@param position integer
---@param alignment StatusView.Item.alignment
---@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-02-23 10:15:14 +01:00
---@param predicate string | table | StatusView.Item.predicate :
---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 name string A unique name to identify the item on the status bar.
---@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.
2022-02-21 08:40:25 +01:00
---@param pos? integer :
---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.
---@param tooltip? string Displayed when mouse hovers the item
2022-02-23 10:15:14 +01:00
---@return StatusView.Item
function StatusView : add_item ( predicate , name , alignment , getitem , command , pos , tooltip )
assert ( self : get_item ( name ) == nil , " status item already exists: " .. name )
---@type StatusView.Item
local item = StatusView.Item ( predicate , name , alignment , command , tooltip )
item.get_item = getitem
pos = type ( pos ) == " nil " and - 1 or tonumber ( pos )
2022-03-21 23:40:14 +01:00
table.insert ( self.items , normalize_position ( self , pos , 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
---@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
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
---@param alignment? StatusView.Item.alignment
---@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
---@return StatusView.Item removed_item
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
---text until StatusView:remove_tooltip() is called.
---@param text string | 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.
---@param self StatusView
---@param items StatusView.styledtext
---@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.
---@param items 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-02-23 10:15:14 +01:00
---@param item 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
core.error (
" 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.
---@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
2022-02-23 10:15:14 +01:00
---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 (
2022-05-13 04:15:29 +02:00
nil ,
2022-02-23 10:15:14 +01:00
" deprecated: " .. position .. " -start " ,
alignment
)
item_start.get_item = items_start
local item_end = StatusView.Item (
2022-05-13 04:15:29 +02:00
nil ,
2022-02-23 10:15:14 +01:00
" deprecated: " .. position .. " -end " ,
alignment
)
item_end.get_item = items_end
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-02-23 10:15:14 +01:00
---@param self StatusView
2022-02-24 07:57:39 +01:00
---@param destination StatusView.Item[]
2022-02-23 10:15:14 +01:00
---@param separator string
2022-02-24 07:57:39 +01:00
---@param alignment StatusView.Item.alignment
---@return StatusView.Item
local function add_spacing ( self , destination , separator , alignment , x )
---@type StatusView.Item
local space = StatusView.Item ( nil , " space " , alignment )
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-02-23 10:15:14 +01:00
---@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
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 left , right = { } , { }
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-02-23 10:15:14 +01:00
---@type StatusView.Item[]
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 = { }
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 )
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-02-24 07:23:29 +01:00
---@param item StatusView.Item
---@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.
---@param self StatusView
---@param item StatusView.Item
---@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 )
elseif self.tooltip_mode then
2021-02-21 11:09:51 +01:00
self : draw_items ( self.tooltip )
else
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-02-23 10:15:14 +01:00
if item.alignment == StatusView.Item . LEFT 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