awesome-wm wibox not reacting to mouse signals when input_passthrough is true - awesome-wm

Basically, what I'm trying to do is have a sidebar, and have it show up when I move my mouse to the left edge of the screen, and have the sidebar disappear when I move my mouse TOO MUCH away from the sidebar.
So I made three widgets:
one that's one pixel wide on the side and that, when it detects the mouse entering, shows the sidebar
the actual sidebar
and a widget that's wider than the sidebar, is fully transparent, has input_passthrough set to true, and its only purpose is to look for the "mouse::leave" signal and when the mouse leaves, it would make itself and the sidebar disappear.
I got most of it to work but there's this particular error that I can't solve:
The wibox function takes as argument a table of fields. If you look through the code I provided, you can notice that the input_passthrough field of the sidebar_visible_limit hasn't been put in the body of the table, but supplied afterwards, right after the creation of the widget.
The problem with this is that it simply doesn't close the sidebar or itself when the mouse leaves. It doesn't detect the mouse leaving.
BUT if put the input_passthrough = true in the table provided to the wibox function, like this:
bar.sidebar_visible_limit = wibox({
x = 0,
y = 0,
ontop = false,
visible = false,
width = bar.sidebar.width + dpi(100),
height = bar.sidebar.height,
bg = '#000000',
opacity = 0.3, -- when it's all done this will be '0'
input_passthrough = true
})
then everything works fine, EXCEPT that now it doesn't allow input to pass through.
I would greatly appreciate an explanation as to why this happens.
This is the code:
awful = require("awful")
local wibox = require("wibox")
local naughty = require("naughty")
local gears = require("gears")
local beautiful = require("beautiful")
xresources = require("beautiful.xresources")
dpi = xresources.apply_dpi
bar = {}
-- make the sidebar
bar.sidebar = wibox({
x = 0,
y = 0,
ontop = false,
visible = false,
width = beautiful.sidebar_width or dpi(450),
bg = beautiful.sidebar_bg or "#2f2e3a",
type = "dock",
height = beautiful.sidebar_height or awful.screen.focused().geometry.height,
})
-- Hide sidebar when mouse leaves too much from the sidebar
-- It's incorporated along in the same table with the sidebar so the users
-- can implement these however they want, e.g. in the 'keys.lua' module
bar.sidebar_visible_limit = wibox({
x = 0,
y = 0,
ontop = false,
visible = false,
width = bar.sidebar.width + dpi(100),
height = bar.sidebar.height,
bg = '#000000',
opacity = 0.3, --when it's all done this will be '0'
})
bar.sidebar_visible_limit.input_passthrough = true
-- Show sidebar when mouse touches edge
local sidebar_displayer = wibox({
x = 0,
y = 0,
height = bar.sidebar.height,
ontop = true,
width = 1,
visible = true,
opacity = 0,
input_passthrough = true
})
function toggle_bar()
-- they have to be in this order, so the sidebar will show first,
-- and then the wibox that will close the sidebar when the mouse leaves
-- second. If you do it the other way around, then if you go with the
-- mouse on the sidebar-closing wibox , then if you try to go back
-- to the sidebar, it will close it because it's 'left' the widget.
-- That's why you have the sidebar-closing wibox on top and allow
-- input_passthrough for the sidebar-closing wibox
bar.sidebar.visible = not bar.sidebar.visible
bar.sidebar.ontop = not bar.sidebar.ontop
bar.sidebar_visible_limit.visible = not bar.sidebar_visible_limit.visible
bar.sidebar_visible_limit.ontop = not bar.sidebar_visible_limit.ontop
end
bar.sidebar_visible_limit:connect_signal( "mouse::leave", toggle_bar )
sidebar_displayer:connect_signal( "mouse::enter", toggle_bar )

You could use a mousegrabber to detect the position of the mouse:
bar.sidebar.ontop = true
function show_sidebar()
bar.sidebar.visible = true
mousegrabber.run(function(mouse)
if mouse.x > bar.sidebar.width + dpi(100) then
hide_sidebar()
return false
end
return true
end)
end
function hide_sidebar()
bar.sidebar.visible = false
end
Alternatively, you could use the same mousegrabber to decide when to show the sidebar too:
bar.sidebar.ontop = true
function show_sidebar()
bar.sidebar.visible = true
end
function hide_sidebar()
bar.sidebar.visible = false
end
mousegrabber.run(function(mouse)
if bar.sidebar.visible then
if mouse.x > bar.sidebar.width + dpi(100) then
hide_sidebar()
end
else
if mouse.x == 1 then
show_sidebar()
end
end
return true
end)
I think this is nicer than creating an invisible wibox over the whole screen.

