Skip to content

Commit

Permalink
feat: support aligning window controls to the left (#894)
Browse files Browse the repository at this point in the history
  • Loading branch information
christoph-heinrich authored May 4, 2024
1 parent a713633 commit 7f03046
Show file tree
Hide file tree
Showing 3 changed files with 64 additions and 46 deletions.
3 changes: 2 additions & 1 deletion src/uosc.conf
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,8 @@ menu_type_to_search=yes
# Can be: never, no-border, always
top_bar=no-border
top_bar_size=40
top_bar_controls=yes
# Can be: `no` (hide), left or right
top_bar_controls=right
# Can be: `no` (hide), `yes` (inherit title from mpv.conf), or a custom template string
top_bar_title=yes
# Template string to enable alternative top bar title. If alt title matches main title,
Expand Down
102 changes: 58 additions & 44 deletions src/uosc/elements/TopBar.lua
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ function TopBar:new() return Class.new(self) --[[@as TopBar]] end
function TopBar:init()
Element.init(self, 'top_bar', {render_order = 4})
self.size = 0
self.icon_size, self.font_size, self.title_bx, self.title_by = 1, 1, 1, 1
self.icon_size, self.font_size, self.title_by = 1, 1, 1
self.show_alt_title = false
self.main_title, self.alt_title = nil, nil

Expand All @@ -62,24 +62,22 @@ function TopBar:init()
return state.fullormaxed and 'set fullscreen no;set window-maximized no' or 'set window-maximized yes'
end

-- Order aligns from right to left
self.buttons = {
TopBarButton:new('tb_close', {
icon = 'close', background = '2311e8', command = 'quit', render_order = self.render_order,
}),
TopBarButton:new('tb_max', {
icon = 'crop_square',
background = '222222',
command = get_maximized_command,
render_order = self.render_order,
}),
TopBarButton:new('tb_min', {
icon = 'minimize',
background = '222222',
command = 'cycle window-minimized',
render_order = self.render_order,
}),
}
local close = TopBarButton:new('tb_close', {
icon = 'close', background = '2311e8', command = 'quit', render_order = self.render_order,
})
local max = TopBarButton:new('tb_max', {
icon = 'crop_square',
background = '222222',
command = get_maximized_command,
render_order = self.render_order,
})
local min = TopBarButton:new('tb_min', {
icon = 'minimize',
background = '222222',
command = 'cycle window-minimized',
render_order = self.render_order,
})
self.buttons = options.top_bar_controls == 'left' and {min, max, close} or {close, max, min}

self:decide_titles()
self:decide_enabled()
Expand All @@ -99,7 +97,7 @@ function TopBar:decide_enabled()
end
self.enabled = self.enabled and (options.top_bar_controls or options.top_bar_title ~= 'no' or state.has_playlist)
for _, element in ipairs(self.buttons) do
element.enabled = self.enabled and options.top_bar_controls
element.enabled = self.enabled and options.top_bar_controls ~= nil
end
end

Expand Down Expand Up @@ -139,13 +137,12 @@ function TopBar:update_dimensions()
self.font_size = math.floor((self.size - (math.ceil(self.size * 0.25) * 2)) * options.font_scale)
self.button_width = round(self.size * 1.15)
local window_border_size = Elements:v('window_border', 'size', 0)
self.ax = window_border_size
self.ay = window_border_size
self.bx = display.width - window_border_size
self.by = self.size + window_border_size
self.title_bx = self.bx - (options.top_bar_controls and (self.button_width * 3) or 0)
self.ax = (options.top_bar_title ~= 'no' or state.has_playlist) and window_border_size or self.title_bx

local button_bx = self.bx
local button_bx = options.top_bar_controls == 'left' and self.button_width * 3 or self.bx
for _, element in pairs(self.buttons) do
element.ax, element.bx = button_bx - self.button_width, button_bx
element.ay, element.by = self.ay, self.by
Expand Down Expand Up @@ -204,20 +201,24 @@ function TopBar:render()
local bg_margin = math.floor((self.size - self.font_size) / 4)
local padding = self.font_size / 2
local spacing = 1
local title_ax = self.ax + bg_margin
local left_aligned = options.top_bar_controls == 'left'
local controls_width = options.top_bar_controls and self.button_width * 3 or 0
local title_ax = self.ax + (left_aligned and controls_width or 0) + bg_margin
local title_bx = self.bx - (left_aligned and 0 or controls_width) - bg_margin
local title_ay = self.ay + bg_margin
local max_bx = self.title_bx - bg_margin

-- Playlist position
if state.has_playlist then
local text = state.playlist_pos .. '' .. state.playlist_count
local formatted_text = '{\\b1}' .. state.playlist_pos .. '{\\b0\\fs' .. self.font_size * 0.9 .. '}/'
.. state.playlist_count
local opts = {size = self.font_size, wrap = 2, color = fgt, opacity = visibility}
local rect_width = round(text_width(text, opts) + padding * 2)
local ax = left_aligned and title_bx - rect_width or title_ax
local rect = {
ax = title_ax,
ax = ax,
ay = title_ay,
bx = round(title_ax + text_width(text, opts) + padding * 2),
bx = ax + rect_width,
by = self.by - bg_margin,
}
local opacity = get_point_to_rectangle_proximity(cursor, rect) == 0
Expand All @@ -228,14 +229,14 @@ function TopBar:render()
})
end
ass:txt(rect.ax + (rect.bx - rect.ax) / 2, rect.ay + (rect.by - rect.ay) / 2, 5, formatted_text, opts)
title_ax = rect.bx + bg_margin
if left_aligned then title_bx = rect.ax - bg_margin else title_ax = rect.bx + bg_margin end

