Merge pull request #844 from Elv13/geometry_overhaul_p2
Geometry overhaul part 2.5
This commit is contained in:
commit
dbd0931343
|
@ -0,0 +1,6 @@
|
|||
---------------------------------------------------------------------------
|
||||
--- This module is deprecated, use `mouse`
|
||||
-- ===============================
|
||||
--
|
||||
-- @module awful.mouse
|
||||
---------------------------------------------------------------------------
|
|
@ -35,6 +35,8 @@ new_type("function", "Functions")
|
|||
new_type("property", "Object properties", false, "Type")
|
||||
-- New type for signals
|
||||
new_type("signal", "Signals", false, "Arguments")
|
||||
-- New type for signals connections
|
||||
new_type("signalhandler", "Request handlers", false, "Arguments")
|
||||
-- Allow objects to define a set of beautiful properties affecting them
|
||||
new_type("beautiful", "Theme variables", false, "Type")
|
||||
-- Put deprecated methods in their own section
|
||||
|
@ -67,6 +69,7 @@ file = {
|
|||
'../docs/aliases/awful_client.lua',
|
||||
'../docs/aliases/awful_screen.lua',
|
||||
'../docs/aliases/awful_tag.lua',
|
||||
'../docs/aliases/awful_mouse.lua',
|
||||
exclude = {
|
||||
-- exclude these modules, as they do not contain any written
|
||||
-- documentation
|
||||
|
|
|
@ -0,0 +1,253 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="154.33519mm"
|
||||
height="83.532181mm"
|
||||
viewBox="0 0 546.85725 295.98017"
|
||||
id="svg2"
|
||||
version="1.1"
|
||||
inkscape:version="0.91 r13725"
|
||||
sodipodi:docname="mouse.svg">
|
||||
<defs
|
||||
id="defs4" />
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="1.4"
|
||||
inkscape:cx="360.06759"
|
||||
inkscape:cy="366.74179"
|
||||
inkscape:document-units="pt"
|
||||
inkscape:current-layer="g5476"
|
||||
showgrid="false"
|
||||
showguides="false"
|
||||
inkscape:guide-bbox="true"
|
||||
inkscape:window-width="1916"
|
||||
inkscape:window-height="1021"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="16"
|
||||
inkscape:window-maximized="1"
|
||||
fit-margin-top="0"
|
||||
fit-margin-left="0"
|
||||
fit-margin-right="0"
|
||||
fit-margin-bottom="0" />
|
||||
<metadata
|
||||
id="metadata7">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(738.15802,-608.17116)">
|
||||
<g
|
||||
id="g5476"
|
||||
transform="translate(173.74631,0)">
|
||||
<path
|
||||
sodipodi:nodetypes="cccccccccc"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path4136"
|
||||
d="m -728.15879,608.42602 178.5142,0 c 9.38939,0.26627 10.95278,7.73823 10.83892,14.69669 l 0,230.46648 c 0.30959,23.52499 -24.37716,50.18749 -47.93254,50.01303 l -104.98806,0.29423 c -20.09841,0.0248 -45.84989,-25.43284 -46.17689,-51.01859 l 0.0204,-227.59279 c 0.18644,-8.43842 1.99483,-16.50279 9.72396,-16.85905 z"
|
||||
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.50971645;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
|
||||
<path
|
||||
sodipodi:nodetypes="cc"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path4138"
|
||||
d="m -733.20425,673.55031 189.76854,0"
|
||||
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.87379962;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
|
||||
<path
|
||||
sodipodi:nodetypes="cccccccccccc"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path4745"
|
||||
d="m -646.69902,611.50192 -79.00757,0 c -5.32157,0.16679 -6.94386,4.08162 -6.91732,11.98114 l 0,45.43998 85.97537,0 0,-7.07362 -5.78092,0 c -1.41482,0 -2.54544,-1.13579 -2.54098,-2.93674 l 0,-36.99862 c 0.0228,-1.71464 0.26476,-2.99382 2.39387,-2.99382 l 5.85448,0 z"
|
||||
style="fill:#617fff;fill-opacity:0.49019608;fill-rule:evenodd;stroke:#617fff;stroke-width:1.63500068;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
|
||||
<path
|
||||
style="fill:#617fff;fill-opacity:0.49019608;fill-rule:evenodd;stroke:#617fff;stroke-width:1.63500068;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m -630.47815,611.50192 79.00757,0 c 5.32158,0.16679 6.94386,4.08162 6.91732,11.98114 l 0,45.43998 -85.97537,0 0,-7.07362 5.78092,0 c 1.41482,0 2.54544,-1.13579 2.54098,-2.93674 l 0,-36.99862 c -0.0228,-1.71464 -0.26476,-2.99382 -2.39387,-2.99382 l -5.85448,0 z"
|
||||
id="path4766"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="cccccccccccc" />
|
||||
<rect
|
||||
ry="7.3555903"
|
||||
rx="7.3555903"
|
||||
y="620.00159"
|
||||
x="-645.80945"
|
||||
height="39.42598"
|
||||
width="15.593853"
|
||||
id="rect4768"
|
||||
style="opacity:1;fill:#617fff;fill-opacity:0.49019608;stroke:#617fff;stroke-width:1.63500068;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m -644.91259,651.46192 -6.56599,0.12226 0,106.78495 6.87951,-0.023"
|
||||
id="path8458"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="cccc" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-style:normal;font-weight:normal;font-size:14.765769px;line-height:125%;font-family:sans-serif;text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
x="-703.82648"
|
||||
y="718.50195"
|
||||
id="text8466"
|
||||
sodipodi:linespacing="125%"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan8468"
|
||||
x="-703.82648"
|
||||
y="718.50195">Button 1</tspan><tspan
|
||||
id="tspan5409"
|
||||
sodipodi:role="line"
|
||||
x="-703.82648"
|
||||
y="736.95917">(LMB)</tspan></text>
|
||||
<text
|
||||
sodipodi:linespacing="125%"
|
||||
id="text5405"
|
||||
y="717.49182"
|
||||
x="-582.11389"
|
||||
style="font-style:normal;font-weight:normal;font-size:14.765769px;line-height:125%;font-family:sans-serif;text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
xml:space="preserve"><tspan
|
||||
y="717.49182"
|
||||
x="-582.11389"
|
||||
id="tspan5407"
|
||||
sodipodi:role="line">Button 2</tspan><tspan
|
||||
id="tspan5411"
|
||||
y="735.94904"
|
||||
x="-582.11389"
|
||||
sodipodi:role="line">(RMB)</tspan></text>
|
||||
<path
|
||||
sodipodi:nodetypes="cccc"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path5413"
|
||||
d="m -644.66005,640.0977 -12.12183,0.12226 0,139.86744 12.81415,-0.023"
|
||||
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m -644.91259,627.47079 -17.42513,0.12226 0,171.18217 18.36999,0.0243"
|
||||
id="path5415"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="cccc" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-style:normal;font-weight:normal;font-size:14.765769px;line-height:125%;font-family:sans-serif;text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
x="-594.89044"
|
||||
y="785.26086"
|
||||
id="text5417"
|
||||
sodipodi:linespacing="125%"><tspan
|
||||
sodipodi:role="line"
|
||||
x="-594.89044"
|
||||
y="785.26086"
|
||||
id="tspan5421">Click: Button 3</tspan></text>
|
||||
<text
|
||||
sodipodi:linespacing="125%"
|
||||
id="text5425"
|
||||
y="803.3548"
|
||||
x="-592.46796"
|
||||
style="font-style:normal;font-weight:normal;font-size:14.765769px;line-height:125%;font-family:sans-serif;text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
xml:space="preserve"><tspan
|
||||
id="tspan5427"
|
||||
y="803.3548"
|
||||
x="-592.46796"
|
||||
sodipodi:role="line">Down: Button 5</tspan></text>
|
||||
<text
|
||||
sodipodi:linespacing="125%"
|
||||
id="text5429"
|
||||
y="763.70624"
|
||||
x="-601.84076"
|
||||
style="font-style:normal;font-weight:normal;font-size:14.765769px;line-height:125%;font-family:sans-serif;text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
xml:space="preserve"><tspan
|
||||
id="tspan5431"
|
||||
y="763.70624"
|
||||
x="-601.84076"
|
||||
sodipodi:role="line">Up: Button 4</tspan></text>
|
||||
<path
|
||||
sodipodi:nodetypes="cc"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path5433"
|
||||
d="m -705.40715,646.22703 0,58.57067"
|
||||
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m -582.19286,646.22703 0,58.57067"
|
||||
id="path5435"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="cc" />
|
||||
<circle
|
||||
r="2.3214285"
|
||||
cy="703.61224"
|
||||
cx="-705.53571"
|
||||
id="path5437"
|
||||
style="opacity:1;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.75;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
|
||||
<circle
|
||||
style="opacity:1;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.75;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
|
||||
id="circle5439"
|
||||
cx="-705.44647"
|
||||
cy="646.91583"
|
||||
r="2.3214285" />
|
||||
<circle
|
||||
r="2.3214285"
|
||||
cy="646.02295"
|
||||
cx="-582.23218"
|
||||
id="circle5441"
|
||||
style="opacity:1;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.75;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
|
||||
<circle
|
||||
style="opacity:1;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.75;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
|
||||
id="circle5443"
|
||||
cx="-582.05359"
|
||||
cy="704.41583"
|
||||
r="2.3214285" />
|
||||
<circle
|
||||
style="opacity:1;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.75;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
|
||||
id="circle5445"
|
||||
cx="-643.66077"
|
||||
cy="627.63007"
|
||||
r="2.3214285" />
|
||||
<circle
|
||||
r="2.3214285"
|
||||
cy="639.95148"
|
||||
cx="-643.66077"
|
||||
id="circle5447"
|
||||
style="opacity:1;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.75;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
|
||||
<circle
|
||||
style="opacity:1;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.75;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
|
||||
id="circle5449"
|
||||
cx="-643.66077"
|
||||
cy="651.55859"
|
||||
r="2.3214285" />
|
||||
<circle
|
||||
r="2.3214285"
|
||||
cy="758.34442"
|
||||
cx="-645.08929"
|
||||
id="circle5451"
|
||||
style="opacity:1;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.75;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
|
||||
<circle
|
||||
style="opacity:1;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.75;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
|
||||
id="circle5453"
|
||||
cx="-645.08929"
|
||||
cy="779.77301"
|
||||
r="2.3214285" />
|
||||
<circle
|
||||
r="2.3214285"
|
||||
cy="798.88013"
|
||||
cx="-645.08929"
|
||||
id="circle5455"
|
||||
style="opacity:1;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.75;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 12 KiB |
|
@ -15,6 +15,7 @@ local math = math
|
|||
local util = require("awful.util")
|
||||
local aclient = require("awful.client")
|
||||
local aplace = require("awful.placement")
|
||||
local asuit = require("awful.layout.suit")
|
||||
|
||||
local ewmh = {}
|
||||
|
||||
|
@ -100,6 +101,7 @@ end
|
|||
--
|
||||
-- It is the default signal handler for `request::activate` on a `client`.
|
||||
--
|
||||
-- @signalhandler awful.ewmh.activate
|
||||
-- @client c A client to use
|
||||
-- @tparam string context The context where this signal was used.
|
||||
-- @tparam[opt] table hints A table with additional hints:
|
||||
|
@ -120,8 +122,11 @@ function ewmh.activate(c, context, hints) -- luacheck: no unused args
|
|||
end
|
||||
end
|
||||
|
||||
--- Tag a window with its requested tag
|
||||
--- Tag a window with its requested tag.
|
||||
--
|
||||
-- It is the default signal handler for `request::tag` on a `client`.
|
||||
--
|
||||
-- @signalhandler awful.ewmh.tag
|
||||
-- @client c A client to tag
|
||||
-- @tag[opt] t A tag to use. If omitted, then the client is made sticky.
|
||||
-- @tparam[opt={}] table hints Extra information
|
||||
|
@ -156,10 +161,18 @@ local context_mapper = {
|
|||
--
|
||||
-- This is the default geometry request handler.
|
||||
--
|
||||
-- @signalhandler awful.ewmh.geometry
|
||||
-- @tparam client c The client
|
||||
-- @tparam string context The context
|
||||
-- @tparam[opt={}] table hints The hints to pass to the handler
|
||||
function ewmh.geometry(c, context, hints)
|
||||
local layout = c.screen.selected_tag and c.screen.selected_tag.layout or nil
|
||||
|
||||
-- Setting the geometry wont work unless the client is floating.
|
||||
if (not c.floating) and (not layout == asuit.floating) then
|
||||
return
|
||||
end
|
||||
|
||||
context = context or ""
|
||||
|
||||
local original_context = context
|
||||
|
|
|
@ -246,6 +246,28 @@ capi.client.connect_signal("list", function()
|
|||
end
|
||||
end)
|
||||
|
||||
--- Default handler for tiled clients request::geometry with the `mouse.move`
|
||||
-- context.
|
||||
-- @tparam client c The client
|
||||
-- @tparam string context The context
|
||||
-- @tparam table hints Additional hints
|
||||
function layout.move_handler(c, context, hints) --luacheck: no unused args
|
||||
-- Quit if it isn't a mouse.move on a tiled layout, that's handled elsewhere
|
||||
if c.floating then return end
|
||||
if context ~= "mouse.move" then return end
|
||||
local l = c.screen.selected_tag and c.screen.selected_tag.layout or nil
|
||||
if l == layout.suit.floating then return end
|
||||
|
||||
local c_u_m = capi.mouse.current_client
|
||||
if c_u_m and not c_u_m.floating then
|
||||
if c_u_m ~= c then
|
||||
c:swap(c_u_m)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
capi.client.connect_signal("request::geometry", layout.move_handler)
|
||||
|
||||
return layout
|
||||
|
||||
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80
|
||||
|
|
|
@ -0,0 +1,59 @@
|
|||
---------------------------------------------------------------------------
|
||||
--- When the the mouse reach the end of the screen, then switch tag instead
|
||||
-- of screens.
|
||||
--
|
||||
-- @author Julien Danjou <julien@danjou.info>
|
||||
-- @copyright 2008 Julien Danjou
|
||||
-- @release @AWESOME_VERSION@
|
||||
-- @submodule mouse
|
||||
---------------------------------------------------------------------------
|
||||
|
||||
local capi = {screen = screen, mouse = mouse}
|
||||
local util = require("awful.util")
|
||||
local tag = require("awful.tag")
|
||||
local resize = require("awful.mouse.resize")
|
||||
|
||||
local module = {}
|
||||
|
||||
function module.drag_to_tag(c)
|
||||
if (not c) or (not c.valid) then return end
|
||||
|
||||
local coords = capi.mouse.coords()
|
||||
|
||||
local dir = nil
|
||||
|
||||
local wa = capi.screen[c.screen].workarea
|
||||
|
||||
if coords.x >= wa.x + wa.width - 1 then
|
||||
capi.mouse.coords({ x = wa.x + 2 }, true)
|
||||
dir = "right"
|
||||
elseif coords.x <= wa.x + 1 then
|
||||
capi.mouse.coords({ x = wa.x + wa.width - 2 }, true)
|
||||
dir = "left"
|
||||
end
|
||||
|
||||
local tags = c.screen.tags
|
||||
local t = c.screen.selected_tag
|
||||
local idx = t.index
|
||||
|
||||
if dir then
|
||||
|
||||
if dir == "right" then
|
||||
local newtag = tags[util.cycle(#tags, idx + 1)]
|
||||
c:move_to_tag(newtag)
|
||||
tag.viewnext()
|
||||
elseif dir == "left" then
|
||||
local newtag = tags[util.cycle(#tags, idx - 1)]
|
||||
c:move_to_tag(newtag)
|
||||
tag.viewprev()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
resize.add_move_callback(function(c, _, _)
|
||||
if module.enabled then
|
||||
module.drag_to_tag(c)
|
||||
end
|
||||
end, "mouse.move")
|
||||
|
||||
return setmetatable(module, {__call = function(_, ...) return module.drag_to_tag(...) end})
|
|
@ -4,17 +4,15 @@
|
|||
-- @author Julien Danjou <julien@danjou.info>
|
||||
-- @copyright 2008 Julien Danjou
|
||||
-- @release @AWESOME_VERSION@
|
||||
-- @module awful.mouse
|
||||
-- @module mouse
|
||||
---------------------------------------------------------------------------
|
||||
|
||||
-- Grab environment we need
|
||||
local layout = require("awful.layout")
|
||||
local tag = require("awful.tag")
|
||||
local aclient = require("awful.client")
|
||||
local aplace = require("awful.placement")
|
||||
local awibox = require("awful.wibox")
|
||||
local util = require("awful.util")
|
||||
local type = type
|
||||
local math = math
|
||||
local ipairs = ipairs
|
||||
local capi =
|
||||
{
|
||||
|
@ -25,136 +23,63 @@ local capi =
|
|||
mousegrabber = mousegrabber,
|
||||
}
|
||||
|
||||
local mouse = {}
|
||||
local mouse = {
|
||||
resize = require("awful.mouse.resize"),
|
||||
snap = require("awful.mouse.snap"),
|
||||
drag_to_tag = require("awful.mouse.drag_to_tag")
|
||||
}
|
||||
|
||||
mouse.object = {}
|
||||
mouse.client = {}
|
||||
mouse.wibox = {}
|
||||
|
||||
--- The default snap distance.
|
||||
-- @tfield integer awful.mouse.snap.default_distance
|
||||
-- @tparam[opt=8] integer default_distance
|
||||
-- @see awful.mouse.snap
|
||||
|
||||
--- Enable screen edges snapping.
|
||||
-- @tfield[opt=true] boolean awful.mouse.snap.edge_enabled
|
||||
|
||||
--- Enable client to client snapping.
|
||||
-- @tfield[opt=true] boolean awful.mouse.snap.client_enabled
|
||||
|
||||
--- Enable changing tag when a client is dragged to the edge of the screen.
|
||||
-- @tfield[opt=false] integer awful.mouse.drag_to_tag.enabled
|
||||
|
||||
--- The snap outline background color.
|
||||
-- @beautiful beautiful.snap_bg
|
||||
-- @tparam color|string|gradient|pattern color
|
||||
|
||||
--- The snap outline width.
|
||||
-- @beautiful beautiful.snap_border_width
|
||||
-- @param integer
|
||||
|
||||
--- The snap outline shape.
|
||||
-- @beautiful beautiful.snap_shape
|
||||
-- @tparam function shape A `gears.shape` compatible function
|
||||
|
||||
--- Get the client object under the pointer.
|
||||
-- @deprecated awful.mouse.client_under_pointer
|
||||
-- @return The client object under the pointer, if one can be found.
|
||||
-- @see current_client
|
||||
function mouse.client_under_pointer()
|
||||
local obj = capi.mouse.object_under_pointer()
|
||||
if type(obj) == "client" then
|
||||
return obj
|
||||
end
|
||||
end
|
||||
util.deprecated("Use mouse.current_client instead of awful.mouse.client_under_pointer()")
|
||||
|
||||
--- Get the drawin object under the pointer.
|
||||
-- @return The drawin object under the pointer, if one can be found.
|
||||
function mouse.drawin_under_pointer()
|
||||
local obj = capi.mouse.object_under_pointer()
|
||||
if type(obj) == "drawin" then
|
||||
return obj
|
||||
end
|
||||
end
|
||||
|
||||
local function snap_outside(g, sg, snap)
|
||||
if g.x < snap + sg.x + sg.width and g.x > sg.x + sg.width then
|
||||
g.x = sg.x + sg.width
|
||||
elseif g.x + g.width < sg.x and g.x + g.width > sg.x - snap then
|
||||
g.x = sg.x - g.width
|
||||
end
|
||||
if g.y < snap + sg.y + sg.height and g.y > sg.y + sg.height then
|
||||
g.y = sg.y + sg.height
|
||||
elseif g.y + g.height < sg.y and g.y + g.height > sg.y - snap then
|
||||
g.y = sg.y - g.height
|
||||
end
|
||||
return g
|
||||
end
|
||||
|
||||
local function snap_inside(g, sg, snap)
|
||||
local edgev = 'none'
|
||||
local edgeh = 'none'
|
||||
if math.abs(g.x) < snap + sg.x and g.x > sg.x then
|
||||
edgev = 'left'
|
||||
g.x = sg.x
|
||||
elseif math.abs((sg.x + sg.width) - (g.x + g.width)) < snap then
|
||||
edgev = 'right'
|
||||
g.x = sg.x + sg.width - g.width
|
||||
end
|
||||
if math.abs(g.y) < snap + sg.y and g.y > sg.y then
|
||||
edgeh = 'top'
|
||||
g.y = sg.y
|
||||
elseif math.abs((sg.y + sg.height) - (g.y + g.height)) < snap then
|
||||
edgeh = 'bottom'
|
||||
g.y = sg.y + sg.height - g.height
|
||||
end
|
||||
|
||||
-- What is the dominant dimension?
|
||||
if g.width > g.height then
|
||||
return g, edgeh
|
||||
else
|
||||
return g, edgev
|
||||
end
|
||||
end
|
||||
|
||||
--- Snap a client to the closest client or screen edge.
|
||||
-- @param c The client to snap.
|
||||
-- @param snap The pixel to snap clients.
|
||||
-- @param x The client x coordinate.
|
||||
-- @param y The client y coordinate.
|
||||
-- @param fixed_x True if the client isn't allowed to move in the x direction.
|
||||
-- @param fixed_y True if the client isn't allowed to move in the y direction.
|
||||
function mouse.client.snap(c, snap, x, y, fixed_x, fixed_y)
|
||||
snap = snap or 8
|
||||
c = c or capi.client.focus
|
||||
local cur_geom = c:geometry()
|
||||
local geom = c:geometry()
|
||||
geom.width = geom.width + (2 * c.border_width)
|
||||
geom.height = geom.height + (2 * c.border_width)
|
||||
local edge
|
||||
geom.x = x or geom.x
|
||||
geom.y = y or geom.y
|
||||
|
||||
geom, edge = snap_inside(geom, capi.screen[c.screen].geometry, snap)
|
||||
geom = snap_inside(geom, capi.screen[c.screen].workarea, snap)
|
||||
|
||||
-- Allow certain windows to snap to the edge of the workarea.
|
||||
-- Only allow docking to workarea for consistency/to avoid problems.
|
||||
if c.dockable then
|
||||
local struts = c:struts()
|
||||
struts['left'] = 0
|
||||
struts['right'] = 0
|
||||
struts['top'] = 0
|
||||
struts['bottom'] = 0
|
||||
if edge ~= "none" and c.floating then
|
||||
if edge == "left" or edge == "right" then
|
||||
struts[edge] = cur_geom.width
|
||||
elseif edge == "top" or edge == "bottom" then
|
||||
struts[edge] = cur_geom.height
|
||||
end
|
||||
end
|
||||
c:struts(struts)
|
||||
end
|
||||
|
||||
geom.x = geom.x - (2 * c.border_width)
|
||||
geom.y = geom.y - (2 * c.border_width)
|
||||
|
||||
for _, snapper in ipairs(aclient.visible(c.screen)) do
|
||||
if snapper ~= c then
|
||||
geom = snap_outside(geom, snapper:geometry(), snap)
|
||||
end
|
||||
end
|
||||
|
||||
geom.width = geom.width - (2 * c.border_width)
|
||||
geom.height = geom.height - (2 * c.border_width)
|
||||
geom.x = geom.x + (2 * c.border_width)
|
||||
geom.y = geom.y + (2 * c.border_width)
|
||||
|
||||
-- It's easiest to undo changes afterwards if they're not allowed
|
||||
if fixed_x then geom.x = cur_geom.x end
|
||||
if fixed_y then geom.y = cur_geom.y end
|
||||
|
||||
return geom
|
||||
return mouse.object.get_current_client()
|
||||
end
|
||||
|
||||
--- Move a client.
|
||||
-- @function awful.mouse.client.move
|
||||
-- @param c The client to move, or the focused one if nil.
|
||||
-- @param snap The pixel to snap clients.
|
||||
-- @param finished_cb An optional callback function, that will be called
|
||||
-- when moving the client has been finished. The client
|
||||
-- that has been moved will be passed to that function.
|
||||
function mouse.client.move(c, snap, finished_cb)
|
||||
-- @param finished_cb Deprecated, do not use
|
||||
function mouse.client.move(c, snap, finished_cb) --luacheck: no unused args
|
||||
if finished_cb then
|
||||
util.deprecated("The mouse.client.move `finished_cb` argument is no longer"..
|
||||
" used, please use awful.mouse.resize.add_leave_callback(f, 'mouse.move')")
|
||||
end
|
||||
|
||||
c = c or capi.client.focus
|
||||
|
||||
if not c
|
||||
|
@ -165,98 +90,39 @@ function mouse.client.move(c, snap, finished_cb)
|
|||
return
|
||||
end
|
||||
|
||||
local orig = c:geometry()
|
||||
local m_c = capi.mouse.coords()
|
||||
local dist_x = m_c.x - orig.x
|
||||
local dist_y = m_c.y - orig.y
|
||||
-- Only allow moving in the non-maximized directions
|
||||
local fixed_x = c.maximized_horizontal
|
||||
local fixed_y = c.maximized_vertical
|
||||
-- Compute the offset
|
||||
local coords = capi.mouse.coords()
|
||||
local geo = aplace.centered(capi.mouse,{parent=c, pretend=true})
|
||||
|
||||
capi.mousegrabber.run(function (_mouse)
|
||||
if not c.valid then return false end
|
||||
local offset = {
|
||||
x = geo.x - coords.x,
|
||||
y = geo.y - coords.y,
|
||||
}
|
||||
|
||||
for _, v in ipairs(_mouse.buttons) do
|
||||
if v then
|
||||
local lay = layout.get(c.screen)
|
||||
if lay == layout.suit.floating or c.floating then
|
||||
local x = _mouse.x - dist_x
|
||||
local y = _mouse.y - dist_y
|
||||
c:geometry(mouse.client.snap(c, snap, x, y, fixed_x, fixed_y))
|
||||
elseif lay ~= layout.suit.magnifier then
|
||||
-- Only move the client to the mouse
|
||||
-- screen if the target screen is not
|
||||
-- floating.
|
||||
-- Otherwise, we move if via geometry.
|
||||
if layout.get(capi.mouse.screen) == layout.suit.floating then
|
||||
local x = _mouse.x - dist_x
|
||||
local y = _mouse.y - dist_y
|
||||
c:geometry(mouse.client.snap(c, snap, x, y, fixed_x, fixed_y))
|
||||
else
|
||||
c.screen = capi.mouse.screen
|
||||
end
|
||||
if layout.get(c.screen) ~= layout.suit.floating then
|
||||
local c_u_m = mouse.client_under_pointer()
|
||||
if c_u_m and not c_u_m.floating then
|
||||
if c_u_m ~= c then
|
||||
c:swap(c_u_m)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
end
|
||||
if finished_cb then
|
||||
finished_cb(c)
|
||||
end
|
||||
return false
|
||||
end, "fleur")
|
||||
mouse.resize(c, "mouse.move", {
|
||||
placement = aplace.under_mouse,
|
||||
offset = offset,
|
||||
snap = snap
|
||||
})
|
||||
end
|
||||
|
||||
mouse.client.dragtotag = { }
|
||||
|
||||
--- Move a client to a tag by dragging it onto the left / right side of the screen
|
||||
--- Move a client to a tag by dragging it onto the left / right side of the screen.
|
||||
-- @deprecated awful.mouse.client.dragtotag.border
|
||||
-- @param c The client to move
|
||||
function mouse.client.dragtotag.border(c)
|
||||
capi.mousegrabber.run(function (_mouse)
|
||||
if not c.valid then return false end
|
||||
util.deprecated("Use awful.mouse.snap.drag_to_tag_enabled = true instead "..
|
||||
"of awful.mouse.client.dragtotag.border(c). It will now be enabled.")
|
||||
|
||||
local button_down = false
|
||||
for _, v in ipairs(_mouse.buttons) do
|
||||
if v then button_down = true end
|
||||
end
|
||||
local wa = capi.screen[c.screen].workarea
|
||||
if _mouse.x >= wa.x + wa.width then
|
||||
capi.mouse.coords({ x = wa.x + wa.width - 1 })
|
||||
elseif _mouse.x <= wa.x then
|
||||
capi.mouse.coords({ x = wa.x + 1 })
|
||||
end
|
||||
if not button_down then
|
||||
local tags = c.screen.tags
|
||||
local t = c.screen.selected_tag
|
||||
local idx
|
||||
for i, v in ipairs(tags) do
|
||||
if v == t then
|
||||
idx = i
|
||||
end
|
||||
end
|
||||
if _mouse.x > wa.x + wa.width - 10 then
|
||||
local newtag = tags[util.cycle(#tags, idx + 1)]
|
||||
c:move_to_tag(newtag)
|
||||
tag.viewnext()
|
||||
elseif _mouse.x < wa.x + 10 then
|
||||
local newtag = tags[util.cycle(#tags, idx - 1)]
|
||||
c:move_to_tag(newtag)
|
||||
tag.viewprev()
|
||||
end
|
||||
return false
|
||||
end
|
||||
return true
|
||||
end, "fleur")
|
||||
-- Enable drag to border
|
||||
mouse.snap.drag_to_tag_enabled = true
|
||||
|
||||
return mouse.client.move(c)
|
||||
end
|
||||
|
||||
--- Move the wibox under the cursor
|
||||
--- Move the wibox under the cursor.
|
||||
-- @function awful.mouse.wibox.move
|
||||
--@param w The wibox to move, or none to use that under the pointer
|
||||
function mouse.wibox.move(w)
|
||||
w = w or mouse.wibox_under_pointer()
|
||||
|
@ -297,55 +163,41 @@ function mouse.wibox.move(w)
|
|||
end
|
||||
|
||||
--- Get a client corner coordinates.
|
||||
-- @param c The client to get corner from, focused one by default.
|
||||
-- @param corner The corner to use: auto, top_left, top_right, bottom_left,
|
||||
-- bottom_right. Default is auto, and auto find the nearest corner.
|
||||
-- @return Actual used corner and x and y coordinates.
|
||||
-- @deprecated awful.mouse.client.corner
|
||||
-- @tparam[opt=client.focus] client c The client to get corner from, focused one by default.
|
||||
-- @tparam string corner The corner to use: auto, top_left, top_right, bottom_left,
|
||||
-- bottom_right, left, right, top bottom. Default is auto, and auto find the
|
||||
-- nearest corner.
|
||||
-- @treturn string The corner name
|
||||
-- @treturn number x The horizontal position
|
||||
-- @treturn number y The vertical position
|
||||
function mouse.client.corner(c, corner)
|
||||
util.deprecated(
|
||||
"Use awful.placement.closest_corner(mouse) or awful.placement[corner](mouse)"..
|
||||
" instead of awful.mouse.client.corner"
|
||||
)
|
||||
|
||||
c = c or capi.client.focus
|
||||
if not c then return end
|
||||
|
||||
local g = c:geometry()
|
||||
local ngeo = nil
|
||||
|
||||
if not corner or corner == "auto" then
|
||||
local m_c = capi.mouse.coords()
|
||||
if math.abs(g.y - m_c.y) < math.abs(g.y + g.height - m_c.y) then
|
||||
if math.abs(g.x - m_c.x) < math.abs(g.x + g.width - m_c.x) then
|
||||
corner = "top_left"
|
||||
else
|
||||
corner = "top_right"
|
||||
end
|
||||
else
|
||||
if math.abs(g.x - m_c.x) < math.abs(g.x + g.width - m_c.x) then
|
||||
corner = "bottom_left"
|
||||
else
|
||||
corner = "bottom_right"
|
||||
end
|
||||
end
|
||||
if (not corner) or corner == "auto" then
|
||||
ngeo, corner = aplace.closest_corner(mouse, {parent = c})
|
||||
elseif corner and aplace[corner] then
|
||||
ngeo = aplace[corner](mouse, {parent = c})
|
||||
end
|
||||
|
||||
local x, y
|
||||
if corner == "top_right" then
|
||||
x = g.x + g.width
|
||||
y = g.y
|
||||
elseif corner == "top_left" then
|
||||
x = g.x
|
||||
y = g.y
|
||||
elseif corner == "bottom_left" then
|
||||
x = g.x
|
||||
y = g.y + g.height
|
||||
else
|
||||
x = g.x + g.width
|
||||
y = g.y + g.height
|
||||
end
|
||||
|
||||
return corner, x, y
|
||||
return corner, ngeo and ngeo.x or nil, ngeo and ngeo.y or nil
|
||||
end
|
||||
|
||||
--- Resize a client.
|
||||
-- @function awful.mouse.client.resize
|
||||
-- @param c The client to resize, or the focused one by default.
|
||||
-- @param corner The corner to grab on resize. Auto detected by default.
|
||||
function mouse.client.resize(c, corner)
|
||||
-- @tparam string corner The corner to grab on resize. Auto detected by default.
|
||||
-- @tparam[opt={}] table args A set of `awful.placement` arguments
|
||||
-- @treturn string The corner (or side) name
|
||||
function mouse.client.resize(c, corner, args)
|
||||
c = c or capi.client.focus
|
||||
|
||||
if not c then return end
|
||||
|
@ -357,19 +209,162 @@ function mouse.client.resize(c, corner)
|
|||
return
|
||||
end
|
||||
|
||||
local lay = layout.get(c.screen)
|
||||
local corner2, x, y = mouse.client.corner(c, corner)
|
||||
-- Move the mouse to the corner
|
||||
if corner and aplace[corner] then
|
||||
aplace[corner](capi.mouse, {parent=c})
|
||||
else
|
||||
local _
|
||||
_, corner = aplace.closest_corner(capi.mouse, {parent=c})
|
||||
end
|
||||
|
||||
if lay == layout.suit.floating or c.floating then
|
||||
return layout.suit.floating.mouse_resize_handler(c, corner2, x, y)
|
||||
elseif lay.mouse_resize_handler then
|
||||
return lay.mouse_resize_handler(c, corner2, x, y)
|
||||
mouse.resize(c, "mouse.resize", args or {include_sides=true})
|
||||
|
||||
return corner
|
||||
end
|
||||
|
||||
--- Default handler for `request::geometry` signals with `mouse.resize` context.
|
||||
-- @signalhandler awful.mouse.resize_handler
|
||||
-- @tparam client c The client
|
||||
-- @tparam string context The context
|
||||
-- @tparam[opt={}] table hints The hints to pass to the handler
|
||||
function mouse.resize_handler(c, context, hints)
|
||||
if hints and context and context:find("mouse.*") then
|
||||
-- This handler only handle the floating clients. If the client is tiled,
|
||||
-- then it let the layouts handle it.
|
||||
local lay = c.screen.selected_tag.layout
|
||||
|
||||
if lay == layout.suit.floating or c.floating then
|
||||
local offset = hints and hints.offset or {}
|
||||
|
||||
if type(offset) == "number" then
|
||||
offset = {
|
||||
x = offset,
|
||||
y = offset,
|
||||
width = offset,
|
||||
height = offset,
|
||||
}
|
||||
end
|
||||
|
||||
c:geometry {
|
||||
x = hints.x + (offset.x or 0 ),
|
||||
y = hints.y + (offset.y or 0 ),
|
||||
width = hints.width + (offset.width or 0 ),
|
||||
height = hints.height + (offset.height or 0 ),
|
||||
}
|
||||
elseif lay.resize_handler then
|
||||
lay.resize_handler(c, context, hints)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Older layouts implement their own mousegrabber.
|
||||
-- @tparam client c The client
|
||||
-- @tparam table args Additional arguments
|
||||
-- @treturn boolean This return false when the resize need to be aborted
|
||||
mouse.resize.add_enter_callback(function(c, args) --luacheck: no unused args
|
||||
if c.floating then return end
|
||||
|
||||
local l = c.screen.selected_tag and c.screen.selected_tag.layout or nil
|
||||
if l == layout.suit.floating then return end
|
||||
|
||||
if l ~= layout.suit.floating and l.mouse_resize_handler then
|
||||
capi.mousegrabber.stop()
|
||||
|
||||
local geo, corner = aplace.closest_corner(capi.mouse, {parent=c})
|
||||
|
||||
l.mouse_resize_handler(c, corner, geo.x, geo.y)
|
||||
|
||||
return false
|
||||
end
|
||||
end, "mouse.resize")
|
||||
|
||||
--- Get the client currently under the mouse cursor.
|
||||
-- @property current_client
|
||||
-- @tparam client|nil The client
|
||||
|
||||
function mouse.object.get_current_client()
|
||||
local obj = capi.mouse.object_under_pointer()
|
||||
if type(obj) == "client" then
|
||||
return obj
|
||||
end
|
||||
end
|
||||
|
||||
function mouse.object.set_current_client() end
|
||||
|
||||
--- Get the wibox currently under the mouse cursor.
|
||||
-- @property current_wibox
|
||||
-- @tparam wibox|nil The wibox
|
||||
|
||||
function mouse.object.get_current_wibox()
|
||||
local obj = capi.mouse.object_under_pointer()
|
||||
if type(obj) == "drawin" then
|
||||
return obj
|
||||
end
|
||||
end
|
||||
|
||||
function mouse.object.set_current_wibox() end
|
||||
|
||||
--- True if the left mouse button is pressed.
|
||||
-- @property is_left_mouse_button_pressed
|
||||
-- @param boolean
|
||||
|
||||
--- True if the right mouse button is pressed.
|
||||
-- @property is_right_mouse_button_pressed
|
||||
-- @param boolean
|
||||
|
||||
--- True if the middle mouse button is pressed.
|
||||
-- @property is_middle_mouse_button_pressed
|
||||
-- @param boolean
|
||||
|
||||
for _, b in ipairs {"left", "right", "middle"} do
|
||||
mouse.object["is_".. b .."_mouse_button_pressed"] = function()
|
||||
return capi.mouse.coords().buttons[1]
|
||||
end
|
||||
|
||||
mouse.object["set_is_".. b .."_mouse_button_pressed"] = function() end
|
||||
end
|
||||
|
||||
capi.client.connect_signal("request::geometry", mouse.resize_handler)
|
||||
|
||||
-- Set the cursor at startup
|
||||
capi.root.cursor("left_ptr")
|
||||
|
||||
-- Implement the custom property handler
|
||||
local props = {}
|
||||
|
||||
capi.mouse.set_newindex_miss_handler(function(_,key,value)
|
||||
if mouse.object["set_"..key] then
|
||||
mouse.object["set_"..key](value)
|
||||
else
|
||||
props[key] = value
|
||||
end
|
||||
end)
|
||||
|
||||
capi.mouse.set_index_miss_handler(function(_,key)
|
||||
if mouse.object["get_"..key] then
|
||||
return mouse.object["get_"..key]()
|
||||
else
|
||||
return props[key]
|
||||
end
|
||||
end)
|
||||
|
||||
--- Get or set the mouse coords.
|
||||
--
|
||||
--@DOC_awful_mouse_coords_EXAMPLE@
|
||||
--
|
||||
-- @tparam[opt=nil] table coords_table None or a table with x and y keys as mouse
|
||||
-- coordinates.
|
||||
-- @tparam[opt=nil] integer coords_table.x The mouse horizontal position
|
||||
-- @tparam[opt=nil] integer coords_table.y The mouse vertical position
|
||||
-- @tparam[opt=false] boolean silent Disable mouse::enter or mouse::leave events that
|
||||
-- could be triggered by the pointer when moving.
|
||||
-- @treturn integer table.x The horizontal position
|
||||
-- @treturn integer table.y The vertical position
|
||||
-- @treturn table table.buttons Table containing the status of buttons, e.g. field [1] is true
|
||||
-- when button 1 is pressed.
|
||||
-- @function mouse.coords
|
||||
|
||||
|
||||
return mouse
|
||||
|
||||
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80
|
||||
|
|
|
@ -0,0 +1,199 @@
|
|||
---------------------------------------------------------------------------
|
||||
--- An extandable mouse resizing handler.
|
||||
--
|
||||
-- This module offer a resizing and moving mechanism for drawable such as
|
||||
-- clients and wiboxes.
|
||||
--
|
||||
-- @author Emmanuel Lepage Vallee <elv1313@gmail.com>
|
||||
-- @copyright 2016 Emmanuel Lepage Vallee
|
||||
-- @release @AWESOME_VERSION@
|
||||
-- @submodule mouse
|
||||
---------------------------------------------------------------------------
|
||||
|
||||
local aplace = require("awful.placement")
|
||||
local capi = {mousegrabber = mousegrabber}
|
||||
|
||||
local module = {}
|
||||
|
||||
local mode = "live"
|
||||
local req = "request::geometry"
|
||||
local callbacks = {enter={}, move={}, leave={}}
|
||||
|
||||
--- Set the resize mode.
|
||||
-- The available modes are:
|
||||
--
|
||||
-- * **live**: Resize the layout everytime the mouse move
|
||||
-- * **after**: Resize the layout only when the mouse is released
|
||||
--
|
||||
-- Some clients, such as XTerm, may lose information if resized too often.
|
||||
--
|
||||
-- @function awful.mouse.resize.set_mode
|
||||
-- @tparam string m The mode
|
||||
function module.set_mode(m)
|
||||
assert(m == "live" or m == "after")
|
||||
mode = m
|
||||
end
|
||||
|
||||
--- Add a initialization callback.
|
||||
-- This callback will be executed before the mouse grabbing start
|
||||
-- @function awful.mouse.resize.add_enter_callback
|
||||
-- @tparam function cb The callback (or nil)
|
||||
-- @tparam[default=other] string context The callback context
|
||||
function module.add_enter_callback(cb, context)
|
||||
context = context or "other"
|
||||
callbacks.enter[context] = callbacks.enter[context] or {}
|
||||
table.insert(callbacks.enter[context], cb)
|
||||
end
|
||||
|
||||
--- Add a "move" callback.
|
||||
-- This callback is executed in "after" mode (see `set_mode`) instead of
|
||||
-- applying the operation.
|
||||
-- @function awful.mouse.resize.add_move_callback
|
||||
-- @tparam function cb The callback (or nil)
|
||||
-- @tparam[default=other] string context The callback context
|
||||
function module.add_move_callback(cb, context)
|
||||
context = context or "other"
|
||||
callbacks.move[context] = callbacks.move[context] or {}
|
||||
table.insert(callbacks.move[context], cb)
|
||||
end
|
||||
|
||||
--- Add a "leave" callback
|
||||
-- This callback is executed just before the `mousegrabber` stop
|
||||
-- @function awful.mouse.resize.add_leave_callback
|
||||
-- @tparam function cb The callback (or nil)
|
||||
-- @tparam[default=other] string context The callback context
|
||||
function module.add_leave_callback(cb, context)
|
||||
context = context or "other"
|
||||
callbacks.leave[context] = callbacks.leave[context] or {}
|
||||
table.insert(callbacks.leave[context], cb)
|
||||
end
|
||||
|
||||
-- Resize, the drawable.
|
||||
--
|
||||
-- Valid `args` are:
|
||||
--
|
||||
-- * *enter_callback*: A function called before the `mousegrabber` start
|
||||
-- * *move_callback*: A function called when the mouse move
|
||||
-- * *leave_callback*: A function called before the `mousegrabber` is released
|
||||
-- * *mode*: The resize mode
|
||||
--
|
||||
-- @function awful.mouse.resize
|
||||
-- @tparam client client A client
|
||||
-- @tparam[default=mouse.resize] string context The resizing context
|
||||
-- @tparam[opt={}] table args A set of `awful.placement` arguments
|
||||
|
||||
local function handler(_, client, context, args) --luacheck: no unused_args
|
||||
args = args or {}
|
||||
context = context or "mouse.resize"
|
||||
|
||||
local placement = args.placement
|
||||
|
||||
if type(placement) == "string" and aplace[placement] then
|
||||
placement = aplace[placement]
|
||||
end
|
||||
|
||||
-- Extend the table with the default arguments
|
||||
args = setmetatable(
|
||||
{
|
||||
placement = placement or aplace.resize_to_mouse,
|
||||
mode = args.mode or mode,
|
||||
pretend = true,
|
||||
},
|
||||
{__index = args or {}}
|
||||
)
|
||||
|
||||
local geo
|
||||
|
||||
for _, cb in ipairs(callbacks.enter[context] or {}) do
|
||||
geo = cb(client, args)
|
||||
|
||||
if geo == false then
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
if args.enter_callback then
|
||||
geo = args.enter_callback(client, args)
|
||||
|
||||
if geo == false then
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
geo = nil
|
||||
|
||||
-- Execute the placement function and use request::geometry
|
||||
capi.mousegrabber.run(function (_mouse)
|
||||
if not client.valid then return end
|
||||
|
||||
-- Resize everytime the mouse move (default behavior)
|
||||
if args.mode == "live" then
|
||||
-- Get the new geometry
|
||||
geo = setmetatable(args.placement(client, args),{__index=args})
|
||||
end
|
||||
|
||||
-- Execute the move callbacks. This can be used to add features such as
|
||||
-- snap or adding fancy graphical effects.
|
||||
for _, cb in ipairs(callbacks.move[context] or {}) do
|
||||
-- If something is returned, assume it is a modified geometry
|
||||
geo = cb(client, geo, args) or geo
|
||||
|
||||
if geo == false then
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
if args.move_callback then
|
||||
geo = args.move_callback(client, geo, args)
|
||||
|
||||
if geo == false then
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
-- In case it was modified
|
||||
setmetatable(geo,{__index=args})
|
||||
|
||||
if args.mode == "live" then
|
||||
-- Ask the resizing handler to resize the client
|
||||
client:emit_signal( req, context, geo)
|
||||
end
|
||||
|
||||
-- Quit when the button is released
|
||||
for _,v in pairs(_mouse.buttons) do
|
||||
if v then return true end
|
||||
end
|
||||
|
||||
-- Only resize after the mouse is released, this avoid losing content
|
||||
-- in resize sensitive apps such as XTerm or allow external modules
|
||||
-- to implement custom resizing
|
||||
if args.mode == "after" then
|
||||
-- Get the new geometry
|
||||
geo = args.placement(client, args)
|
||||
|
||||
-- Ask the resizing handler to resize the client
|
||||
client:emit_signal( req, context, geo)
|
||||
end
|
||||
|
||||
geo = nil
|
||||
|
||||
for _, cb in ipairs(callbacks.leave[context] or {}) do
|
||||
geo = cb(client, geo, args)
|
||||
end
|
||||
|
||||
if args.leave_callback then
|
||||
geo = args.leave_callback(client, geo, args)
|
||||
end
|
||||
|
||||
if not geo then return false end
|
||||
|
||||
-- In case it was modified
|
||||
setmetatable(geo,{__index=args})
|
||||
|
||||
client:emit_signal( req, context, geo)
|
||||
|
||||
return false
|
||||
end, "cross")
|
||||
end
|
||||
|
||||
return setmetatable(module, {__call=handler})
|
|
@ -0,0 +1,269 @@
|
|||
---------------------------------------------------------------------------
|
||||
--- Mouse snapping related functions
|
||||
--
|
||||
-- @author Julien Danjou <julien@danjou.info>
|
||||
-- @copyright 2008 Julien Danjou
|
||||
-- @release @AWESOME_VERSION@
|
||||
-- @submodule mouse
|
||||
---------------------------------------------------------------------------
|
||||
|
||||
local aclient = require("awful.client")
|
||||
local resize = require("awful.mouse.resize")
|
||||
local aplace = require("awful.placement")
|
||||
local wibox = require("wibox")
|
||||
local beautiful = require("beautiful")
|
||||
local color = require("gears.color")
|
||||
local shape = require("gears.shape")
|
||||
local cairo = require("lgi").cairo
|
||||
|
||||
local capi = {
|
||||
root = root,
|
||||
mouse = mouse,
|
||||
screen = screen,
|
||||
client = client,
|
||||
mousegrabber = mousegrabber,
|
||||
}
|
||||
|
||||
local module = {
|
||||
default_distance = 8
|
||||
}
|
||||
|
||||
local placeholder_w = nil
|
||||
|
||||
local function show_placeholder(geo)
|
||||
if not geo then
|
||||
if placeholder_w then
|
||||
placeholder_w.visible = false
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
placeholder_w = placeholder_w or wibox {
|
||||
ontop = true,
|
||||
bg = color(beautiful.snap_bg or beautiful.bg_urgent or "#ff0000"),
|
||||
}
|
||||
|
||||
placeholder_w:geometry(geo)
|
||||
|
||||
local img = cairo.ImageSurface(cairo.Format.A1, geo.width, geo.height)
|
||||
local cr = cairo.Context(img)
|
||||
|
||||
cr:set_operator(cairo.Operator.CLEAR)
|
||||
cr:set_source_rgba(0,0,0,1)
|
||||
cr:paint()
|
||||
cr:set_operator(cairo.Operator.SOURCE)
|
||||
cr:set_source_rgba(1,1,1,1)
|
||||
|
||||
local line_width = beautiful.snap_border_width or 5
|
||||
cr:set_line_width(beautiful.xresources.apply_dpi(line_width))
|
||||
|
||||
local f = beautiful.snap_shape or function()
|
||||
cr:translate(line_width,line_width)
|
||||
shape.rounded_rect(cr,geo.width-2*line_width,geo.height-2*line_width, 10)
|
||||
end
|
||||
|
||||
f(cr, geo.width, geo.height)
|
||||
|
||||
cr:stroke()
|
||||
|
||||
placeholder_w.shape_bounding = img._native
|
||||
|
||||
placeholder_w.visible = true
|
||||
end
|
||||
|
||||
local function build_placement(snap, axis)
|
||||
return aplace.scale
|
||||
+ aplace[snap]
|
||||
+ (
|
||||
axis and aplace["maximize_"..axis] or nil
|
||||
)
|
||||
end
|
||||
|
||||
local function detect_screen_edges(c, snap)
|
||||
local coords = capi.mouse.coords()
|
||||
|
||||
local sg = c.screen.geometry
|
||||
|
||||
local v, h = nil
|
||||
|
||||
if math.abs(coords.x) <= snap + sg.x and coords.x >= sg.x then
|
||||
h = "left"
|
||||
elseif math.abs((sg.x + sg.width) - coords.x) <= snap then
|
||||
h = "right"
|
||||
end
|
||||
|
||||
if math.abs(coords.y) <= snap + sg.y and coords.y >= sg.y then
|
||||
v = "top"
|
||||
elseif math.abs((sg.y + sg.height) - coords.y) <= snap then
|
||||
v = "bottom"
|
||||
end
|
||||
|
||||
return v, h
|
||||
end
|
||||
|
||||
local current_snap, current_axis = nil
|
||||
|
||||
local function detect_areasnap(c, distance)
|
||||
local old_snap = current_snap
|
||||
local v, h = detect_screen_edges(c, distance)
|
||||
|
||||
if v and h then
|
||||
current_snap = v.."_"..h
|
||||
else
|
||||
current_snap = v or h or nil
|
||||
end
|
||||
|
||||
if old_snap == current_snap then return end
|
||||
|
||||
current_axis = ((v and not h) and "horizontally")
|
||||
or ((h and not v) and "vertically")
|
||||
or nil
|
||||
|
||||
-- Show the expected geometry outline
|
||||
show_placeholder(
|
||||
current_snap and build_placement(current_snap, current_axis)(c, {
|
||||
to_percent = 0.5,
|
||||
honor_workarea = true,
|
||||
pretend = true
|
||||
}) or nil
|
||||
)
|
||||
|
||||
end
|
||||
|
||||
local function apply_areasnap(c, args)
|
||||
if not current_snap then return end
|
||||
|
||||
-- Remove the move offset
|
||||
args.offset = {}
|
||||
|
||||
placeholder_w.visible = false
|
||||
|
||||
return build_placement(current_snap, current_axis)(c,{
|
||||
to_percent = 0.5,
|
||||
honor_workarea = true,
|
||||
})
|
||||
end
|
||||
|
||||
local function snap_outside(g, sg, snap)
|
||||
if g.x < snap + sg.x + sg.width and g.x > sg.x + sg.width then
|
||||
g.x = sg.x + sg.width
|
||||
elseif g.x + g.width < sg.x and g.x + g.width > sg.x - snap then
|
||||
g.x = sg.x - g.width
|
||||
end
|
||||
if g.y < snap + sg.y + sg.height and g.y > sg.y + sg.height then
|
||||
g.y = sg.y + sg.height
|
||||
elseif g.y + g.height < sg.y and g.y + g.height > sg.y - snap then
|
||||
g.y = sg.y - g.height
|
||||
end
|
||||
return g
|
||||
end
|
||||
|
||||
local function snap_inside(g, sg, snap)
|
||||
local edgev = 'none'
|
||||
local edgeh = 'none'
|
||||
if math.abs(g.x) < snap + sg.x and g.x > sg.x then
|
||||
edgev = 'left'
|
||||
g.x = sg.x
|
||||
elseif math.abs((sg.x + sg.width) - (g.x + g.width)) < snap then
|
||||
edgev = 'right'
|
||||
g.x = sg.x + sg.width - g.width
|
||||
end
|
||||
if math.abs(g.y) < snap + sg.y and g.y > sg.y then
|
||||
edgeh = 'top'
|
||||
g.y = sg.y
|
||||
elseif math.abs((sg.y + sg.height) - (g.y + g.height)) < snap then
|
||||
edgeh = 'bottom'
|
||||
g.y = sg.y + sg.height - g.height
|
||||
end
|
||||
|
||||
-- What is the dominant dimension?
|
||||
if g.width > g.height then
|
||||
return g, edgeh
|
||||
else
|
||||
return g, edgev
|
||||
end
|
||||
end
|
||||
|
||||
--- Snap a client to the closest client or screen edge.
|
||||
-- @function awful.mouse.snap
|
||||
-- @param c The client to snap.
|
||||
-- @param snap The pixel to snap clients.
|
||||
-- @param x The client x coordinate.
|
||||
-- @param y The client y coordinate.
|
||||
-- @param fixed_x True if the client isn't allowed to move in the x direction.
|
||||
-- @param fixed_y True if the client isn't allowed to move in the y direction.
|
||||
function module.snap(c, snap, x, y, fixed_x, fixed_y)
|
||||
snap = snap or module.default_distance
|
||||
c = c or capi.client.focus
|
||||
local cur_geom = c:geometry()
|
||||
local geom = c:geometry()
|
||||
geom.width = geom.width + (2 * c.border_width)
|
||||
geom.height = geom.height + (2 * c.border_width)
|
||||
local edge
|
||||
geom.x = x or geom.x
|
||||
geom.y = y or geom.y
|
||||
|
||||
geom, edge = snap_inside(geom, capi.screen[c.screen].geometry, snap)
|
||||
geom = snap_inside(geom, capi.screen[c.screen].workarea, snap)
|
||||
|
||||
-- Allow certain windows to snap to the edge of the workarea.
|
||||
-- Only allow docking to workarea for consistency/to avoid problems.
|
||||
if c.dockable then
|
||||
local struts = c:struts()
|
||||
struts['left'] = 0
|
||||
struts['right'] = 0
|
||||
struts['top'] = 0
|
||||
struts['bottom'] = 0
|
||||
if edge ~= "none" and c.floating then
|
||||
if edge == "left" or edge == "right" then
|
||||
struts[edge] = cur_geom.width
|
||||
elseif edge == "top" or edge == "bottom" then
|
||||
struts[edge] = cur_geom.height
|
||||
end
|
||||
end
|
||||
c:struts(struts)
|
||||
end
|
||||
|
||||
geom.x = geom.x - (2 * c.border_width)
|
||||
geom.y = geom.y - (2 * c.border_width)
|
||||
|
||||
for _, snapper in ipairs(aclient.visible(c.screen)) do
|
||||
if snapper ~= c then
|
||||
geom = snap_outside(geom, snapper:geometry(), snap)
|
||||
end
|
||||
end
|
||||
|
||||
geom.width = geom.width - (2 * c.border_width)
|
||||
geom.height = geom.height - (2 * c.border_width)
|
||||
geom.x = geom.x + (2 * c.border_width)
|
||||
geom.y = geom.y + (2 * c.border_width)
|
||||
|
||||
-- It's easiest to undo changes afterwards if they're not allowed
|
||||
if fixed_x then geom.x = cur_geom.x end
|
||||
if fixed_y then geom.y = cur_geom.y end
|
||||
|
||||
return geom
|
||||
end
|
||||
|
||||
-- Enable edge snapping
|
||||
resize.add_move_callback(function(c, geo, args)
|
||||
-- Screen edge snapping (areosnap)
|
||||
if (module.edge_enabled ~= false)
|
||||
and args and (args.snap == nil or args.snap) then
|
||||
detect_areasnap(c, 16)
|
||||
end
|
||||
|
||||
-- Snapping between clients
|
||||
if (module.client_enabled ~= false)
|
||||
and args and (args.snap == nil or args.snap) then
|
||||
return module.snap(c, args.snap, geo.x, geo.y)
|
||||
end
|
||||
end, "mouse.move")
|
||||
|
||||
-- Apply the aerosnap
|
||||
resize.add_leave_callback(function(c, _, args)
|
||||
if module.edge_enabled == false then return end
|
||||
return apply_areasnap(c, args)
|
||||
end, "mouse.move")
|
||||
|
||||
return setmetatable(module, {__call = function(_, ...) return module.snap(...) end})
|
|
@ -9,14 +9,21 @@
|
|||
-- * Turn each function into an API with various common customization parameters.
|
||||
-- * Re-use the same functions for the `mouse`, `client`s, `screen`s and `wibox`es
|
||||
--
|
||||
--
|
||||
--
|
||||
-- <h3>Compositing</h3>
|
||||
--
|
||||
-- It is possible to compose placement function using the `+` or `*` operator:
|
||||
--
|
||||
-- local f = (awful.placement.right + awful.placement.left)
|
||||
-- f(client.focus)
|
||||
--@DOC_awful_placement_compose_EXAMPLE@
|
||||
--
|
||||
-- ### Common arguments
|
||||
--@DOC_awful_placement_compose2_EXAMPLE@
|
||||
--
|
||||
-- <h3>Common arguments</h3>
|
||||
--
|
||||
-- **pretend** (*boolean*):
|
||||
--
|
||||
-- Do not apply the new geometry. This is useful if only the return values is
|
||||
-- necessary.
|
||||
--
|
||||
-- **honor_workarea** (*boolean*):
|
||||
--
|
||||
|
@ -44,6 +51,10 @@
|
|||
--
|
||||
-- **attach** (*boolean*):
|
||||
--
|
||||
-- **offset** (*table or number*):
|
||||
--
|
||||
-- The offset(s) to apply to the new geometry.
|
||||
--
|
||||
-- **store_geometry** (*boolean*):
|
||||
--
|
||||
-- Keep a single history of each type of placement. It can be restored using
|
||||
|
@ -77,6 +88,7 @@ local capi =
|
|||
local client = require("awful.client")
|
||||
local layout = require("awful.layout")
|
||||
local a_screen = require("awful.screen")
|
||||
local util = require("awful.util")
|
||||
local dpi = require("beautiful").xresources.apply_dpi
|
||||
|
||||
local function get_screen(s)
|
||||
|
@ -85,20 +97,89 @@ end
|
|||
|
||||
local wrap_client = nil
|
||||
|
||||
local function compose(w1, w2)
|
||||
return wrap_client(function(...)
|
||||
w1(...)
|
||||
w2(...)
|
||||
return --It make no sense to keep a return value
|
||||
end)
|
||||
--- Allow multiple placement functions to be daisy chained.
|
||||
-- This also allow the functions to be aware they are being chained and act
|
||||
-- upon the previous nodes results to avoid unnecessary processing or deduce
|
||||
-- extra paramaters/arguments.
|
||||
local function compose(...)
|
||||
local queue = {}
|
||||
|
||||
local nodes = {...}
|
||||
|
||||
-- Allow placement.foo + (var == 42 and placement.bar)
|
||||
if not nodes[2] then
|
||||
return nodes[1]
|
||||
end
|
||||
|
||||
-- nodes[1] == self, nodes[2] == other
|
||||
for _, w in ipairs(nodes) do
|
||||
-- Build an execution queue
|
||||
if w.context and w.context == "compose" then
|
||||
for _, elem in ipairs(w.queue or {}) do
|
||||
table.insert(queue, elem)
|
||||
end
|
||||
else
|
||||
table.insert(queue, w)
|
||||
end
|
||||
end
|
||||
|
||||
local ret = wrap_client(function(d, args, ...)
|
||||
local rets = {}
|
||||
local last_geo = nil
|
||||
|
||||
-- As some functions may have to take into account results from
|
||||
-- previously execued ones, add the `composition_results` hint.
|
||||
args = setmetatable({composition_results=rets}, {__index=args})
|
||||
|
||||
-- Only apply the geometry once, not once per chain node, to do this,
|
||||
-- Force the "pretend" argument and restore the original value for
|
||||
-- the last node.
|
||||
local pretend_real = args.pretend
|
||||
|
||||
args.pretend = true
|
||||
|
||||
for k, f in ipairs(queue) do
|
||||
if k == #queue then
|
||||
args.pretend = pretend_real or false
|
||||
end
|
||||
|
||||
local r = {f(d, args, ...)}
|
||||
last_geo = r[1] or last_geo
|
||||
args.override_geometry = last_geo
|
||||
|
||||
-- Keep the return value, store one per context
|
||||
if f.context then
|
||||
-- When 2 composition queue are executed, merge the return values
|
||||
if f.context == "compose" then
|
||||
for k2,v in pairs(r) do
|
||||
rets[k2] = v
|
||||
end
|
||||
else
|
||||
rets[f.context] = r
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return last_geo, rets
|
||||
end, "compose")
|
||||
|
||||
ret.queue = queue
|
||||
|
||||
return ret
|
||||
end
|
||||
|
||||
wrap_client = function(f)
|
||||
return setmetatable({is_placement=true}, {
|
||||
__call = function(_,...) return f(...) end,
|
||||
__add = compose, -- Composition is usually defined as +
|
||||
__mul = compose -- Make sense if you think of the functions as matrices
|
||||
})
|
||||
wrap_client = function(f, context)
|
||||
return setmetatable(
|
||||
{
|
||||
is_placement= true,
|
||||
context = context,
|
||||
},
|
||||
{
|
||||
__call = function(_,...) return f(...) end,
|
||||
__add = compose, -- Composition is usually defined as +
|
||||
__mul = compose -- Make sense if you think of the functions as matrices
|
||||
}
|
||||
)
|
||||
end
|
||||
|
||||
local placement_private = {}
|
||||
|
@ -111,7 +192,7 @@ local placement_private = {}
|
|||
local placement = setmetatable({}, {
|
||||
__index = placement_private,
|
||||
__newindex = function(_, k, f)
|
||||
placement_private[k] = wrap_client(f)
|
||||
placement_private[k] = wrap_client(f, k)
|
||||
end
|
||||
})
|
||||
|
||||
|
@ -143,6 +224,21 @@ local align_map = {
|
|||
-- Store function -> keys
|
||||
local reverse_align_map = {}
|
||||
|
||||
-- Some parameters to correctly compute the final size
|
||||
local resize_to_point_map = {
|
||||
-- Corners
|
||||
top_left = {p1= nil , p2={1,1}, x_only=false, y_only=false, align="bottom_right"},
|
||||
top_right = {p1={0,1} , p2= nil , x_only=false, y_only=false, align="bottom_left" },
|
||||
bottom_left = {p1= nil , p2={1,0}, x_only=false, y_only=false, align="top_right" },
|
||||
bottom_right = {p1={0,0} , p2= nil , x_only=false, y_only=false, align="top_left" },
|
||||
|
||||
-- Sides
|
||||
left = {p1= nil , p2={1,1}, x_only=true , y_only=false, align="top_right" },
|
||||
right = {p1={0,0} , p2= nil , x_only=true , y_only=false, align="top_left" },
|
||||
top = {p1= nil , p2={1,1}, x_only=false, y_only=true , align="bottom_left" },
|
||||
bottom = {p1={0,0} , p2= nil , x_only=false, y_only=true , align="top_left" },
|
||||
}
|
||||
|
||||
--- Add a context to the arguments.
|
||||
-- This function extend the argument table. The context is used by some
|
||||
-- internal helper methods. If there already is a context, it has priority and
|
||||
|
@ -163,17 +259,49 @@ local function store_geometry(d, reqtype)
|
|||
data[d][reqtype].screen = d.screen
|
||||
end
|
||||
|
||||
--- Apply some modifications before applying the new geometry.
|
||||
-- @tparam table new_geo The new geometry
|
||||
-- @tparam table args The common arguments
|
||||
-- @treturn table|nil The new geometry
|
||||
local function fix_new_geometry(new_geo, args)
|
||||
if args.pretend or not new_geo then return nil end
|
||||
|
||||
local offset = args.offset or {}
|
||||
|
||||
if type(offset) == "number" then
|
||||
offset = {
|
||||
x = offset,
|
||||
y = offset,
|
||||
width = offset,
|
||||
height = offset,
|
||||
}
|
||||
end
|
||||
|
||||
return {
|
||||
x = new_geo.x and (new_geo.x + (offset.x or 0)),
|
||||
y = new_geo.y and (new_geo.y + (offset.y or 0)),
|
||||
width = new_geo.width and (new_geo.width + (offset.width or 0)),
|
||||
height = new_geo.height and (new_geo.height + (offset.height or 0)),
|
||||
}
|
||||
end
|
||||
|
||||
--- Get the area covered by a drawin.
|
||||
-- @param d The drawin
|
||||
-- @tparam[opt=nil] table new_geo A new geometry
|
||||
-- @tparam[opt=false] boolean ignore_border_width Ignore the border
|
||||
-- @tparam table args the method arguments
|
||||
-- @treturn The drawin's area.
|
||||
local function area_common(d, new_geo, ignore_border_width)
|
||||
local function area_common(d, new_geo, ignore_border_width, args)
|
||||
-- The C side expect no arguments, nil isn't valid
|
||||
local geometry = new_geo and d:geometry(new_geo) or d:geometry()
|
||||
local border = ignore_border_width and 0 or d.border_width or 0
|
||||
geometry.x = geometry.x
|
||||
geometry.y = geometry.y
|
||||
|
||||
-- When using the placement composition along with the "pretend"
|
||||
-- option, it is necessary to keep a "virtual" geometry.
|
||||
if args and args.override_geometry then
|
||||
geometry = util.table.clone(args.override_geometry)
|
||||
end
|
||||
|
||||
geometry.width = geometry.width + 2 * border
|
||||
geometry.height = geometry.height + 2 * border
|
||||
return geometry
|
||||
|
@ -196,14 +324,17 @@ local function geometry_common(obj, args, new_geo, ignore_border_width)
|
|||
|
||||
-- It's a mouse
|
||||
if obj.coords then
|
||||
local coords = new_geo and obj.coords(new_geo) or obj.coords()
|
||||
local coords = fix_new_geometry(new_geo, args)
|
||||
and obj.coords(new_geo) or obj.coords()
|
||||
return {x=coords.x, y=coords.y, width=0, height=0}
|
||||
elseif obj.geometry then
|
||||
local geo = obj.geometry
|
||||
|
||||
-- It is either a drawable or something that implement its API
|
||||
if type(geo) == "function" then
|
||||
local dgeo = area_common(obj, new_geo, ignore_border_width)
|
||||
local dgeo = area_common(
|
||||
obj, fix_new_geometry(new_geo, args), ignore_border_width, args
|
||||
)
|
||||
|
||||
-- Apply the margins
|
||||
if args.margins then
|
||||
|
@ -434,6 +565,24 @@ local function area_remove(areas, elem)
|
|||
return areas
|
||||
end
|
||||
|
||||
-- Convert 2 points into a rectangle
|
||||
local function rect_from_points(p1x, p1y, p2x, p2y)
|
||||
return {
|
||||
x = p1x,
|
||||
y = p1y,
|
||||
width = p2x - p1x,
|
||||
height = p2y - p1y,
|
||||
}
|
||||
end
|
||||
|
||||
-- Convert a rectangle and matrix info into a point
|
||||
local function rect_to_point(rect, corner_i, corner_j)
|
||||
return {
|
||||
x = rect.x + corner_i * math.floor(rect.width ),
|
||||
y = rect.y + corner_j * math.floor(rect.height),
|
||||
}
|
||||
end
|
||||
|
||||
--- Move a drawable to the closest corner of the parent geometry (such as the
|
||||
-- screen).
|
||||
--
|
||||
|
@ -445,6 +594,7 @@ end
|
|||
-- @tparam[opt=client.focus] drawable d A drawable (like `client`, `mouse`
|
||||
-- or `wibox`)
|
||||
-- @tparam[opt={}] table args The arguments
|
||||
-- @treturn table The new geometry
|
||||
-- @treturn string The corner name
|
||||
function placement.closest_corner(d, args)
|
||||
args = add_context(args, "closest_corner")
|
||||
|
@ -460,8 +610,10 @@ function placement.closest_corner(d, args)
|
|||
-- Use the product of 3 to get the closest point in a NxN matrix
|
||||
local function f(_n, mat)
|
||||
n = _n
|
||||
corner_i = -math.ceil( ( (sgeo.x - pos.x) * n) / sgeo.width )
|
||||
corner_j = -math.ceil( ( (sgeo.y - pos.y) * n) / sgeo.height )
|
||||
-- The +1 is required to avoid a rounding error when
|
||||
-- pos.x == sgeo.x+sgeo.width
|
||||
corner_i = -math.ceil( ( (sgeo.x - pos.x) * n) / (sgeo.width + 1))
|
||||
corner_j = -math.ceil( ( (sgeo.y - pos.y) * n) / (sgeo.height + 1))
|
||||
return mat[corner_j + 1][corner_i + 1]
|
||||
end
|
||||
|
||||
|
@ -476,9 +628,9 @@ function placement.closest_corner(d, args)
|
|||
|
||||
-- Transpose the corner back to the original size
|
||||
local new_args = setmetatable({position = corner}, {__index=args})
|
||||
placement_private.align(d, new_args)
|
||||
local ngeo = placement_private.align(d, new_args)
|
||||
|
||||
return corner
|
||||
return ngeo, corner
|
||||
end
|
||||
|
||||
--- Place the client so no part of it will be outside the screen (workarea).
|
||||
|
@ -520,6 +672,7 @@ end
|
|||
--- Place the client where there's place available with minimum overlap.
|
||||
--@DOC_awful_placement_no_overlap_EXAMPLE@
|
||||
-- @param c The client.
|
||||
-- @treturn table The new geometry
|
||||
function placement.no_overlap(c)
|
||||
c = c or capi.client.focus
|
||||
local geometry = area_common(c)
|
||||
|
@ -578,14 +731,24 @@ end
|
|||
|
||||
--- Place the client under the mouse.
|
||||
--@DOC_awful_placement_under_mouse_EXAMPLE@
|
||||
-- @param c The client.
|
||||
-- @return The new client geometry.
|
||||
function placement.under_mouse(c)
|
||||
c = c or capi.client.focus
|
||||
local c_geometry = area_common(c)
|
||||
-- @tparam drawable d A drawable (like `client`, `mouse` or `wibox`)
|
||||
-- @tparam[opt={}] table args Other arguments
|
||||
-- @treturn table The new geometry
|
||||
function placement.under_mouse(d, args)
|
||||
args = add_context(args, "under_mouse")
|
||||
d = d or capi.client.focus
|
||||
|
||||
local m_coords = capi.mouse.coords()
|
||||
return c:geometry({ x = m_coords.x - c_geometry.width / 2,
|
||||
y = m_coords.y - c_geometry.height / 2 })
|
||||
|
||||
local ngeo = geometry_common(d, args)
|
||||
ngeo.x = m_coords.x - ngeo.width / 2
|
||||
ngeo.y = m_coords.y - ngeo.height / 2
|
||||
|
||||
local bw = d.border_width or 0
|
||||
ngeo.width = ngeo.width - 2*bw
|
||||
ngeo.height = ngeo.height - 2*bw
|
||||
|
||||
return ngeo
|
||||
end
|
||||
|
||||
--- Place the client next to the mouse.
|
||||
|
@ -595,7 +758,7 @@ end
|
|||
--@DOC_awful_placement_next_to_mouse_EXAMPLE@
|
||||
-- @client[opt=focused] c The client.
|
||||
-- @tparam[opt=apply_dpi(5)] integer offset The offset from the mouse position.
|
||||
-- @return The new client geometry.
|
||||
-- @treturn table The new geometry
|
||||
function placement.next_to_mouse(c, offset)
|
||||
c = c or capi.client.focus
|
||||
offset = offset or dpi(5)
|
||||
|
@ -627,6 +790,78 @@ function placement.next_to_mouse(c, offset)
|
|||
return c:geometry({ x = x, y = y })
|
||||
end
|
||||
|
||||
--- Resize the drawable to the cursor.
|
||||
--
|
||||
-- Valid args:
|
||||
--
|
||||
-- * *axis*: The axis (vertical or horizontal). If none is
|
||||
-- specified, then the drawable will be resized on both axis.
|
||||
--
|
||||
--@DOC_awful_placement_resize_to_mouse_EXAMPLE@
|
||||
-- @tparam drawable d A drawable (like `client`, `mouse` or `wibox`)
|
||||
-- @tparam[opt={}] table args Other arguments
|
||||
-- @treturn table The new geometry
|
||||
function placement.resize_to_mouse(d, args)
|
||||
d = d or capi.client.focus
|
||||
args = add_context(args, "resize_to_mouse")
|
||||
|
||||
local coords = capi.mouse.coords()
|
||||
local ngeo = geometry_common(d, args)
|
||||
local h_only = args.axis == "horizontal"
|
||||
local v_only = args.axis == "vertical"
|
||||
|
||||
-- To support both growing and shrinking the drawable, it is necessary
|
||||
-- to decide to use either "north or south" and "east or west" directions.
|
||||
-- Otherwise, the result will always be 1x1
|
||||
local _, closest_corner = placement.closest_corner(capi.mouse, {
|
||||
parent = d,
|
||||
pretend = true,
|
||||
include_sides = args.include_sides or false,
|
||||
})
|
||||
|
||||
-- Given "include_sides" wasn't set, it will always return a name
|
||||
-- with the 2 axis. If only one axis is needed, adjust the result
|
||||
if h_only then
|
||||
closest_corner = closest_corner:match("left") or closest_corner:match("right")
|
||||
elseif v_only then
|
||||
closest_corner = closest_corner:match("top") or closest_corner:match("bottom")
|
||||
end
|
||||
|
||||
-- Use p0 (mouse), p1 and p2 to create a rectangle
|
||||
local pts = resize_to_point_map[closest_corner]
|
||||
local p1 = pts.p1 and rect_to_point(ngeo, pts.p1[1], pts.p1[2]) or coords
|
||||
local p2 = pts.p2 and rect_to_point(ngeo, pts.p2[1], pts.p2[2]) or coords
|
||||
|
||||
-- Create top_left and bottom_right points, convert to rectangle
|
||||
ngeo = rect_from_points(
|
||||
pts.y_only and ngeo.x or math.min(p1.x, p2.x),
|
||||
pts.x_only and ngeo.y or math.min(p1.y, p2.y),
|
||||
pts.y_only and ngeo.x + ngeo.width or math.max(p2.x, p1.x),
|
||||
pts.x_only and ngeo.y + ngeo.height or math.max(p2.y, p1.y)
|
||||
)
|
||||
|
||||
local bw = d.border_width or 0
|
||||
|
||||
for _, a in ipairs {"width", "height"} do
|
||||
ngeo[a] = ngeo[a] - 2*bw
|
||||
end
|
||||
|
||||
-- Now, correct the geometry by the given size_hints offset
|
||||
if d.apply_size_hints then
|
||||
local w, h = d:apply_size_hints(
|
||||
ngeo.width,
|
||||
ngeo.height
|
||||
)
|
||||
local offset = align_map[pts.align](w, h, ngeo.width, ngeo.height)
|
||||
ngeo.x = ngeo.x - offset.x
|
||||
ngeo.y = ngeo.y - offset.y
|
||||
end
|
||||
|
||||
geometry_common(d, args, ngeo)
|
||||
|
||||
return ngeo
|
||||
end
|
||||
|
||||
--- Move the drawable (client or wibox) `d` to a screen position or side.
|
||||
--
|
||||
-- Supported args.positions are:
|
||||
|
@ -646,6 +881,7 @@ end
|
|||
--@DOC_awful_placement_align_EXAMPLE@
|
||||
-- @tparam drawable d A drawable (like `client`, `mouse` or `wibox`)
|
||||
-- @tparam[opt={}] table args Other arguments
|
||||
-- @treturn table The new geometry
|
||||
function placement.align(d, args)
|
||||
args = add_context(args, "align")
|
||||
d = d or capi.client.focus
|
||||
|
@ -663,14 +899,18 @@ function placement.align(d, args)
|
|||
dgeo.height
|
||||
)
|
||||
|
||||
geometry_common(d, args, {
|
||||
local ngeo = {
|
||||
x = (pos.x and math.ceil(sgeo.x + pos.x) or dgeo.x) ,
|
||||
y = (pos.y and math.ceil(sgeo.y + pos.y) or dgeo.y) ,
|
||||
width = math.ceil(dgeo.width ) - 2*bw,
|
||||
height = math.ceil(dgeo.height ) - 2*bw,
|
||||
})
|
||||
}
|
||||
|
||||
geometry_common(d, args, ngeo)
|
||||
|
||||
attach(d, placement[args.position], args)
|
||||
|
||||
return ngeo
|
||||
end
|
||||
|
||||
-- Add the alias functions
|
||||
|
@ -678,7 +918,7 @@ for k in pairs(align_map) do
|
|||
placement[k] = function(d, args)
|
||||
args = add_context(args, k)
|
||||
args.position = k
|
||||
placement_private.align(d, args)
|
||||
return placement_private.align(d, args)
|
||||
end
|
||||
reverse_align_map[placement[k]] = k
|
||||
end
|
||||
|
@ -716,6 +956,7 @@ end
|
|||
--@DOC_awful_placement_stretch_EXAMPLE@
|
||||
-- @tparam[opt=client.focus] drawable d A drawable (like `client` or `wibox`)
|
||||
-- @tparam[opt={}] table args The arguments
|
||||
-- @treturn table The new geometry
|
||||
function placement.stretch(d, args)
|
||||
args = add_context(args, "stretch")
|
||||
|
||||
|
@ -757,6 +998,8 @@ function placement.stretch(d, args)
|
|||
geometry_common(d, args, ngeo)
|
||||
|
||||
attach(d, placement["stretch_"..args.direction], args)
|
||||
|
||||
return ngeo
|
||||
end
|
||||
|
||||
-- Add the alias functions
|
||||
|
@ -764,7 +1007,7 @@ for _,v in ipairs {"left", "right", "up", "down"} do
|
|||
placement["stretch_"..v] = function(d, args)
|
||||
args = add_context(args, "stretch_"..v)
|
||||
args.direction = v
|
||||
placement_private.stretch(d, args)
|
||||
return placement_private.stretch(d, args)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -785,6 +1028,7 @@ end
|
|||
--@DOC_awful_placement_maximize_EXAMPLE@
|
||||
-- @tparam[opt=client.focus] drawable d A drawable (like `client` or `wibox`)
|
||||
-- @tparam[opt={}] table args The arguments
|
||||
-- @treturn table The new geometry
|
||||
function placement.maximize(d, args)
|
||||
args = add_context(args, "maximize")
|
||||
d = d or capi.client.focus
|
||||
|
@ -808,6 +1052,8 @@ function placement.maximize(d, args)
|
|||
geometry_common(d, args, ngeo)
|
||||
|
||||
attach(d, placement.maximize, args)
|
||||
|
||||
return ngeo
|
||||
end
|
||||
|
||||
-- Add the alias functions
|
||||
|
@ -815,10 +1061,66 @@ for _, v in ipairs {"vertically", "horizontally"} do
|
|||
placement["maximize_"..v] = function(d2, args)
|
||||
args = add_context(args, "maximize_"..v)
|
||||
args.axis = v
|
||||
placement_private.maximize(d2, args)
|
||||
return placement_private.maximize(d2, args)
|
||||
end
|
||||
end
|
||||
|
||||
--- Scale the drawable by either a relative or absolute percent.
|
||||
--
|
||||
-- Valid args:
|
||||
--
|
||||
-- **to_percent** : A number between 0 and 1. It represent a percent related to
|
||||
-- the parent geometry.
|
||||
-- **by_percent** : A number between 0 and 1. It represent a percent related to
|
||||
-- the current size.
|
||||
-- **direction**: Nothing or "left", "right", "up", "down".
|
||||
--
|
||||
-- @tparam[opt=client.focus] drawable d A drawable (like `client` or `wibox`)
|
||||
-- @tparam[opt={}] table args The arguments
|
||||
-- @treturn table The new geometry
|
||||
function placement.scale(d, args)
|
||||
args = add_context(args, "scale_to_percent")
|
||||
d = d or capi.client.focus
|
||||
|
||||
local to_percent = args.to_percent
|
||||
local by_percent = args.by_percent
|
||||
|
||||
local percent = to_percent or by_percent
|
||||
|
||||
local direction = args.direction
|
||||
|
||||
local sgeo = get_parent_geometry(d, args)
|
||||
local ngeo = geometry_common(d, args, nil)
|
||||
|
||||
local old_area = {width = ngeo.width, height = ngeo.height}
|
||||
|
||||
if (not direction) or direction == "left" or direction == "right" then
|
||||
ngeo.width = (to_percent and sgeo or ngeo).width*percent
|
||||
|
||||
if direction == "left" then
|
||||
ngeo.x = ngeo.x - (ngeo.width - old_area.width)
|
||||
end
|
||||
end
|
||||
|
||||
if (not direction) or direction == "up" or direction == "down" then
|
||||
ngeo.height = (to_percent and sgeo or ngeo).height*percent
|
||||
|
||||
if direction == "up" then
|
||||
ngeo.y = ngeo.y - (ngeo.height - old_area.height)
|
||||
end
|
||||
end
|
||||
|
||||
local bw = d.border_width or 0
|
||||
ngeo.width = ngeo.width - 2*bw
|
||||
ngeo.height = ngeo.height - 2*bw
|
||||
|
||||
geometry_common(d, args, ngeo)
|
||||
|
||||
attach(d, placement.maximize, args)
|
||||
|
||||
return ngeo
|
||||
end
|
||||
|
||||
---@DOC_awful_placement_maximize_vertically_EXAMPLE@
|
||||
|
||||
---@DOC_awful_placement_maximize_horizontally_EXAMPLE@
|
||||
|
|
134
mouse.c
134
mouse.c
|
@ -19,7 +19,39 @@
|
|||
*
|
||||
*/
|
||||
|
||||
/** awesome mouse API
|
||||
/** awesome mouse API.
|
||||
*
|
||||
* The mouse buttons are represented as index. The common ones are:
|
||||
*
|
||||
* ![Client geometry](../images/mouse.svg)
|
||||
*
|
||||
* It is possible to be notified of mouse events by connecting to various
|
||||
* `client`, `widget`s and `wibox` signals:
|
||||
*
|
||||
* * `mouse::enter`
|
||||
* * `mouse::leave`
|
||||
* * `mouse::press`
|
||||
* * `mouse::release`
|
||||
* * `mouse::move`
|
||||
*
|
||||
* It is also possible to add generic mouse button callbacks for `client`s,
|
||||
* `wiboxe`s and the `root` window. Those are set in the default `rc.lua` as such:
|
||||
*
|
||||
* **root**:
|
||||
*
|
||||
* root.buttons(awful.util.table.join(
|
||||
* awful.button({ }, 3, function () mymainmenu:toggle() end),
|
||||
* awful.button({ }, 4, awful.tag.viewnext),
|
||||
* awful.button({ }, 5, awful.tag.viewprev)
|
||||
* ))
|
||||
*
|
||||
* **client**:
|
||||
*
|
||||
* clientbuttons = awful.util.table.join(
|
||||
* awful.button({ }, 1, function (c) client.focus = c; c:raise() end),
|
||||
* awful.button({ modkey }, 1, awful.mouse.client.move),
|
||||
* awful.button({ modkey }, 3, awful.mouse.client.resize)
|
||||
* )
|
||||
*
|
||||
* See also `mousegrabber`
|
||||
*
|
||||
|
@ -33,27 +65,19 @@
|
|||
#include "math.h"
|
||||
#include "common/util.h"
|
||||
#include "common/xutil.h"
|
||||
#include "common/luaclass.h"
|
||||
#include "globalconf.h"
|
||||
#include "objects/client.h"
|
||||
#include "objects/drawin.h"
|
||||
#include "objects/screen.h"
|
||||
|
||||
/** Mouse library.
|
||||
*
|
||||
* @table mouse
|
||||
*/
|
||||
static int miss_index_handler = LUA_REFNIL;
|
||||
static int miss_newindex_handler = LUA_REFNIL;
|
||||
|
||||
/**
|
||||
* The `screen` under the cursor
|
||||
* @field screen
|
||||
*/
|
||||
|
||||
/** A table with X and Y coordinates.
|
||||
* @field x X coordinate.
|
||||
* @field y Y coordinate.
|
||||
* @field buttons Table containing the status of buttons, e.g. field [1] is true
|
||||
* when button 1 is pressed.
|
||||
* @table coords_table
|
||||
* @property screen
|
||||
* @param screen
|
||||
*/
|
||||
|
||||
/** Get the pointer position.
|
||||
|
@ -119,6 +143,39 @@ mouse_warp_pointer(xcb_window_t window, int16_t x, int16_t y)
|
|||
0, 0, 0, 0, x, y);
|
||||
}
|
||||
|
||||
/**
|
||||
* Allow the a Lua handler to be implemented for custom properties and
|
||||
* functions.
|
||||
* \param L A lua state
|
||||
* \param handler A function on the LUA_REGISTRYINDEX
|
||||
*/
|
||||
static int
|
||||
luaA_mouse_call_handler(lua_State *L, int handler)
|
||||
{
|
||||
int nargs = lua_gettop(L);
|
||||
|
||||
/* Push error handling function and move it before args */
|
||||
lua_pushcfunction(L, luaA_dofunction_error);
|
||||
lua_insert(L, - nargs - 1);
|
||||
int error_func_pos = 1;
|
||||
|
||||
/* push function and move it before args */
|
||||
lua_rawgeti(L, LUA_REGISTRYINDEX, handler);
|
||||
lua_insert(L, - nargs - 1);
|
||||
|
||||
if(lua_pcall(L, nargs, LUA_MULTRET, error_func_pos))
|
||||
{
|
||||
warn("%s", lua_tostring(L, -1));
|
||||
/* Remove error function and error string */
|
||||
lua_pop(L, 2);
|
||||
return 0;
|
||||
}
|
||||
/* Remove error function */
|
||||
lua_remove(L, error_func_pos);
|
||||
|
||||
return lua_gettop(L);
|
||||
}
|
||||
|
||||
/** Mouse library.
|
||||
* \param L The Lua VM state.
|
||||
* \return The number of elements pushed on stack.
|
||||
|
@ -133,8 +190,13 @@ luaA_mouse_index(lua_State *L)
|
|||
int16_t mouse_x, mouse_y;
|
||||
|
||||
/* attr is not "screen"?! */
|
||||
if (A_STRNEQ(attr, "screen"))
|
||||
return luaA_default_index(L);
|
||||
if (A_STRNEQ(attr, "screen")) {
|
||||
if (miss_index_handler != LUA_REFNIL) {
|
||||
return luaA_mouse_call_handler(L, miss_index_handler);
|
||||
}
|
||||
else
|
||||
return luaA_default_index(L);
|
||||
}
|
||||
|
||||
if (!mouse_query_pointer_root(&mouse_x, &mouse_y, NULL, NULL))
|
||||
{
|
||||
|
@ -162,8 +224,14 @@ luaA_mouse_newindex(lua_State *L)
|
|||
const char *attr = luaL_checkstring(L, 2);
|
||||
screen_t *screen;
|
||||
|
||||
if (A_STRNEQ(attr, "screen"))
|
||||
return luaA_default_newindex(L);
|
||||
if (A_STRNEQ(attr, "screen")) {
|
||||
/* Call the lua mouse property handler */
|
||||
if (miss_newindex_handler != LUA_REFNIL) {
|
||||
return luaA_mouse_call_handler(L, miss_newindex_handler);
|
||||
}
|
||||
else
|
||||
return luaA_default_newindex(L);
|
||||
}
|
||||
|
||||
screen = luaA_checkscreen(L, 3);
|
||||
mouse_warp_pointer(globalconf.screen->root, screen->geometry.x, screen->geometry.y);
|
||||
|
@ -201,15 +269,7 @@ luaA_mouse_pushstatus(lua_State *L, int x, int y, uint16_t mask)
|
|||
return 1;
|
||||
}
|
||||
|
||||
/** Get or set the mouse coords.
|
||||
*
|
||||
* @tparam coords_table coords_table None or a table with x and y keys as mouse
|
||||
* coordinates.
|
||||
* @tparam boolean silent Disable mouse::enter or mouse::leave events that
|
||||
* could be triggered by the pointer when moving.
|
||||
* @treturn coords_table A table with mouse coordinates.
|
||||
* @function coords
|
||||
*/
|
||||
/* documented in lib/awful/mouse/init.lua */
|
||||
static int
|
||||
luaA_mouse_coords(lua_State *L)
|
||||
{
|
||||
|
@ -271,12 +331,32 @@ luaA_mouse_object_under_pointer(lua_State *L)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a custom property handler (getter).
|
||||
*/
|
||||
static int
|
||||
luaA_mouse_set_index_miss_handler(lua_State *L)
|
||||
{
|
||||
return luaA_registerfct(L, 1, &miss_index_handler);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a custom property handler (setter).
|
||||
*/
|
||||
static int
|
||||
luaA_mouse_set_newindex_miss_handler(lua_State *L)
|
||||
{
|
||||
return luaA_registerfct(L, 1, &miss_newindex_handler);
|
||||
}
|
||||
|
||||
const struct luaL_Reg awesome_mouse_methods[] =
|
||||
{
|
||||
{ "__index", luaA_mouse_index },
|
||||
{ "__newindex", luaA_mouse_newindex },
|
||||
{ "coords", luaA_mouse_coords },
|
||||
{ "object_under_pointer", luaA_mouse_object_under_pointer },
|
||||
{ "set_index_miss_handler", luaA_mouse_set_index_miss_handler},
|
||||
{ "set_newindex_miss_handler", luaA_mouse_set_newindex_miss_handler},
|
||||
{ NULL, NULL }
|
||||
};
|
||||
const struct luaL_Reg awesome_mouse_meta[] =
|
||||
|
|
|
@ -46,7 +46,6 @@ function(escape_string variable content escaped_content line_prefix)
|
|||
if(variable MATCHES "--DOC_HIDE_ALL")
|
||||
return()
|
||||
endif()
|
||||
|
||||
string(REGEX REPLACE "\n" ";" var_lines "${variable}")
|
||||
|
||||
set(tmp_output ${content})
|
||||
|
@ -201,10 +200,17 @@ function(run_test test_path namespace template escaped_content)
|
|||
|
||||
# Only add it if there is something to display.
|
||||
if(NOT ${TEST_CODE} STREQUAL "\n--")
|
||||
# Do not use the @usage tag, use 4 spaces
|
||||
file(READ ${test_path} tmp_content)
|
||||
if(NOT tmp_content MATCHES "--DOC_NO_USAGE")
|
||||
set(DOC_PREFIX "@usage")
|
||||
endif()
|
||||
|
||||
escape_string(
|
||||
" @usage"
|
||||
" ${DOC_PREFIX}"
|
||||
"${TEST_DOC_CONTENT}" TEST_DOC_CONTENT ""
|
||||
)
|
||||
|
||||
set(TEST_DOC_CONTENT "${TEST_DOC_CONTENT}${TEST_CODE}")
|
||||
endif()
|
||||
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
screen[1]._resize {x = 175, width = 128, height = 96} --DOC_HIDE
|
||||
mouse.coords {x=175+60,y=60} --DOC_HIDE
|
||||
|
||||
-- Get the position
|
||||
print(mouse.coords().x)
|
||||
|
||||
-- Change the position
|
||||
mouse.coords {
|
||||
x = 185,
|
||||
y = 10
|
||||
}
|
||||
|
||||
mouse.push_history() --DOC_HIDE
|
|
@ -1,6 +1,7 @@
|
|||
-- Align a client to the bottom of the parent area. --DOC_HEADER
|
||||
-- @tparam drawable d A drawable (like `client`, `mouse` or `wibox`) --DOC_HEADER
|
||||
-- @tparam[opt={}] table args Other arguments") --DOC_HEADER
|
||||
-- @treturn table The new geometry --DOC_HEADER
|
||||
-- @name bottom --DOC_HEADER
|
||||
-- @class function --DOC_HEADER
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
-- Align a client to the bottom left of the parent area. --DOC_HEADER
|
||||
-- @tparam drawable d A drawable (like `client`, `mouse` or `wibox`) --DOC_HEADER
|
||||
-- @tparam[opt={}] table args Other arguments") --DOC_HEADER
|
||||
-- @treturn table The new geometry --DOC_HEADER
|
||||
-- @name bottom_left --DOC_HEADER
|
||||
-- @class function --DOC_HEADER
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
-- Align a client to the bottom right of the parent area. --DOC_HEADER
|
||||
-- @tparam drawable d A drawable (like `client`, `mouse` or `wibox`) --DOC_HEADER
|
||||
-- @tparam[opt={}] table args Other arguments") --DOC_HEADER
|
||||
-- @treturn table The new geometry --DOC_HEADER
|
||||
-- @name bottom_right --DOC_HEADER
|
||||
-- @class function --DOC_HEADER
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
-- Align a client to the horizontal center left of the parent area. --DOC_HEADER
|
||||
-- @tparam drawable d A drawable (like `client`, `mouse` or `wibox`) --DOC_HEADER
|
||||
-- @tparam[opt={}] table args Other arguments") --DOC_HEADER
|
||||
-- @treturn table The new geometry --DOC_HEADER
|
||||
-- @name center_horizontal --DOC_HEADER
|
||||
-- @class function --DOC_HEADER
|
||||
screen[1]._resize {width = 128, height = 96} --DOC_HIDE
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
-- Align a client to the center of the parent area. --DOC_HEADER
|
||||
-- @tparam drawable d A drawable (like `client`, `mouse` or `wibox`) --DOC_HEADER
|
||||
-- @tparam[opt={}] table args Other arguments") --DOC_HEADER
|
||||
-- @treturn table The new geometry --DOC_HEADER
|
||||
-- @name centered --DOC_HEADER
|
||||
-- @class function --DOC_HEADER
|
||||
|
||||
|
|
|
@ -55,7 +55,7 @@ assert(mouse.coords().x == c.x and mouse.coords().y == c.y+c.height+2*bw) --DOC_
|
|||
|
||||
-- It is possible to emulate the mouse API to get the closest corner of
|
||||
-- random area
|
||||
local corner = awful.placement.closest_corner(
|
||||
local _, corner = awful.placement.closest_corner(
|
||||
{coords=function() return {x = 100, y=100} end},
|
||||
{include_sides = true, bounding_rect = {x=0, y=0, width=200, height=200}}
|
||||
)
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
screen[1]._resize {width = 128, height = 96} --DOC_HIDE
|
||||
screen[1]._resize {x = 175, width = 128, height = 96} --DOC_NO_USAGE --DOC_HIDE
|
||||
local awful = {placement = require("awful.placement")} --DOC_HIDE
|
||||
local c = client.gen_fake {x = 45, y = 35, width=40, height=30} --DOC_HIDE
|
||||
local c = client.gen_fake {x = 220, y = 35, width=40, height=30} --DOC_HIDE
|
||||
|
||||
local f = (awful.placement.right + awful.placement.left)
|
||||
f(client.focus)
|
||||
-- "right" will be replaced by "left"
|
||||
local f = (awful.placement.right + awful.placement.left)
|
||||
f(client.focus)
|
||||
|
||||
assert(c.x == 0 and c.y==screen[1].geometry.height/2-30/2-c.border_width--DOC_HIDE
|
||||
local sg = screen[1].geometry--DOC_HIDE
|
||||
assert(c.x == sg.x and c.y==sg.height/2-30/2-c.border_width--DOC_HIDE
|
||||
and c.width==40 and c.height==30)--DOC_HIDE
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
screen[1]._resize {x = 175, width = 128, height = 96} --DOC_NO_USAGE --DOC_HIDE
|
||||
local awful = {placement = require("awful.placement")} --DOC_HIDE
|
||||
local c = client.gen_fake {x = 220, y = 35, width=40, height=30} --DOC_HIDE
|
||||
|
||||
|
||||
-- Simulate Windows 7 "edge snap" (also called aero snap) feature
|
||||
local axis = "vertically"
|
||||
|
||||
local f = awful.placement.scale
|
||||
+ awful.placement.left
|
||||
+ (axis and awful.placement["maximize_"..axis] or nil)
|
||||
|
||||
local geo = f(client.focus, {honor_workarea=true, to_percent = 0.5})
|
||||
|
||||
local wa = screen[1].workarea--DOC_HIDE
|
||||
assert(c.x == wa.x and geo.x == wa.x)--DOC_HIDE
|
||||
assert(c.y == wa.y) --DOC_HIDE
|
||||
assert(c.width == wa.width/2 - 2*c.border_width)--DOC_HIDE
|
||||
assert(c.height == wa.height - 2*c.border_width)--DOC_HIDE
|
|
@ -1,6 +1,7 @@
|
|||
-- Align a client to the left of the parent area. --DOC_HEADER
|
||||
-- @tparam drawable d A drawable (like `client`, `mouse` or `wibox`) --DOC_HEADER
|
||||
-- @tparam[opt={}] table args Other arguments") --DOC_HEADER
|
||||
-- @treturn table The new geometry --DOC_HEADER
|
||||
-- @name left --DOC_HEADER
|
||||
-- @class function --DOC_HEADER
|
||||
|
||||
|
|
|
@ -0,0 +1,74 @@
|
|||
--DOC_HIDE_ALL
|
||||
local awful = {placement = require("awful.placement")}
|
||||
local unpack = unpack or table.unpack -- luacheck: globals unpack (compatibility with Lua 5.1)
|
||||
|
||||
screen._setup_grid(64, 48, {4, 4, 4, 4}, {workarea_sides=0})
|
||||
|
||||
local function test_touch_mouse(c)
|
||||
local coords = mouse.coords()
|
||||
|
||||
return c:geometry().x == coords.x or c:geometry().y == coords.y
|
||||
or c:geometry().x+c:geometry().width+2*c.border_width == coords.x
|
||||
or c:geometry().y+c:geometry().height+2*c.border_width == coords.y
|
||||
end
|
||||
|
||||
for s=1, 8 do
|
||||
local scr = screen[s]
|
||||
local x, y = scr.geometry.x, scr.geometry.y
|
||||
local c = client.gen_fake{x = x+22, y = y+16, width=20, height=15, screen=scr}
|
||||
assert(client.get()[s] == c)
|
||||
end
|
||||
|
||||
for s=9, 16 do
|
||||
local scr = screen[s]
|
||||
local x, y = scr.geometry.x, scr.geometry.y
|
||||
local c = client.gen_fake{x = x+10, y = y+10, width=44, height=28, screen=scr}
|
||||
assert(client.get()[s] == c)
|
||||
end
|
||||
|
||||
local function move_corsor(s, x, y)
|
||||
local sg = screen[s].geometry
|
||||
mouse.coords {x=sg.x+x,y=sg.y+y}
|
||||
end
|
||||
|
||||
local all_coords_out = {
|
||||
top_left = {10, 10},
|
||||
top = {32, 10},
|
||||
top_right = {60, 10},
|
||||
right = {60, 20},
|
||||
bottom_right = {60, 40},
|
||||
bottom = {32, 40},
|
||||
bottom_left = {10, 40},
|
||||
left = {10, 29},
|
||||
}
|
||||
|
||||
local all_coords_in = {
|
||||
top_left = {20, 18},
|
||||
top = {32, 18},
|
||||
top_right = {44, 18},
|
||||
right = {44, 24},
|
||||
bottom_right = {44, 34},
|
||||
bottom = {32, 34},
|
||||
bottom_left = {20, 34},
|
||||
left = {32, 24},
|
||||
}
|
||||
|
||||
-- Top left
|
||||
local s = 1
|
||||
for k, v in pairs(all_coords_out) do
|
||||
move_corsor(s, unpack(v))
|
||||
assert(client.get()[s].screen == screen[s])
|
||||
awful.placement.resize_to_mouse(client.get()[s], {include_sides=true})
|
||||
mouse.push_history()
|
||||
assert(test_touch_mouse(client.get()[s]), k)
|
||||
s = s + 1
|
||||
end
|
||||
|
||||
for k, v in pairs(all_coords_in) do
|
||||
move_corsor(s, unpack(v))
|
||||
assert(client.get()[s].screen == screen[s])
|
||||
awful.placement.resize_to_mouse(client.get()[s], {include_sides=true})
|
||||
mouse.push_history()
|
||||
assert(test_touch_mouse(client.get()[s]), k)
|
||||
s = s + 1
|
||||
end
|
|
@ -1,6 +1,7 @@
|
|||
-- Align a client to the right of the parent area. --DOC_HEADER
|
||||
-- @tparam drawable d A drawable (like `client`, `mouse` or `wibox`) --DOC_HEADER
|
||||
-- @tparam[opt={}] table args Other arguments") --DOC_HEADER
|
||||
-- @treturn table The new geometry --DOC_HEADER
|
||||
-- @name right --DOC_HEADER
|
||||
-- @class function --DOC_HEADER
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
-- Stretch the drawable to the bottom of the parent area. --DOC_HEADER
|
||||
-- @tparam drawable d A drawable (like `client` or `wibox`) --DOC_HEADER
|
||||
-- @tparam[opt={}] table args Other arguments --DOC_HEADER
|
||||
-- @treturn table The new geometry --DOC_HEADER
|
||||
-- @name stretch_down --DOC_HEADER
|
||||
-- @class function --DOC_HEADER
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
-- Stretch the drawable to the left of the parent area. --DOC_HEADER
|
||||
-- @tparam drawable d A drawable (like `client` or `wibox`) --DOC_HEADER
|
||||
-- @tparam[opt={}] table args Other arguments --DOC_HEADER
|
||||
-- @treturn table The new geometry --DOC_HEADER
|
||||
-- @name stretch_left --DOC_HEADER
|
||||
-- @class function --DOC_HEADER
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
-- Stretch the drawable to the right of the parent area. --DOC_HEADER
|
||||
-- @tparam drawable d A drawable (like `client` or `wibox`) --DOC_HEADER
|
||||
-- @tparam[opt={}] table args Other arguments --DOC_HEADER
|
||||
-- @treturn table The new geometry --DOC_HEADER
|
||||
-- @name stretch_right --DOC_HEADER
|
||||
-- @class function --DOC_HEADER
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
-- Stretch the drawable to the top of the parent area. --DOC_HEADER
|
||||
-- @tparam drawable d A drawable (like `client` or `wibox`) --DOC_HEADER
|
||||
-- @tparam[opt={}] table args Other arguments --DOC_HEADER
|
||||
-- @treturn table The new geometry --DOC_HEADER
|
||||
-- @name stretch_up --DOC_HEADER
|
||||
-- @class function --DOC_HEADER
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
-- Align a client to the top of the parent area. --DOC_HEADER
|
||||
-- @tparam drawable d A drawable (like `client`, `mouse` or `wibox`) --DOC_HEADER
|
||||
-- @tparam[opt={}] table args Other arguments") --DOC_HEADER
|
||||
-- @treturn table The new geometry --DOC_HEADER
|
||||
-- @name top --DOC_HEADER
|
||||
-- @class function --DOC_HEADER
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
-- Align a client to the top left of the parent area. --DOC_HEADER
|
||||
-- @tparam drawable d A drawable (like `client`, `mouse` or `wibox`) --DOC_HEADER
|
||||
-- @tparam[opt={}] table args Other arguments") --DOC_HEADER
|
||||
-- @treturn table The new geometry --DOC_HEADER
|
||||
-- @name top_left --DOC_HEADER
|
||||
-- @class function --DOC_HEADER
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
-- Align a client to the top right of the parent area. --DOC_HEADER
|
||||
-- @tparam drawable d A drawable (like `client`, `mouse` or `wibox`) --DOC_HEADER
|
||||
-- @tparam[opt={}] table args Other arguments") --DOC_HEADER
|
||||
-- @treturn table The new geometry --DOC_HEADER
|
||||
-- @name top_right --DOC_HEADER
|
||||
-- @class function --DOC_HEADER
|
||||
|
||||
|
|
|
@ -166,6 +166,9 @@ client:_add_signal("property::fullscreen")
|
|||
client:_add_signal("property::border_width")
|
||||
client:_add_signal("property::hidden")
|
||||
client:_add_signal("property::screen")
|
||||
client:_add_signal("request::tag")
|
||||
client:_add_signal("request::geometry")
|
||||
client:_add_signal("request::activate")
|
||||
client:_add_signal("raised")
|
||||
client:_add_signal("lowered")
|
||||
client:_add_signal("list")
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
-- Test set_{,new}index_miss_handler
|
||||
|
||||
local mouse = mouse
|
||||
local class = tag
|
||||
local obj = class({})
|
||||
local handler = require("gears.object.properties")
|
||||
|
@ -30,4 +31,8 @@ assert(not obj.key)
|
|||
obj.key = 1337
|
||||
assert(obj.key == 1337)
|
||||
|
||||
-- The the custom mouse handler
|
||||
mouse.foo = "bar"
|
||||
assert(mouse.foo == "bar")
|
||||
|
||||
require("_runner").run_steps({ function() return true end })
|
||||
|
|
|
@ -0,0 +1,409 @@
|
|||
local test_client = require("_client")
|
||||
local placement = require("awful.placement")
|
||||
local amouse = require("awful.mouse")
|
||||
|
||||
local steps = {}
|
||||
|
||||
table.insert(steps, function(count)
|
||||
if count == 1 then -- Setup.
|
||||
test_client("foobar", "foobar")
|
||||
elseif #client.get() > 0 then
|
||||
|
||||
client.get()[1] : geometry {
|
||||
x = 200,
|
||||
y = 200,
|
||||
width = 300,
|
||||
height = 300,
|
||||
}
|
||||
|
||||
return true
|
||||
end
|
||||
end)
|
||||
|
||||
table.insert(steps, function()
|
||||
-- The mousegrabber expect a button to be pressed.
|
||||
root.fake_input("button_press",1)
|
||||
local c = client.get()[1]
|
||||
|
||||
-- Just in case there is an accidental delayed geometry callback
|
||||
assert(c:geometry().x == 200)
|
||||
assert(c:geometry().y == 200)
|
||||
assert(c:geometry().width == 300)
|
||||
assert(c:geometry().height == 300)
|
||||
|
||||
mouse.coords {x = 500+2*c.border_width, y= 500+2*c.border_width}
|
||||
|
||||
local corner = amouse.client.resize(c)
|
||||
|
||||
assert(corner == "bottom_right")
|
||||
|
||||
return true
|
||||
end)
|
||||
|
||||
-- The geometry should remain the same, as the cursor is placed at the end of
|
||||
-- the geometry.
|
||||
table.insert(steps, function()
|
||||
|
||||
local c = client.get()[1]
|
||||
|
||||
assert(c:geometry().x == 200)
|
||||
assert(c:geometry().y == 200)
|
||||
assert(c:geometry().width == 300)
|
||||
assert(c:geometry().height == 300)
|
||||
|
||||
mouse.coords {x = 600+2*c.border_width, y= 600+2*c.border_width}
|
||||
|
||||
return true
|
||||
end)
|
||||
|
||||
-- Grow the client by 100px
|
||||
table.insert(steps, function()
|
||||
|
||||
local c = client.get()[1]
|
||||
|
||||
assert(c:geometry().x == 200)
|
||||
assert(c:geometry().y == 200)
|
||||
assert(c:geometry().width == 400)
|
||||
assert(c:geometry().height == 400)
|
||||
|
||||
mouse.coords {x = 400+2*c.border_width, y= 400+2*c.border_width}
|
||||
|
||||
return true
|
||||
end)
|
||||
|
||||
-- Shirnk the client by 200px
|
||||
table.insert(steps, function()
|
||||
|
||||
local c = client.get()[1]
|
||||
|
||||
assert(c:geometry().x == 200)
|
||||
assert(c:geometry().y == 200)
|
||||
assert(c:geometry().width == 200)
|
||||
assert(c:geometry().height == 200)
|
||||
|
||||
mouse.coords {x = 100, y= 100}
|
||||
|
||||
return true
|
||||
end)
|
||||
|
||||
-- Grow the client by 100px from the top left
|
||||
table.insert(steps, function()
|
||||
|
||||
local c = client.get()[1]
|
||||
|
||||
assert(c:geometry().x == 100)
|
||||
assert(c:geometry().y == 100)
|
||||
assert(c:geometry().width == 300)
|
||||
assert(c:geometry().height == 300)
|
||||
|
||||
mouse.coords {x = 300, y= 200}
|
||||
|
||||
return true
|
||||
end)
|
||||
|
||||
-- Shirnk the client by 100px from the top right
|
||||
table.insert(steps, function()
|
||||
|
||||
local c = client.get()[1]
|
||||
|
||||
assert(c:geometry().x == 100)
|
||||
assert(c:geometry().y == 200)
|
||||
-- assert(c:geometry().width == 200-2*c.border_width) --FIXME off by border width...
|
||||
-- assert(c:geometry().height == 200-2*c.border_width) --FIXME off by border width...
|
||||
|
||||
mouse.coords {x = 300, y= 200}
|
||||
|
||||
return true
|
||||
end)
|
||||
|
||||
-- Stop the resize
|
||||
table.insert(steps, function()
|
||||
root.fake_input("button_release",1)
|
||||
|
||||
-- if not mousegrabber.isrunning then --FIXME it should work, but doesn't
|
||||
-- return true
|
||||
-- end
|
||||
|
||||
mousegrabber.stop()
|
||||
|
||||
return true
|
||||
end)
|
||||
|
||||
-- Setup for move testing
|
||||
table.insert(steps, function()
|
||||
assert(not mousegrabber.isrunning())
|
||||
|
||||
local c = client.get()[1]
|
||||
|
||||
c:geometry {
|
||||
width = 200,
|
||||
height = 200,
|
||||
}
|
||||
|
||||
placement.bottom_right(c)
|
||||
|
||||
mouse.coords {x = c.screen.geometry.width -150,
|
||||
y = c.screen.geometry.height-150}
|
||||
|
||||
|
||||
return true
|
||||
end)
|
||||
|
||||
-- Start the move mouse grabber
|
||||
table.insert(steps, function()
|
||||
local c = client.get()[1]
|
||||
|
||||
-- The resize is over, it should not move the client anymore
|
||||
assert(c:geometry().x == c.screen.geometry.width - 200 - 2*c.border_width)
|
||||
assert(c:geometry().y == c.screen.geometry.height - 200 - 2*c.border_width)
|
||||
assert(c:geometry().width == 200)
|
||||
assert(c:geometry().height == 200)
|
||||
|
||||
assert(c.valid)
|
||||
|
||||
root.fake_input("button_press",1)
|
||||
|
||||
-- Begin the move
|
||||
amouse.client.move(c)
|
||||
|
||||
-- Make sure nothing unwanted happen by accident
|
||||
assert(c:geometry().x == c.screen.geometry.width - 200 - 2*c.border_width)
|
||||
assert(c:geometry().y == c.screen.geometry.height - 200 - 2*c.border_width)
|
||||
assert(c:geometry().width == 200)
|
||||
assert(c:geometry().height == 200)
|
||||
|
||||
-- The cursor should not have moved
|
||||
assert(mouse.coords().x == c.screen.geometry.width - 150)
|
||||
assert(mouse.coords().y == c.screen.geometry.height - 150)
|
||||
mouse.coords {x = 50 + 2*c.border_width, y= 50 + 2*c.border_width}
|
||||
|
||||
assert(mousegrabber.isrunning())
|
||||
|
||||
return true
|
||||
end)
|
||||
|
||||
-- The client should now be in the top left
|
||||
table.insert(steps, function()
|
||||
local c = client.get()[1]
|
||||
assert(c:geometry().x == 0)
|
||||
assert(c:geometry().y == 0)
|
||||
assert(c:geometry().width == 200)
|
||||
assert(c:geometry().height == 200)
|
||||
|
||||
-- Move to the bottom left
|
||||
mouse.coords {
|
||||
x = 50 + 2*c.border_width,
|
||||
y = c.screen.geometry.height - 200
|
||||
}
|
||||
|
||||
return true
|
||||
end)
|
||||
|
||||
-- Ensure the move was correct, the snap to the top part of the screen
|
||||
table.insert(steps, function()
|
||||
local c = client.get()[1]
|
||||
|
||||
assert(c:geometry().x == 0)
|
||||
assert(c:geometry().y == c.screen.geometry.height - 250 - 2*c.border_width)
|
||||
assert(c:geometry().width == 200)
|
||||
assert(c:geometry().height == 200)
|
||||
|
||||
-- Should trigger the top snap
|
||||
mouse.coords {x = 600, y= 0}
|
||||
|
||||
-- The snap is only applied on release
|
||||
root.fake_input("button_release",1)
|
||||
|
||||
return true
|
||||
end)
|
||||
|
||||
-- The client should now fill the top half of the screen
|
||||
table.insert(steps, function()
|
||||
local c = client.get()[1]
|
||||
|
||||
assert(c:geometry().x == c.screen.workarea.x )
|
||||
assert(c:geometry().y == c.screen.workarea.y )
|
||||
assert(c:geometry().width == c.screen.workarea.width - 2*c.border_width )
|
||||
assert(c:geometry().height == math.ceil(c.screen.workarea.height/2 - 2*c.border_width))
|
||||
|
||||
-- Snap to the top right
|
||||
root.fake_input("button_press",1)
|
||||
amouse.client.move(c)
|
||||
placement.top_right(mouse, {honor_workarea=false})
|
||||
root.fake_input("button_release",1)
|
||||
|
||||
return true
|
||||
end)
|
||||
|
||||
-- The client should now fill the top right corner of the screen
|
||||
table.insert(steps, function()
|
||||
local c = client.get()[1]
|
||||
|
||||
assert(c:geometry().x == c.screen.workarea.x+c.screen.workarea.width/2 )
|
||||
assert(c:geometry().y == c.screen.workarea.y )
|
||||
assert(c:geometry().width == math.ceil(c.screen.workarea.width/2 - 2*c.border_width) )
|
||||
assert(c:geometry().height == math.ceil(c.screen.workarea.height/2 - 2*c.border_width))
|
||||
|
||||
-- Snap to the top right
|
||||
root.fake_input("button_press",1)
|
||||
amouse.client.move(c)
|
||||
placement.right(mouse, {honor_workarea=false})
|
||||
root.fake_input("button_release",1)
|
||||
|
||||
return true
|
||||
end)
|
||||
|
||||
-- The client should now fill the top right half of the screen
|
||||
table.insert(steps, function()
|
||||
local c = client.get()[1]
|
||||
|
||||
assert(c:geometry().x == c.screen.workarea.x+c.screen.workarea.width/2 )
|
||||
assert(c:geometry().y == c.screen.workarea.y )
|
||||
assert(c:geometry().width == math.ceil(c.screen.workarea.width/2 - 2*c.border_width))
|
||||
assert(c:geometry().height == c.screen.workarea.height - 2*c.border_width )
|
||||
|
||||
-- Snap to the top right
|
||||
root.fake_input("button_press",1)
|
||||
amouse.client.move(c)
|
||||
placement.bottom(mouse, {honor_workarea=false})
|
||||
root.fake_input("button_release",1)
|
||||
|
||||
return true
|
||||
end)
|
||||
|
||||
-- The client should now fill the bottom half of the screen
|
||||
table.insert(steps, function()
|
||||
local c = client.get()[1]
|
||||
|
||||
assert(c:geometry().x == c.screen.workarea.x )
|
||||
assert(c:geometry().y == c.screen.workarea.y+c.screen.workarea.height/2 )
|
||||
assert(c:geometry().width == c.screen.workarea.width - 2*c.border_width )
|
||||
assert(c:geometry().height == math.ceil(c.screen.workarea.height/2 - 2*c.border_width))
|
||||
|
||||
-- Snap to the top right
|
||||
root.fake_input("button_press",1)
|
||||
amouse.client.move(c)
|
||||
placement.bottom_left(mouse, {honor_workarea=false})
|
||||
root.fake_input("button_release",1)
|
||||
|
||||
return true
|
||||
end)
|
||||
|
||||
-- The client should now fill the bottom left corner of the screen
|
||||
table.insert(steps, function()
|
||||
local c = client.get()[1]
|
||||
|
||||
assert(c:geometry().x == c.screen.workarea.x )
|
||||
assert(c:geometry().y == c.screen.workarea.y+c.screen.workarea.height/2 )
|
||||
assert(c:geometry().width == math.ceil(c.screen.workarea.width/2 - 2*c.border_width) )
|
||||
assert(c:geometry().height == math.ceil(c.screen.workarea.height/2 - 2*c.border_width))
|
||||
|
||||
-- Snap to the top right
|
||||
root.fake_input("button_press",1)
|
||||
amouse.client.move(c)
|
||||
placement.left(mouse, {honor_workarea=false})
|
||||
root.fake_input("button_release",1)
|
||||
|
||||
return true
|
||||
end)
|
||||
|
||||
-- The client should now fill the left half of the screen
|
||||
table.insert(steps, function()
|
||||
local c = client.get()[1]
|
||||
|
||||
assert(c:geometry().x == c.screen.workarea.x )
|
||||
assert(c:geometry().y == c.screen.workarea.y )
|
||||
assert(c:geometry().width == math.ceil(c.screen.workarea.width/2 - 2*c.border_width) )
|
||||
assert(c:geometry().height == c.screen.workarea.height - 2*c.border_width )
|
||||
|
||||
-- Snap to the top right
|
||||
root.fake_input("button_press",1)
|
||||
amouse.client.move(c)
|
||||
placement.top_left(mouse, {honor_workarea=false})
|
||||
root.fake_input("button_release",1)
|
||||
|
||||
return true
|
||||
end)
|
||||
|
||||
local cur_tag = nil
|
||||
|
||||
-- The client should now fill the top left corner of the screen
|
||||
table.insert(steps, function()
|
||||
local c = client.get()[1]
|
||||
|
||||
assert(c:geometry().x == c.screen.workarea.x )
|
||||
assert(c:geometry().y == c.screen.workarea.y )
|
||||
assert(c:geometry().width == math.ceil(c.screen.workarea.width/2 - 2*c.border_width) )
|
||||
assert(c:geometry().height == math.ceil(c.screen.workarea.height/2 - 2*c.border_width))
|
||||
|
||||
-- Change the mode to test drag_to_tag
|
||||
amouse.drag_to_tag.enabled = true
|
||||
amouse.snap.edge_enabled = false
|
||||
|
||||
cur_tag = c.first_tag
|
||||
|
||||
root.fake_input("button_press",1)
|
||||
amouse.client.move(c)
|
||||
placement.right(mouse, {honor_workarea=false})
|
||||
root.fake_input("button_release",1)
|
||||
|
||||
return true
|
||||
end)
|
||||
|
||||
-- The tag should have changed
|
||||
table.insert(steps, function()
|
||||
local c = client.get()[1]
|
||||
|
||||
assert(c.first_tag ~= cur_tag )
|
||||
assert(c.first_tag.index == cur_tag.index + 1)
|
||||
|
||||
-- Move it back
|
||||
root.fake_input("button_press",1)
|
||||
amouse.client.move(c)
|
||||
placement.left(mouse, {honor_workarea=false})
|
||||
root.fake_input("button_release",1)
|
||||
|
||||
return true
|
||||
end)
|
||||
|
||||
-- The tag should now be the same as before
|
||||
table.insert(steps, function()
|
||||
local c = client.get()[1]
|
||||
|
||||
assert(c.first_tag == cur_tag)
|
||||
assert(c.first_tag.index == 1)
|
||||
|
||||
-- Wrap
|
||||
root.fake_input("button_press",1)
|
||||
amouse.client.move(c)
|
||||
placement.left(mouse, {honor_workarea=false})
|
||||
root.fake_input("button_release",1)
|
||||
|
||||
return true
|
||||
end)
|
||||
|
||||
-- The tag should now be the last
|
||||
table.insert(steps, function()
|
||||
local c = client.get()[1]
|
||||
|
||||
assert(c.first_tag.index == #c.screen.tags)
|
||||
|
||||
-- Wrap back
|
||||
root.fake_input("button_press",1)
|
||||
amouse.client.move(c)
|
||||
placement.right(mouse, {honor_workarea=false})
|
||||
root.fake_input("button_release",1)
|
||||
|
||||
return true
|
||||
end)
|
||||
|
||||
-- The tag should now original one again
|
||||
table.insert(steps, function()
|
||||
local c = client.get()[1]
|
||||
|
||||
assert(c.first_tag == cur_tag)
|
||||
|
||||
return true
|
||||
end)
|
||||
|
||||
require("_runner").run_steps(steps)
|
Loading…
Reference in New Issue