I would greatly appreciate an explanation as to why this happens.
I think the X11 server reports mouse enter/exit relative to the input region of a window. With input_passthrough, the input region becomes empty. This means that the X11 server will now report the mouse pointer to be inside the window beneath your wibox and not inside of the wibox itself.
BUT if put the input_passthrough = true in the table provided to the wibox function then everything works fine, EXCEPT that now it doesn't allow input to pass through.
In other words: In this case the input_passthrough property is not set. Seems like you found one of the properties that cannot be set this way. ;-)
Since I guess you also want some ideas on how you can do what you are trying to do: If you are running a compositing manager (xcompmgr, compton, ...), you could make a wibox with a completely transparent background. That way, the X11 server will "think" that the window is there and report input events relative to it, but the wibox will not actually be visible on screen.
(And if you don't have a compositing manager: Make a widget that displays your wallpaper with the proper offset. A hint on how to do this can be found by reading /usr/share/awesome/lib/wibox/drawable.lua, the part that begins with if not capi.awesome.composite_manager_running then and that does something with the wallpaper. Feel free to ask for more details if necessary.)

Related

Closing window when object reaches bottom

I want to display a message, and close the window when the user clicks. This should happen when the circle reaches the bottom of the window. I'm not sure how to go about this, everything works fine until the circle passes the bottom of the window, the closing message doesn't pop up and the window doesnt close on click. I'm using the graphics.py graphics library from Zelle for Python. I'm a beginner in Python so my knowledge is very limited right now. My code is as follows:
from graphics import *
def q2a():
win = GraphWin("window",400,400)
win.setCoords(0,0,400,400)
win.setBackground("light grey")
#drawing circle
circle = Circle(Point(200,100),30)
circle.setFill("red")
circle.draw(win)
#text
message = Text(Point(200,200),"Click Anywhere to Begin")
message.draw(win)
#clicking
while True:
click = win.checkMouse()
if click:
message.undraw()
while circle.getCenter().getY() < 170:
dy=1
dx = 0
dy *=-.01
circle.move(dx,dy)
if circle.getCenter()== 0:
circle.undraw()
gameover = Text(Point(200,200),"Game Over - Click to Close")
gameover.draw(win)
win.checkMouse()
win.close()
q2a()
I believe the problem is simpler than you're making it. One problem is that this is an infinite loop:
while circle.getCenter().getY() < 170:
dy=1
dx = 0
dy *=-.01
circle.move(dx,dy)
As circle's Y center starts at 100 and decreases, so it's always less than 170 so this loop never finishes and any code beyond this point is never executed. Let's use the circle's radius, 30, instead so the circle stops when it sits on the bottom of the window.
Another issue is that I believe you're using checkMouse() when you really want getMouse(). Read the documentation about the difference between these two commands.
Here's my rework of your code (with some style tweaks.) I changed the -0.01 increment to -0.1 as I've no patience!
from graphics import *
RADIUS = 30
HEIGHT, WIDTH = 400, 400
CENTER = Point(HEIGHT / 2, WIDTH / 2)
def q2a():
win = GraphWin("window", HEIGHT, WIDTH)
win.setCoords(0, 0, HEIGHT, WIDTH)
win.setBackground("light grey")
# drawing circle
circle = Circle(Point(WIDTH / 2, 100), RADIUS)
circle.setFill("red")
circle.draw(win)
# text
message = Text(CENTER, "Click Anywhere to Begin")
message.draw(win)
# moving
win.getMouse()
message.undraw()
while circle.getCenter().getY() > RADIUS:
circle.move(0, -0.1)
# end game
circle.undraw()
gameover = Text(CENTER, "Game Over - Click to Close")
gameover.draw(win)
win.getMouse()
win.close()
q2a()

how to stop Vpython from being unresponsive

Im fairly new to Vpython and Ive created a simulation for circular motion. I tried adding controls to the simulation and so far I am able to pause the simulation but when i do pause it, the entire control panel becomes unresponsive and i am unable to resume the simulation. Does anyone know why this may be or how i can fix this?
def playorpause(self, goorno):
self.pause = goorno
def actual_sim(self):
c = controls() # Create controls window
# Create a button in the controls window:
# b = Button(pos=(-50, 0), width=60, height=60, text="Pause", action=lambda: self.change)
# play = Button(pos=(50, 0), width=60, height=60, text="Play", command = self.play_sim)
b = button(pos=(-50, 0), width=60, height=60, text='Pause', action=lambda: self.playorpause(True))
play = button(pos=(50,0), width = 60, height = 60, text = "play", action =lambda: self.playorpause(False))
def loop():
#### simulation code
if self.pause == False:
loop()
I'm not sure, because I don't really understand your code, but a repetitive loop needs to include a rate statement. Otherwise the program will lock up, unable to output anything.

Knowing button press in corona sdk

In my corona aplication, I've a widget button to move an image. I was able to find the onPress method, but failed to find a method to check whether the button is still pressed. So that user don't have to tap the same button over and over again for moving the image...
Code:
function move( event )
local phase = event.phase
if "began" == phase then
define_robo()
image.x=image.x+2;
end
end
local movebtn = widget.newButton
{
width = 50,
height = 50,
defaultFile = "left.png",
overFile = "left.png",
onPress = move,
}
Any help is appreciable...
If your question is that you would like to know when the user's finger is moved, or when he releases the button, you can add handlers for those events:
"moved" a finger moved on the screen.
"ended" a finger was lifted from the screen.
"began" only handles when he starts touching the screen.
So your move function would be like:
function move( event )
local phase = event.phase
if "began" == phase then
define_robo()
image.x=image.x+2;
elseif "moved" == phase then
-- your moved code
elseif "ended" == phase then
-- your ended code
end
end
-- Updated according to comment:
Use this, replacing nDelay by the delay between each move, and nTimes by the number of times you wanna do the move:
function move( event )
local phase = event.phase
if "began" == phase then
local nDelay = 1000
local nTimes = 3
define_robo()
timer.performWithDelay(nDelay, function() image.x=image.x+2 end, nTimes )
end
end
Try this:
local image = display.newRect(100,100,50,50) -- Your image
local timer_1 -- timer
local function move()
print("move...")
image.x=image.x+2;
timer_1 = timer.performWithDelay(10,move,1) -- you can set the time as per your need
end
local function stopMove()
print("stopMove...")
if(timer_1)then timer.cancel(timer_1) end
end
local movebtn = widget.newButton {
width = 50,
height = 50,
defaultFile = "obj1.png",
overFile = "obj1.png",
onPress = move, -- This will get called when you 'press' the button
onRelease = stopMove, -- This will get called when you 'release' the button
}
Keep coding................ :)