-- Click action
cursor:zone('primary_click', rect, function() mp.command('script-binding uosc/playlist') end)
end

-- Skip rendering titles if there's not enough horizontal space
if max_bx - title_ax > self.font_size * 3 and options.top_bar_title ~= 'no' then
if title_bx - title_ax > self.font_size * 3 and options.top_bar_title ~= 'no' then
-- Main title
local main_title = self.show_alt_title and self.alt_title or self.main_title
if main_title then
Expand All @@ -246,11 +247,13 @@ function TopBar:render()
opacity = visibility,
border = options.text_border * state.scale,
border_color = bg,
clip = string.format('\\clip(%d, %d, %d, %d)', self.ax, self.ay, max_bx, self.by),
clip = string.format('\\clip(%d, %d, %d, %d)', self.ax, self.ay, title_bx, self.by),
}
local bx = round(math.min(max_bx, title_ax + text_width(main_title, opts) + padding * 2))
local rect_ideal_width = round(text_width(main_title, opts) + padding * 2)
local rect_width = math.min(rect_ideal_width, title_bx - title_ax)
local ax = left_aligned and title_bx - rect_width or title_ax
local by = self.by - bg_margin
local title_rect = {ax = title_ax, ay = title_ay, bx = bx, by = by}
local title_rect = {ax = ax, ay = title_ay, bx = ax + rect_width, by = by}

if options.top_bar_alt_title_place == 'toggle' then
cursor:zone('primary_click', title_rect, function() self:toggle_title() end)
Expand All @@ -259,7 +262,9 @@ function TopBar:render()
ass:rect(title_rect.ax, title_rect.ay, title_rect.bx, title_rect.by, {
color = bg, opacity = visibility * config.opacity.title, radius = state.radius,
})
ass:txt(title_ax + padding, self.ay + (self.size / 2), 4, main_title, opts)
local align = left_aligned and rect_ideal_width == rect_width and 6 or 4
local x = align == 6 and title_rect.bx - padding or ax + padding
ass:txt(x, self.ay + (self.size / 2), align, main_title, opts)
title_ay = by + spacing
end

