mirror of
https://github.com/vidarh/rubywm.git
synced 2024-11-15 19:48:30 +01:00
Support resize of tiling windows
This commit is contained in:
parent
3c518cfc20
commit
3b7ccb6f4a
5 changed files with 142 additions and 79 deletions
|
@ -5,6 +5,7 @@ class Desktop
|
|||
def initialize(wm, id, name) = (@wm, @id, @name = wm, id, name)
|
||||
def active? = (@wm.current_desktop_id == self.id)
|
||||
def children = @wm.windows.values.find_all{_1.desktop==self}
|
||||
def mapped_regular_children = children.find_all{_1.mapped && !_1.special?}
|
||||
def show = children.each(&:show)
|
||||
def hide = children.each(&:hide)
|
||||
def inspect = "<Desktop id=#{id} window=#{@window}>"
|
||||
|
|
20
geom.rb
20
geom.rb
|
@ -8,16 +8,26 @@ def gap(geom,g)
|
|||
geom
|
||||
end
|
||||
|
||||
def split_geom(geom, dir, node, gap)
|
||||
def split_geom(geom, dir, node, gap, ratio)
|
||||
geom = geom.dup
|
||||
case dir
|
||||
when :lr
|
||||
geom.width = (geom.width-gap)/2
|
||||
geom.x += geom.width+gap if node == 1
|
||||
lw = (geom.width-gap)*ratio
|
||||
if node == 0
|
||||
geom.width = lw
|
||||
else
|
||||
geom.width = (geom.width-gap)*(1.0-ratio)
|
||||
geom.x += lw + gap
|
||||
end
|
||||
return geom
|
||||
when :tb
|
||||
geom.height = (geom.height-gap)/2
|
||||
geom.y += geom.height+gap if node == 1
|
||||
th = (geom.height-gap)*ratio
|
||||
if node == 0
|
||||
geom.height = th
|
||||
else
|
||||
geom.height = (geom.height-gap)*(1.0-ratio)
|
||||
geom.y += th + gap
|
||||
end
|
||||
return geom
|
||||
else raise "Invalid direction"
|
||||
end
|
||||
|
|
13
node.rb
13
node.rb
|
@ -1,14 +1,16 @@
|
|||
class Node
|
||||
attr_accessor :split, :nodes, :dir, :parent
|
||||
attr_accessor :ratio, :nodes, :dir, :parent
|
||||
attr_reader :geom
|
||||
|
||||
def inspect = "<Node @nodes=#{@nodes.inspect}, @dir=#{@dir.inspect} @split=#{@split.inspect} @parent=#{@parent.object_id}>"
|
||||
def inspect = "<Node #{object_id} @nodes=#{@nodes.inspect}, @dir=#{@dir.inspect} @ratio=#{@ratio.inspect} @parent=#{@parent.object_id}>"
|
||||
|
||||
def initialize(nodes=[], parent: nil, dir: nil)
|
||||
@nodes = Array(nodes.dup)
|
||||
@nodes.each{|n| n.parent = self }
|
||||
@parent = parent
|
||||
@split = 0.5
|
||||
@ratio = 0.5
|
||||
@dir = dir
|
||||
@geom = nil # *current* geometry, subject to change at all time
|
||||
end
|
||||
|
||||
# FIXME: Restate children, placements, find in terms of
|
||||
|
@ -47,6 +49,7 @@ class Node
|
|||
@dir ||= dir
|
||||
dir = @dir
|
||||
nextdir = {lr: :tb, tb: :lr}[dir]
|
||||
@geom = geom.dup
|
||||
case @nodes.length
|
||||
when 0
|
||||
when 1
|
||||
|
@ -57,8 +60,8 @@ class Node
|
|||
end
|
||||
@nodes[0].layout(g, dir, level+1)
|
||||
when 2
|
||||
@nodes[0].layout(split_geom(geom, dir, 0,gap), gap, nextdir, level+1)
|
||||
@nodes[1].layout(split_geom(geom, dir, 1,gap), gap, nextdir, level+1)
|
||||
@nodes[0].layout(split_geom(geom, dir, 0,gap, @ratio), gap, nextdir, level+1)
|
||||
@nodes[1].layout(split_geom(geom, dir, 1,gap, @ratio), gap, nextdir, level+1)
|
||||
else
|
||||
STDERR.puts "WARNING: Too many nodes"
|
||||
end
|
||||
|
|
67
rubywm.rb
67
rubywm.rb
|
@ -18,16 +18,13 @@ Thread.abort_on_exception = true
|
|||
dpy = X11::Display.new
|
||||
|
||||
# FIXME: This is a workaround for a deadlock
|
||||
dpy.atom(:WM_CLASS)
|
||||
dpy.atom(:STRING)
|
||||
#dpy.atom(:WM_CLASS)
|
||||
#dpy.atom(:STRING)
|
||||
|
||||
|
||||
$wm = WindowManager.new(dpy, num_desktops: 10)
|
||||
wm = WindowManager.new(dpy, num_desktops: 10)
|
||||
|
||||
start = nil
|
||||
attr = nil
|
||||
|
||||
d = TypeDispatcher.new($wm)
|
||||
d = TypeDispatcher.new(wm)
|
||||
d.on(:client_message) do |ev|
|
||||
data = ev.data.unpack("V*")
|
||||
name = dpy.get_atom_name(ev.type)
|
||||
|
@ -36,7 +33,6 @@ d.on(:client_message) do |ev|
|
|||
end
|
||||
|
||||
loop do
|
||||
# FIXME: Select properly
|
||||
ev = nil
|
||||
begin
|
||||
ev = dpy.next_packet
|
||||
|
@ -47,58 +43,5 @@ loop do
|
|||
end
|
||||
|
||||
p ev
|
||||
|
||||
case ev
|
||||
when X11::Form::ButtonPress
|
||||
if ev.child # Whichever button, we want to know more about this window
|
||||
w = $wm.window(ev.child)
|
||||
attr = w.get_geometry rescue nil
|
||||
if attr
|
||||
$wm.set_focus(w.wid)
|
||||
start = ev
|
||||
end
|
||||
end
|
||||
when X11::Form::MotionNotify # if start.button == 1 we move; if 3 we resize, all with the same request:
|
||||
# TODO: If floating, do this; if tiling, find neighours and resize them too.
|
||||
|
||||
if ev.child != start.child
|
||||
$wm.set_focus(ev.child)
|
||||
end
|
||||
|
||||
p [:MOTION, start, attr]
|
||||
if start
|
||||
xdiff = ev.root_x - start.root_x;
|
||||
ydiff = ev.root_y - start.root_y;
|
||||
|
||||
if start&.child && attr
|
||||
w = $wm.window(start.child)
|
||||
|
||||
# FIXME: Any other types we don't want to allow moving or resizing
|
||||
begin
|
||||
next if w.special?
|
||||
rescue # FIXME
|
||||
end
|
||||
p w
|
||||
if start.detail == 1 # Move
|
||||
w.configure(x: attr.x + xdiff, y: attr.y + ydiff)
|
||||
elsif start.detail == 3 # Resize
|
||||
# If left/above the centre point, we grow/shrink the window to the left/top
|
||||
# otherwise to the right/bottom. Doing it to the left/top requires
|
||||
# moving it at the same time.
|
||||
attr.x = attr.x + (ev.event_x-attr.x < attr.width / 2 ? xdiff : 0)
|
||||
attr.y = attr.y + (ev.event_y-attr.y < attr.height/ 2 ? ydiff : 0)
|
||||
attr.width = attr.width + (ev.event_x-attr.x < attr.width / 2 ? -xdiff : xdiff)
|
||||
attr.height = attr.height+ (ev.event_y-attr.y < attr.height / 2 ? -ydiff : ydiff)
|
||||
start.root_x = ev.root_x
|
||||
start.root_y = ev.root_y
|
||||
w.configure(x: attr.x, y: attr.y, width: attr.width, height: attr.height)
|
||||
end
|
||||
end
|
||||
end
|
||||
when X11::Form::ButtonRelease
|
||||
# Make sure we don't accidentally operate on another window.
|
||||
start.child = nil if start
|
||||
else
|
||||
d.(ev.class, ev)
|
||||
end
|
||||
d.(ev.class, ev)
|
||||
end
|
||||
|
|
120
wm.rb
120
wm.rb
|
@ -23,6 +23,7 @@ class WindowManager
|
|||
# FIXME: Config
|
||||
# FIXME: Improved way of specifying pre-designed layouts.
|
||||
r = desktops[1].layout.root
|
||||
r.ratio = 0.5
|
||||
r.nodes[0] = Leaf.new(iclass: "todo-todo")
|
||||
r.nodes[1] = Node.new([
|
||||
Leaf.new(iclass: "todo-done"),
|
||||
|
@ -214,7 +215,7 @@ class WindowManager
|
|||
# FIXME: Switch focus (keep focus stack per desktop)
|
||||
old.hide
|
||||
change_property(:_NET_CURRENT_DESKTOP, :cardinal, d)
|
||||
f = current_desktop&.children&.find {|w| !w.special?}
|
||||
f = current_desktop&.mapped_regular_children&.first
|
||||
set_focus(f.wid) if f
|
||||
end
|
||||
|
||||
|
@ -238,7 +239,7 @@ class WindowManager
|
|||
# FIXME: This needs tweaks. Especially for floating windows, where
|
||||
# what we really want is to e.g. treat partially overlapping windows
|
||||
# so that the one closest to *overlapping* the correct border is picked
|
||||
def find_closest(w, dir, from = windows.values)
|
||||
def find_closest(w, dir, from)
|
||||
g = w.get_geometry
|
||||
|
||||
case dir
|
||||
|
@ -252,8 +253,6 @@ class WindowManager
|
|||
list = []
|
||||
p [:here]
|
||||
from.each do |win|
|
||||
next if win.special?
|
||||
next if !win.mapped
|
||||
p [:checking, win, dir]
|
||||
g2 = win.get_geometry rescue nil
|
||||
next if g2.nil?
|
||||
|
@ -312,6 +311,114 @@ class WindowManager
|
|||
p dpy.get_atom_name(ev.atom) rescue nil
|
||||
end
|
||||
|
||||
def on_button_press(ev)
|
||||
return if !ev.child
|
||||
w = window(ev.child)
|
||||
@attr = w.get_geometry rescue nil
|
||||
if @attr
|
||||
set_focus(w.wid)
|
||||
@start = ev
|
||||
end
|
||||
end
|
||||
|
||||
# FIXME: This does not yet handle tiling
|
||||
def on_motion_notify(ev)
|
||||
# @start.button == 1 -> move
|
||||
# @start.button == 3 -> resize
|
||||
if ev.child != @start.child
|
||||
set_focus(ev.child) rescue nil # FIXME
|
||||
end
|
||||
return if !@start&.child || !@attr
|
||||
|
||||
xdiff = ev.root_x - @start.root_x;
|
||||
ydiff = ev.root_y - @start.root_y;
|
||||
|
||||
w = window(@start.child)
|
||||
|
||||
# FIXME: Any other types we don't want to allow moving or resizing
|
||||
begin
|
||||
return if w.special?
|
||||
rescue # FIXME: Why is this here?
|
||||
end
|
||||
|
||||
#p w
|
||||
if @start.detail == 1 # Move
|
||||
w.configure(x: attr.x + xdiff, y: attr.y + ydiff)
|
||||
elsif @start.detail == 3 # Resize
|
||||
lr = (ev.event_x-@attr.x < @attr.width / 2)
|
||||
tb = (ev.event_y-@attr.y < @attr.height/ 2)
|
||||
if w.floating?
|
||||
# If left/above the centre point, we grow/shrink the window to the left/top
|
||||
# otherwise to the right/bottom. Doing it to the left/top requires
|
||||
# moving it at the same time.
|
||||
@attr.x = @attr.x + (lr ? xdiff : 0)
|
||||
@attr.y = @attr.y + (tb ? ydiff : 0)
|
||||
@attr.width = @attr.width + (lr ? -xdiff : xdiff)
|
||||
@attr.height = @attr.height+ (tb ? -ydiff : ydiff)
|
||||
@start.root_x = ev.root_x
|
||||
@start.root_y = ev.root_y
|
||||
w.configure(x: @attr.x, y: @attr.y, width: @attr.width, height: @attr.height)
|
||||
else
|
||||
layout = current_desktop&.layout
|
||||
if layout
|
||||
prev = layout.find(w)
|
||||
node = prev.parent
|
||||
p :RESIZE
|
||||
p [prev, node, layout.root]
|
||||
while node #&& node != layout.root
|
||||
p [lr, tb, node.dir, node]
|
||||
if !lr.nil? && node.dir == :lr
|
||||
dx = 0
|
||||
if node.geom
|
||||
dx = (((node.geom.width * node.ratio) + xdiff)/node.geom.width) - node.ratio
|
||||
else
|
||||
dx = 0.00
|
||||
end
|
||||
if node.nodes[0] == prev && !lr
|
||||
# FIXME: This doesn't seem to matter
|
||||
node.ratio += dx
|
||||
@start.root_x = ev.root_x
|
||||
lr = nil
|
||||
elsif node.nodes[1] == prev
|
||||
node.ratio += dx
|
||||
@start.root_x = ev.root_x
|
||||
lr = nil
|
||||
end
|
||||
end
|
||||
if !tb.nil? && node.dir == :tb
|
||||
dy = 0
|
||||
if node.geom
|
||||
dy = (((node.geom.height * node.ratio) + ydiff)/node.geom.height) - node.ratio
|
||||
else
|
||||
dy = 0.00
|
||||
end
|
||||
if node.nodes[0] == prev && !tb
|
||||
# FIXME: This doesn't seem to matter
|
||||
node.ratio += dy
|
||||
@start.root_y = ev.root_y
|
||||
node.ratio.clamp(0.1,0.9)
|
||||
tb = nil
|
||||
elsif node.nodes[1] == prev
|
||||
node.ratio += dy
|
||||
@start.root_y = ev.root_y
|
||||
tb = nil
|
||||
end
|
||||
end
|
||||
node.ratio = node.ratio.clamp(0.1,0.9)
|
||||
prev = node
|
||||
node = node.parent
|
||||
end
|
||||
p :RESIZE_DONE
|
||||
layout.call
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def on_button_release(ev)
|
||||
@start.child = nil if @start
|
||||
end
|
||||
|
||||
def on_focus_in(ev) = (set_focus(ev.event) if !focus)
|
||||
def on_enter_notify(ev) = set_focus(ev.event)
|
||||
def on_unmap_notify(ev) = window(ev.window)&.desktop&.update_layout
|
||||
|
@ -366,7 +473,7 @@ class WindowManager
|
|||
def on_rwm_focus(_, dir)
|
||||
dir = dpy.get_atom_name(dir).downcase.to_sym
|
||||
return if !@focus || @focus.special?
|
||||
w = find_closest(@focus, dir, @focus.desktop.children)
|
||||
w = find_closest(@focus, dir, @focus.desktop.mapped_regular_children)
|
||||
set_focus(w.wid) if w
|
||||
end
|
||||
|
||||
|
@ -418,7 +525,7 @@ class WindowManager
|
|||
return
|
||||
end
|
||||
|
||||
w = find_closest(@focus, dir, @focus.desktop.children.find_all{_1.mapped})
|
||||
w = find_closest(@focus, dir, @focus.desktop.mapped_regular_children)
|
||||
|
||||
l1 = @focus.desktop&.layout&.find(@focus)
|
||||
l2 = w&.desktop&.layout&.find(w)
|
||||
|
@ -438,5 +545,4 @@ class WindowManager
|
|||
set_focus(@focus.wid)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
|
Loading…
Reference in a new issue