collision/screen.lua

358 lines
9.8 KiB
Lua
Raw Normal View History

2015-02-15 22:19:17 +01:00
local capi = {screen=screen,client=client,mouse=mouse, keygrabber = keygrabber}
local math = math
local wibox = require( "wibox" )
local awful = require( "awful" )
local beautiful = require( "beautiful" )
local surface = require( "gears.surface" )
local pango = require( "lgi" ).Pango
local pangocairo = require( "lgi" ).PangoCairo
local mouse = require( "collision.mouse" )
local util = require( "collision.util" )
local shape = require( "gears.shape" )
local scale = pango.SCALE
2014-10-16 05:50:58 +02:00
local module = {}
2014-10-16 05:50:58 +02:00
local wiboxes = {}
local wiboxes_s = setmetatable({},{__mode="k"})
local bgs = {}
local size = 75
local pss = capi.mouse.screen
2014-10-16 05:50:58 +02:00
-- Keep an index of the last selection client for each screen
local last_clients = setmetatable({},{__mode="kv"})
local last_clients_coords = {}
local screens,screens_inv = util.get_ordered_screens()
2015-02-15 07:57:40 +01:00
2015-02-15 22:19:17 +01:00
local function current_screen(focus)
return (not focus) and capi.mouse.screen or (capi.client.focus and capi.client.focus.screen or capi.mouse.screen)
2015-02-15 07:57:40 +01:00
end
2014-10-16 05:50:58 +02:00
local function fit(self,_,width,height)
-- Compute the optimal font size
local padding = self._private.padding or 10
local tm = self._private.top_margin or 0
height = math.max(0, height - tm)
local min = math.min(width, height)
local font_size = math.max(0, min - 2*padding)
self._private.desc:set_size( font_size * scale )
self._private.layout:set_font_description(self._private.desc)
local _, geo = self._private.layout:get_pixel_extents()
-- The extra "min" is in case a font "lie" and is too big.
return math.min(geo.x+geo.width, min), math.min(geo.y+geo.height, min) + tm
2014-10-16 05:50:58 +02:00
end
local function draw(self, _, cr, width, height)
-- Some layouts never call fit()...
fit(self, context, width, height)
local _, geo = self._private.layout:get_pixel_extents()
local tm = self._private.top_margin or 0
local sz = math.min(width, height - tm)
-- Translate the canvas to center the text
local dy = ( sz - geo.height )/2 - geo.y + tm
local dx = ( sz - geo.width )/2 - geo.x
cr:move_to(dx, dy)
-- Show the text
cr:show_layout(self._private.layout)
end
local function set_screen(self, s)
self._private.layout.text = tostring(s.index)
self:emit_signal("widget::layout_changed")
end
local function set_padding(self, padding)
self._private.padding = padding
self:emit_signal("widget::layout_changed")
end
local function set_top_margin(margin)
self._private.top_margin = margin or 0
self:emit_signal("widget::layout_changed")
end
local function new_constrained_text(s)
local wdg = wibox.widget.base.empty_widget()
-- Add the basic functions
rawset(wdg, "draw" , draw )
rawset(wdg, "fit" , fit )
rawset(wdg, "set_screen" , set_screen )
rawset(wdg, "set_padding" , set_padding )
rawset(wdg, "set_top_margin" , set_top_margin )
-- Create the pango objects
local pango_crx = pangocairo.font_map_get_default():create_context()
local pango_l = pango.Layout.new(pango_crx)
local desc = pango.FontDescription()
desc:set_family( "Verdana" )
desc:set_weight( pango.Weight.BOLD )
wdg._private.layout = pango_l
wdg._private.desc = desc
wdg:emit_signal("widget::layout_changed")
if s then set_screen(wdg, s) end
return wdg
end
local function create_wibox(s)
s = capi.screen[s]
if wiboxes_s[s] then return wiboxes_s[s] end
local wa = s.workarea
-- Create a round wibox
local w = wibox {
width = size,
height = size,
x = math.floor(wa.x+wa.width /2-size/2),
y = math.floor(wa.y+wa.height/2-size/2),
ontop = true
}
-- Theme options
local sh = beautiful.collision_screen_shape or shape.circle
local bw = beautiful.collision_screen_border_width
local bc = beautiful.collision_screen_border_color
local padding = beautiful.collision_screen_padding or 10
local bg = beautiful.collision_screen_bg or beautiful.bg_alternate or "#ff0000"
local fg = beautiful.collision_screen_fg or beautiful.fg_normal or "#0000ff"
local bg_focus = beautiful.collision_screen_bg_focus or beautiful.bg_urgent or "#ff0000"
local fg_focus = beautiful.collision_screen_fg_focus or beautiful.fg_urgent or "#ff0000"
-- Setup the widgets
w:setup {
{
nil,
{
nil,
{
screen = s,
padding = padding,
widget = new_constrained_text
},
nil,
layout = wibox.layout.align.vertical
},
nil,
layout = wibox.layout.align.horizontal
},
bg = bg,
shape = sh or shape.circle,
shape_border_width = bw,
shape_border_color = bc,
id = "main_background",
widget = wibox.container.background
}
-- Set the wibox shape
surface.apply_shape_bounding(w, sh)
wiboxes_s[s] = w
wiboxes[s.index] = w --DEPRECATED
bgs[s] = w:get_children_by_id("main_background")[1]
return w
end
-- Hopefully, the wiboxes will be gargabe collected
local init = false
local function init_wiboxes()
if init then return end
awful.screen.connect_for_each_screen(function(s)
create_wibox(s)
end)
init = true
return true
2014-10-16 05:50:58 +02:00
end
2015-02-15 22:19:17 +01:00
local function select_screen(scr_index,move,old_screen)
if capi.screen[scr_index] ~= capi.screen[old_screen or 1] then
local c = last_clients[capi.screen[scr_index]]
2017-05-29 04:16:25 +02:00
-- If the client is leaked elsewhere, prevent an error message
if c and not pcall(function() return c.valid end) and not c.valid then
last_clients[capi.screen[scr_index]] = nil
2015-03-29 05:49:48 +02:00
c = nil
end
2017-05-29 04:16:25 +02:00
if c and c.valid and c:isvisible() then
local geom = c:geometry()
if last_clients_coords[scr_index] and last_clients_coords[scr_index].client == c then
capi.mouse.coords(last_clients_coords[scr_index])
else
capi.mouse.coords({x=geom.x+geom.width/2,y=geom.y+geom.height/2+55})
end
else
local geom = capi.screen[scr_index].geometry
capi.mouse.coords({x=geom.x+geom.width/2,y=geom.y+geom.height/2+55})
end
2017-05-29 04:16:25 +02:00
mouse.highlight()
end
2014-11-07 05:46:40 +01:00
if move then
2017-05-29 04:16:25 +02:00
local t = capi.screen[scr_index].selected_tag
if t then
t.screen = old_screen
awful.tag.viewonly(t)
end
2014-11-07 05:46:40 +01:00
else
2017-05-29 04:16:25 +02:00
local c = capi.mouse.current_client
2014-11-07 05:46:40 +01:00
if c then
capi.client.focus = c
end
end
2014-10-16 05:50:58 +02:00
return capi.screen[scr_index]
2014-10-16 05:50:58 +02:00
end
local function in_rect(c,point)
if not c then return true end
local geo = c:geometry()
return (
geo.x < point.x and geo.y < point.y and
geo.x + geo.width > point.x and geo.y + geo.height > point.y
)
end
local function save_cursor_position()
local coords = capi.mouse.coords()
local c = capi.client.focus
-- Be sure that that mouse in inside of the selected client before doing that
if c and in_rect(c,coords) then
last_clients_coords[c.screen] = {
client = c,
x = coords.x,
y = coords.y,
}
else
last_clients_coords[capi.mouse.screen] = nil
end
end
2015-02-15 05:50:21 +01:00
local function next_screen(ss,dir,move)
if capi.screen.count() == 1 then return 1 end
local scr_index = capi.screen[screens_inv[ss]].index
2015-02-15 05:50:21 +01:00
if type(scr_index) == "screen" then
scr_index = scr_index.index
end
2015-02-15 05:50:21 +01:00
if dir == "left" then
scr_index = scr_index == 1 and #screens or scr_index - 1
elseif dir == "right" then
scr_index = scr_index == #screens and 1 or scr_index+1
end
return select_screen(screens_inv[capi.screen[scr_index]],move,ss)
2015-02-15 05:50:21 +01:00
end
2014-11-07 05:46:40 +01:00
function module.display(_,dir,move)
2014-10-16 05:50:58 +02:00
if #wiboxes == 0 then
init_wiboxes()
2014-10-16 05:50:58 +02:00
end
save_cursor_position()
module.reload(nil,dir)
local ss = current_screen(move)
2014-11-07 05:46:40 +01:00
next_screen(ss,dir,move)
module.reload(nil,dir)
end
2015-02-15 22:19:17 +01:00
local function highlight_screen(ss)
2017-05-29 04:16:25 +02:00
ss = capi.screen[ss]
2015-02-15 22:19:17 +01:00
if pss ~= ss then
local bg = beautiful.collision_screen_bg or beautiful.bg_alternate or "#ff0000"
local fg = beautiful.collision_screen_fg or beautiful.fg_normal or "#0000ff"
local bg_focus = beautiful.collision_screen_bg_focus or beautiful.bg_urgent or "#ff0000"
local fg_focus = beautiful.collision_screen_fg_focus or beautiful.fg_urgent or "#ff0000"
2017-05-29 04:16:25 +02:00
if pss then
bgs[pss].bg = bg
bgs[pss].fg = fg
end
2015-02-15 22:19:17 +01:00
pss = ss
bgs[ss].bg = bg_focus
bgs[ss].fg = fg_focus
2015-02-15 22:19:17 +01:00
end
end
function module.hide()
2014-10-16 05:50:58 +02:00
if #wiboxes == 0 then return end
for s=1, capi.screen.count() do
wiboxes[s].visible = false
end
mouse.hide()
end
2015-02-15 22:19:17 +01:00
local function show()
for s=1, capi.screen.count() do
wiboxes[s].visible = true
end
end
function module.reload(mod,dir,_,_,move)
2015-02-15 22:19:17 +01:00
local ss = current_screen(move)
2014-10-16 05:50:58 +02:00
if dir then
2014-11-07 05:46:40 +01:00
ss = next_screen(ss,dir:lower(),move or (mod and #mod == 4))
2014-10-16 05:50:58 +02:00
end
2015-02-15 22:19:17 +01:00
highlight_screen(ss)
show()
2014-10-16 05:50:58 +02:00
return true
end
2015-02-15 05:50:21 +01:00
function module.select_screen(idx)
save_cursor_position()
select_screen(screens_inv[idx],false)
if #wiboxes == 0 then
init_wiboxes()
end
2015-02-15 22:19:17 +01:00
highlight_screen(screens_inv[idx])
2015-02-15 22:19:17 +01:00
show()
capi.keygrabber.run(function(_, _, event)
if event == "release" then
module.hide()
mouse.hide()
capi.keygrabber.stop()
return false
end
return true
end)
2015-02-15 05:50:21 +01:00
end
2017-05-29 04:16:25 +02:00
-- Make sure this keeps working when a new screen is added
awful.screen.connect_for_each_screen(function(s)
if next(wiboxes) then
create_wibox(s)
end
end)
capi.client.connect_signal("focus",function(c)
last_clients[c.screen] = c
end)
return module
-- kate: space-indent on; indent-width 4; replace-tabs on;