Expand All @@ -276,12 +281,17 @@ function TopBar:render()
border_color = bg,
opacity = visibility,
}
local bx = round(math.min(max_bx, title_ax + text_width(self.alt_title, opts) + padding * 2))
local rect_ideal_width = round(text_width(self.alt_title, opts) + padding * 2)
local rect_width = math.min(rect_ideal_width, title_bx - title_ax)
local ax = left_aligned and title_bx - rect_width or title_ax
local bx = ax + rect_width
opts.clip = string.format('\\clip(%d, %d, %d, %d)', title_ax, title_ay, bx, by)
ass:rect(title_ax, title_ay, bx, by, {
ass:rect(ax, title_ay, bx, by, {
color = bg, opacity = visibility * config.opacity.title, radius = state.radius,
})
ass:txt(title_ax + padding, title_ay + height / 2, 4, self.alt_title, opts)
local align = left_aligned and rect_ideal_width == rect_width and 6 or 4
local x = align == 6 and bx - padding or ax + padding
ass:txt(x, title_ay + height / 2, align, self.alt_title, opts)
title_ay = by + spacing
end

Expand All @@ -290,7 +300,8 @@ function TopBar:render()
local padding_half = round(padding / 2)
local font_size = self.font_size * 0.8
local height = font_size * 1.3
local text = '' .. state.current_chapter.index .. ': ' .. state.current_chapter.title
local prefix, postfix = left_aligned and '' or '', left_aligned and '' or ''
local text = prefix .. state.current_chapter.index .. ': ' .. state.current_chapter.title .. postfix
local next_chapter = state.chapters[state.current_chapter.index + 1]
local chapter_end = next_chapter and next_chapter.time or state.duration or 0
local remaining_time = ((state.time or 0) - chapter_end) /
Expand All @@ -309,26 +320,29 @@ function TopBar:render()
local remaining_box_width = remaining_width + padding_half * 2

-- Title
local max_bx = title_bx - remaining_box_width - spacing
local rect_ideal_width = round(text_width(text, opts) + padding * 2)
local rect_width = math.min(rect_ideal_width, max_bx - title_ax)
local ax = left_aligned and title_bx - rect_width or title_ax
local rect = {
ax = title_ax,
ax = ax,
ay = title_ay,
bx = round(math.min(
max_bx - remaining_box_width - spacing,
title_ax + text_width(text, opts) + padding * 2
)),
bx = ax + rect_width,
by = title_ay + height,
}
opts.clip = string.format('\\clip(%d, %d, %d, %d)', title_ax, title_ay, rect.bx, rect.by)
ass:rect(rect.ax, rect.ay, rect.bx, rect.by, {
color = bg, opacity = visibility * config.opacity.title, radius = state.radius,
})
ass:txt(rect.ax + padding, rect.ay + height / 2, 4, text, opts)
local align = left_aligned and rect_ideal_width == rect_width and 6 or 4
local x = align == 6 and rect.bx - padding or rect.ax + padding
ass:txt(x, rect.ay + height / 2, align, text, opts)

-- Click action
cursor:zone('primary_click', rect, function() mp.command('script-binding uosc/chapters') end)

-- Time
rect.ax = rect.bx + spacing
rect.ax = left_aligned and rect.ax - spacing - remaining_box_width or rect.bx + spacing
rect.bx = rect.ax + remaining_box_width
opts.clip = nil
ass:rect(rect.ax, rect.ay, rect.bx, rect.by, {
Expand Down
5 changes: 4 additions & 1 deletion src/uosc/main.lua
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ defaults = {
top_bar = 'no-border',
top_bar_size = 40,
top_bar_persistency = '',
top_bar_controls = true,
top_bar_controls = 'right',
top_bar_title = 'yes',
top_bar_alt_title = '',
top_bar_alt_title_place = 'below',
Expand Down Expand Up @@ -126,6 +126,9 @@ if options.total_time and options.destination_time == 'playtime-remaining' then
elseif not itable_index_of({'total', 'playtime-remaining', 'time-remaining'}, options.destination_time) then
options.destination_time = 'playtime-remaining'
end
if not itable_index_of({'left', 'right'}, options.top_bar_controls) then
options.top_bar_controls = options.top_bar_controls == 'yes' and 'right' or nil
end
-- Ensure required environment configuration
if options.autoload then mp.commandv('set', 'keep-open-pause', 'no') end

Expand Down

0 comments on commit 7f03046

Please sign in to comment.