summaryrefslogtreecommitdiffstats
path: root/bin/subtle-contrib/ruby/selector.rb
diff options
context:
space:
mode:
Diffstat (limited to 'bin/subtle-contrib/ruby/selector.rb')
-rw-r--r--bin/subtle-contrib/ruby/selector.rb344
1 files changed, 344 insertions, 0 deletions
diff --git a/bin/subtle-contrib/ruby/selector.rb b/bin/subtle-contrib/ruby/selector.rb
new file mode 100644
index 0000000..13857df
--- /dev/null
+++ b/bin/subtle-contrib/ruby/selector.rb
@@ -0,0 +1,344 @@
+#!/usr/bin/ruby
+#
+# @file Selector
+#
+# @copyright (c) 2011-2012, Christoph Kappel <unexist@dorfelite.net>
+# @version $Id$
+#
+# Client selector that works like the subscription selector in google reader.
+#
+# Colors:
+#
+# Focus - Currently selected client
+# Occupied - Visible clients on current views
+# Unoccupied - Currently no visible clients
+#
+# Keys:
+#
+# Left, Up - Move to left
+# Right, Down - Move to right
+# Tab - Cycle through windows/matches
+# Escape - Leave input mode/exit selector
+# Return - Focus currently selected and hide/exit selector
+# Any capital/digit - Select client prefixed with capital letter/digit
+# Any text - Select client with matching instance name
+#
+# http://subforge.org/projects/subtle-contrib/wiki/Selector
+#
+
+require 'singleton'
+
+begin
+ require 'subtle/subtlext'
+rescue LoadError
+ puts ">>> ERROR: Couldn't find subtlext"
+ exit
+end
+
+# Check for subtlext version
+major, minor, teeny = Subtlext::VERSION.split('.').map(&:to_i)
+if major == 0 and minor == 10 and 3216 > teeny
+ puts ">>> ERROR: selector needs at least subtle `0.10.3216' (found: %s)" % [
+ Subtlext::VERSION
+ ]
+ exit
+end
+
+# Launcher class
+module Subtle # {{{
+ module Contrib # {{{
+ class Selector # {{{
+ include Singleton
+
+ # Prefix letters
+ LETTERS = ((49..57).to_a|(65..89).to_a).map(&:chr)
+
+ # Default values
+ @@font = '-*-*-medium-*-*-*-14-*-*-*-*-*-*-*'
+
+ # Singleton methods
+
+ ## fonts {{{
+ # Set font strings
+ # @param [String] fonts Fonts array
+ ##
+
+ def self.font=(font)
+ @@font = font
+ end # }}}
+
+ ## run {{{
+ # Run expose
+ ##
+
+ def self.run
+ self.instance.run
+ end # }}}
+
+ # Instance methods
+
+ ## initialize {{{
+ # Create expose instance
+ ##
+
+ def initialize
+ # Values
+ @colors = Subtlext::Subtle.colors
+ @expanded = false
+ @buffer = ''
+ @x = 0
+ @y = 0
+ @width = 0
+ @height = 0
+
+ # Create main window
+ @win = Subtlext::Window.new(:x => 0, :y => 0, :width => 1, :height => 1) do |w|
+ w.name = 'Selector'
+ w.font = @@font
+ w.foreground = @colors[:title_fg]
+ w.background = @colors[:title_bg]
+ w.border_size = 0
+ end
+
+ # Font metrics
+ @font_height = @win.font_height + 6
+ @font_y = @win.font_y
+
+ # Handler
+ @win.on :key_down, method(:key_down)
+ @win.on :draw, method(:redraw)
+ end # }}}
+
+ ## run {{{
+ # Show and run launcher
+ ##
+
+ def run
+ update
+ show
+ hide
+ end # }}}
+
+ private
+
+ ## key_down {{{
+ # Key down handler
+ # @param [String] key Pressed key
+ ##
+
+ def key_down(key, mods)
+ ret = true
+
+ case key
+ when :left, :up # {{{
+ idx = @clients.index(@current) || 0
+ idx -= 1 if 0 < idx
+ @current = @clients[idx] # }}}
+ when :right, :down # {{{
+ idx = @clients.index(@current) || 0
+ idx += 1 if idx < (@clients.size - 1)
+ @current = @clients[idx] # }}}
+ when :return # {{{
+ @current.focus
+
+ ret = false # }}}
+ when :escape # {{{
+ if @expanded
+ @buffer = ''
+ @expanded = false
+ else
+ ret = false
+ end
+
+ arrange # }}}
+ when :backspace # {{{
+ if @expanded
+ @expanded = (0 < @buffer.chop!.size)
+
+ arrange
+ end # }}}
+ when :tab # {{{
+ if @buffer.empty?
+ clients = @clients
+ else
+ # Select matching clients
+ clients = @clients.select do |c|
+ c.instance.downcase.start_with?(@buffer)
+ end
+ end
+
+ unless (idx = clients.index(@current)).nil?
+ # Cycle between clients
+ if idx < (clients.size - 1)
+ idx += 1
+ else
+ idx = 0
+ end
+
+ @current = clients[idx]
+ end # }}}
+ else # {{{
+ str = key.to_s
+
+ if !(idx = LETTERS.index(str)).nil? and idx < @clients.size
+ @clients[idx].focus
+
+ ret = false
+ elsif !str.empty?
+ @buffer << str.downcase
+
+ @clients.each do |c|
+ if c.instance.downcase.start_with?(@buffer)
+ @current = c
+
+ break
+ end
+ end
+
+ arrange
+ end # }}}
+ end
+
+ redraw(@win) if ret
+
+ ret
+ end # }}}
+
+ ## update # {{{
+ # Update clients and windows
+ ##
+
+ def update
+ @buffer = ''
+ @clients = Subtlext::Client.all
+ @visible = Subtlext::Client.visible
+ @current = Subtlext::Client.current rescue nil
+
+ arrange
+ end # }}}
+
+ ## arrange {{{
+ # Arrange window
+ ##
+
+ def arrange
+ geo = Subtlext::Screen.current.geometry
+ @width = geo.width * 50 / 100 #< Max width
+ @height = @font_height
+ wx = 0
+ wy = 0
+ len = 0
+ wwidth = 0
+
+ # Toggle expand
+ if @buffer.empty?
+ @height = @font_height
+ @expanded = false
+ else
+ @height += @font_height
+ @expanded = true
+ end
+
+ # Calculate window width
+ @clients.each_with_index do |c, i|
+ str = '%s:%s' % [ LETTERS[i], c.instance ]
+ len = @win.font_width(str) + 6
+
+ # Wrap lines
+ if wx + len > @width
+ wwidth = wx if wx > wwidth
+ wx = 0
+ wy += @font_height
+ end
+
+ wx += len
+ end
+
+ # Update window geometry
+ @width = 0 == wwidth ? wx : wwidth
+ @height += wy
+ @x = geo.x + ((geo.width - @width) / 2)
+ @y = geo.y + ((geo.height - @height) / 2)
+
+ @win.geometry = [ @x , @y, @width, @height ]
+ end # }}}
+
+ ## redraw {{{
+ # Redraw window content
+ # @param [Window] w Window instance
+ ##
+
+ def redraw(w)
+ wx = 0
+ wy = 0
+ len = 0
+ wwidth = 0
+
+ @win.clear
+
+ # Render window
+ @clients.each_with_index do |c, i|
+ str = '%s:%s' % [ LETTERS[i], c.instance ]
+ len = @win.font_width(str) + 6
+
+ # Wrap lines
+ if wx + len > @width
+ wwidth = wx if wx > wwidth
+ wx = 0
+ wy += @font_height
+ end
+
+ # Select color
+ if @clients[i] == @current
+ fg = @colors[:focus_fg]
+ bg = @colors[:focus_bg]
+ elsif @visible.include?(@clients[i])
+ fg = @colors[:occupied_fg]
+ bg = @colors[:occupied_bg]
+ else
+ fg = @colors[:views_fg]
+ bg = @colors[:views_bg]
+ end
+
+ @win.draw_rect(wx, wy, len, @font_height, bg, true)
+ @win.draw_text(wx + 3, wy + @font_y + 3, str, fg)
+
+ wx += len
+ end
+
+ # Draw input buffer
+ unless @buffer.empty?
+ @win.draw_text(6, @height - @font_height + @font_y + 3,
+ 'Input: %s' % [ @buffer ])
+ end
+ end # }}}
+
+ ## show {{{
+ # Show launcher
+ ##
+
+ def show
+ @win.show
+ end # }}}
+
+ ## hide # {{{
+ # Hide launcher
+ ##
+
+ def hide
+ @win.hide
+ end # }}}
+ end # }}}
+ end # }}}
+end # }}}
+
+# Implicitly run
+if __FILE__ == $0
+ # Set font
+ #Subtle::Contrib::Selector.font =
+ # 'xft:DejaVu Sans Mono:pixelsize=80:antialias=true'
+
+ Subtle::Contrib::Selector.run
+end
+
+# vim:ts=2:bs=2:sw=2:et:fdm=marker