Advertisement
Pauan

Subtle New API 01

May 29th, 2012
207
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Ruby 4.56 KB | None | 0 0
  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
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement