SHARE
TWEET

Subtle New API 01

Pauan May 29th, 2012 23 Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. ## Clients get extended with this module, because apparently I can't change
  2. ## the Subtlext::Client class
  3. module ClientWrapper
  4.   def self.make_setter *args
  5.     args.each do |arg|
  6.       class_eval("def #{arg} x
  7.                    self.#{arg} = x
  8.                  end")
  9.     end
  10.   end
  11.  
  12.   def self.make_toggle_setter *args
  13.     args.each do |arg|
  14.       class_eval("def #{arg} x
  15.                    toggle_#{arg} if x != is_#{arg}?
  16.                  end")
  17.     end
  18.   end
  19.  
  20.   ## Subtle provides things like `c.gravity = :foo` but that won't work if
  21.   ## the client is instance_eval'd into a block, so this provides versions
  22.   ## that do the assignment so you can say things like `gravity :foo`
  23.   make_setter :flags, :geometry, :gravity, :tags
  24.  
  25.   ## Subtle only provides things like `is_float?` and `toggle_float`, so this
  26.   ## creates some nice wrappers so you can say things like `c.float true`
  27.   ## rather than `c.toggle_float unless c.is_float?`
  28.   make_toggle_setter :borderless, :fixed, :float,  :full,
  29.                      :resize,     :stick, :urgent, :zaphod
  30.  
  31.   ## Needed so assigning the gravity sets it on all the client's views rather
  32.   ## than only the current one
  33.   def gravity= x
  34.     if Symbol === x
  35.       views.each { |v| super v => x }
  36.     else
  37.       super
  38.     end
  39.   end
  40.  
  41.   ## Returns true if the client would be matched by the argument `x`
  42.   ##
  43.   ## It uses the same matching rules as `match foo` inside a tag, with the
  44.   ## sole exception that strings are exact matches rather than treated as
  45.   ## regexps
  46.   def matches? x
  47.     case x
  48.       when String
  49.         x == instance or x == klass
  50.       when Regexp
  51.         x.match(instance) or x.match(klass)
  52.       when Hash
  53.         x.all? do |k, v|
  54.           k = (k == :class) ? klass
  55.                             : send(k)
  56.           (String === v) ? v == k
  57.                          : v.match(k)
  58.         end
  59.       else
  60.         raise TypeError
  61.     end
  62.   end
  63. end
  64.  
  65.  
  66. # TODO: better way to access the old stuff...
  67. $self = self
  68. alias old_view view
  69. alias old_tag  tag
  70.  
  71. class Tag
  72.   def initialize name, &f
  73.     @clients  = []
  74.     @on_match = []
  75.  
  76.     ## Special DSL sugar that lets you pass in a block with no arguments
  77.     if block_given?
  78.       if f.arity == 0
  79.         instance_eval &f
  80.       else
  81.         f.call self
  82.       end
  83.     end
  84.  
  85.     ## Needed because `$self.old_tag` evaluates the block's body in a
  86.     ## different environment
  87.     _clients  = @clients
  88.     _gravity  = @gravity
  89.     _on_match = @on_match
  90.  
  91.     $self.old_tag name do
  92.       _clients.each { |x| match x }
  93.       gravity _gravity if _gravity
  94.       on_match do |c|
  95.         ## Makes the methods in `ClientWrapper` available to the client
  96.         # TODO: potentially slow, maybe I can cache it somehow?
  97.         c.extend ClientWrapper
  98.         _on_match.each { |x| x.call c }
  99.       end
  100.     end
  101.   end
  102.  
  103.   def on x, &f
  104.     if x == :match
  105.                    ## DSL sugar
  106.       @on_match << (f.arity == 0 ? lambda { |c| c.instance_eval &f } : f)
  107.     else
  108.       raise NoMethodError
  109.     end
  110.   end
  111.  
  112.   ## Sets the gravity of either the entire view, or an individual client
  113.   ## within the view
  114.   def gravity g, x=nil
  115.     if x
  116.       @clients  << x
  117.       @on_match << lambda { |c| c.gravity = g if c.matches? x }
  118.     else
  119.       @gravity = g
  120.     end
  121.   end
  122.  
  123.   ## Matches a client and optionally calls a block, passing it the client
  124.   def client x, &f
  125.     @clients << x
  126.     if block_given?
  127.                    ## DSL sugar
  128.       @on_match << (f.arity == 0 ? lambda { |c| c.instance_eval &f if c.matches? x }
  129.                                  : lambda { |c| yield c if c.matches? x })
  130.     end
  131.   end
  132. end
  133.  
  134. class View < Tag
  135.   def initialize name, &f
  136.     @icon = []
  137.     @tag  = [name]
  138.  
  139.     ## Calls the Tag class's initialize
  140.     super
  141.  
  142.                  ## This is needed because views can't have more than one
  143.                  ## `match` method, so we have to union them together into
  144.                  ## a single `match`
  145.     _tag       = Regexp.union @tag
  146.     _icon      = @icon
  147.     _dynamic   = @dynamic
  148.     _icon_only = @icon_only
  149.  
  150.     $self.old_view name do
  151.       match _tag
  152.       _icon.each { |x| icon x }
  153.       dynamic   true if _dynamic
  154.       icon_only true if _icon_only
  155.     end
  156.   end
  157.  
  158.   def icon x
  159.     @icon << x
  160.   end
  161.  
  162.   def tag x
  163.     @tag << x
  164.   end
  165.  
  166.   def dynamic x
  167.     @dynamic = x
  168.   end
  169.  
  170.   def icon_only x
  171.     @icon_only = x
  172.   end
  173. end
  174.  
  175. ## These two are just to make it a bit shorter and cleaner looking
  176. def view name, &f
  177.   View.new(name, &f)
  178. end
  179.  
  180. def tag name, &f
  181.   Tag.new(name, &f)
  182. end
RAW Paste Data
We use cookies for various purposes including analytics. By continuing to use Pastebin, you agree to our use of cookies as described in the Cookies Policy. OK, I Understand
 
Top