How can I remove onEvent from button widget in Corona?

I am trying to remove onEvent listener from button widget. I tried to assign nil to onEvent attribute but it didn't work and lastly I tried this:
buttonWidget : removeEventListener("touch", buttonWidget.onEvent)
I have several button like that and it just stopped all button's event listeners. What do you suggest? How can I remove the event listener for one button widget? Thanks.
Here is how I create my button widgets:
for i=0,2 do
for j=0,8 do
count=count+1
letterBtn[count] = widget.newButton{
id = alphabet[count],
left = 5+j*50,
top = H-160+i*50,
label = alphabet[count],
width = 45,
height = 45,
font = nil,
fontSize = 18,
labelColor = { default = {0,0,0}, over = {255,255,255}},
onEvent = btnOnEventHandler
};
end
end
Can you tell me how can I remove onEvent later?
Okey, I tried Button: setEnabled(false) but still it disables all buttons not just one. I already tried your second advice but it gives the same result. I am copying the rest of the code. Can you please look at it and tell me what I am missing?
local function checkLetter(e)
if(guessWord) then
for i=1, #guessWord do
local c = guessWord:sub(i,i)
if c==e.target.id then
letter[i].text = e.target.id
letterCount = letterCount +1
print("letterCount"..letterCount)
e.target:setEnabled(false)
end
end
if (letterCount == #guessWord and not hanged) then
timer.performWithDelay(500, function()
letterCount=0
rightWGuess = rightWGuess+1
for k,v in pairs(notGuessedWord) do
if v == guessWord then
notGuessedWord[k]=nil
end
end
enableButtons()
startGame() end ,1)
end
end
end
local function btnOnEventHandler(e)
if(e.phase == "began") then
checkLetter(e)
print(e.target.id)
end
return true
end
If you want to temporarily (or permanently) stop a button from responding to touch events, you can use Button:setEnabled(false).
The following worked for me for removing a listener from just 2 buttons. Button 1 and 3 stopped responding to events as expected while 2, 4, and 5 still did.
Update: To disable, you have to do it on the 'ended' phase or Corona gets confused.
widget = require 'widget'
local function btnOnEventHandler(event)
print('Event', event.target.id, event.phase)
if event.phase == 'ended' then
-- Disable the button so it can't be clicked again
-- Must disable in the end state or Corona gets
-- confused
event.target:setEnabled(false)
end
end
local buttons = {}
for i=1,5 do
buttons[i] = widget.newButton{
id = 'button' .. i,
left = display.contentCenterX - 50,
top = 60 * i,
label = 'Button ' .. i,
width = 100,
height = 50,
onEvent = btnOnEventHandler
}
end
buttons[1]:removeEventListener('touch', buttons[1].onEvent)
buttons[3]:removeEventListener('touch', buttons[3].onEvent)

Setting windows layout for a specific application in awesome-wm

How to config awesome so it would start new application with two windows aligned like this:
----------------
|xxxxxxxxxx####|
|xxxxxxxxxx####|
|xxxxxxxxxx####|
|xxxxxxxxxx####|
----------------
where "x" is for example conversation window in pidgin and '#' is buddy list window.
In general I would like to specify width of right window and put it on the right side (maximized vertically) and the other window should take the remaining space.
I already have some almost-working code, but it behaves strangely (it setups everything correct for pidgin, but it doesn't for gimp and v_sim, and occasionally without any known to me reason it changes geometry of the left window. Or when I start application (v_sim) it isn't placed in correct positions and it isn't maximized vertically, but when I then restart awesome, it places it correctly. So I guess that this application changes something when it starts.
Here is code which I use now:
awful.rules.rules = {
...
{ rule = { class = "Pidgin", role = "buddy_list" },
properties = {
floating = true
},
callback = function( c )
local w_area = screen[ c.screen ].workarea
local winwidth = 340
c:struts( { right = winwidth } )
c:geometry( { x = w_area.width - winwidth, width = winwidth, y = w_area.y, height = w_area.height } )
end
},
{ rule = { class = "Pidgin", role = "conversation" },
properties = {
floating = true,
x = 0,
maximized_vertical = true,
maximized_horizontal = true
},
callback = awful.client.setslave
},
...
}
I had this exact same problem, but I wanted a large Firefox window on the left with a small terminal on the right. To get it to work I dedicated a tag for this purpose with a tile-left layout and adjusted the width factor (i.e. the operation normally performed by CTRL-L).
Add the following to the end of rc.lua where yourtag is the tag in which you would like to place these windows. The 0.15 value can be adjusted to your taste.
awful.tag.viewonly(yourtag)
awful.tag.incmwfact(0.15, yourtage)
Also, using the awful.client.setslave for the window that you want on the right ensures that they don't get switched.
{
rule = { class = "URxvt" },
callback = awful.client.setslave
},
You may also direct certain applications to a tag using the tag property.
{
rule = { class = "Firefox" },
properties = { tag = browse }
},
{
rule = { class = "URxvt", instance = "browse" },
properties = { tag = browse },
},
I then created a binding to open these applications as follows.
-- Custom programs
awful.key({ modkey, "Shift" }, "b", function()
awful.tag.viewonly(browse)
awful.util.spawn_with_shell("urxvt -name browse -e newsbeuter")
awful.util.spawn("firefox")
end)
This is the final result:
Alternatively, you can use a floating contact list window with struts.
This prevents the contact list window from being maximized when no message-window is present.
Also, it allows the CL-window to be placed next to arbitrary (tiling) windows.
Check out: http://www.bramschoenmakers.nl/en/node/738
Although his implementation is a bit buggy for my version of awesome.
The problem is that it does not adjust for struts that have already been set.
My implementation:
{ rule = { class = "Pidgin", role = "buddy_list" },
properties = {floating=true,
maximized_vertical=true, maximized_horizontal=false },
callback = function (c)
local cl_width = 250 -- width of buddy list window
local scr_area = screen[c.screen].workarea
local cl_strut = c:struts()
-- scr_area is affected by this client's struts, so we have to adjust for that
if c:isvisible() and cl_strut ~= nil and cl_strut.left > 0 then
c:geometry({x=scr_area.x-cl_strut.left, y=scr_area.y, width=cl_strut.left})
-- scr_area is unaffected, so we can use the naive coordinates
else
c:struts({left=cl_width, right=0})
c:geometry({x=scr_area.x, y=scr_area.y, width=cl_width})
end
end },
This puts the CL window on the left and allocating a fixed space for it.
(You don't need any rule for the conversation-window)

Resources