diff --git a/lib/StandardGUI/menubar.rb b/lib/StandardGUI/menubar.rb index 0ec36ac..a528f02 100644 --- a/lib/StandardGUI/menubar.rb +++ b/lib/StandardGUI/menubar.rb @@ -1,79 +1,95 @@ # coding: utf-8 require_relative './popupmenu' require_relative './common' +require_relative './image' module WS # ウィンドウにくっつけるメニューバー class WSMenuBar < WSContainer + + class WSMenuBarItem < WSImage + attr_reader :item + attr_accessor :popup + + def initialize(str, item, font=nil) + @str = str + @item = item + @font = font if font + w = @font.getWidth(@str) + + super(nil,nil,w+10,@font.size+4) + + @image = {true => Image.new(w + 10, @font.size + 4).draw(4, 0, Image.new(w + 2, @font.size + 4, [150,150,150])).draw_font_ex(5, 2, @str, @font, :color=>COLOR[:font],:aa=>false), + false => Image.new(w + 10, @font.size + 4).draw_font_ex(5, 2, @str, @font, :color=>COLOR[:font],:aa=>false)} + self.image = @image[false] + + @mouse_on = false + @popup = false + end + + def on_mouse_push(tx, ty) + @mouse_on = false + + self.parent.popup(self) + + super + end + + def on_mouse_move(tx, ty) + @mouse_on = true + + self.parent.popup(self) if @popup && WS.captured?(@popup) + + super + end + + def on_mouse_out + @mouse_on = false + + super + end + + def render + self.image = @image[@mouse_on] + + super + end + end + def initialize(menuitems) - super(0, 0, 10, 16) # 数字テキトー。オートレイアウトで設定する。 - @menuitems = menuitems - self.image.bgcolor = COLOR[:base] + super(nil, nil, nil, 16) #オートレイアウトで設定する。 @font = Font.new(12) - @selected = nil + @menuitems = ary = menuitems.map{|ary| + mbi = WSMenuBarItem.new(ary[0], ary[1], @font) + self.add_control(mbi) + mbi + } + self.image.bgcolor = COLOR[:base] @popup = nil - end - - # メニュークリックでポップアップメニューを表示する - def on_mouse_push(tx, ty) - x = 5 - @selected = nil - @menuitems.each_with_index do |ary, i| - tmp = @font.get_width(ary[0]) - if x - 5 <= tx and tx < x + tmp + 5 - WS.desktop.remove_control(@popup) if @popup - tmpx, tmpy = self.get_global_vertex - @popup = WSPopupMenu.new(x + tmpx, 16 + tmpy, ary[1]) - WS.desktop.add_control(@popup) - @popup.object = self - super - WS.capture(@popup) + + layout(:left_ex) do + ary.each do |item| + add item end - x += tmp + 10 end - super end - - # マウスの移動でどのメニューが選択されているかを判定し、選択する - def on_mouse_move(tx, ty) - x = 5 - @selected = nil - @menuitems.each_with_index do |ary, i| - tmp = @font.get_width(ary[0]) - if x - 5 <= tx and tx < x + tmp + 5 - @selected = i - if @popup and WS.captured?(@popup) - WS.desktop.remove_control(@popup) if @popup - tmpx, tmpy = self.get_global_vertex - @popup = WSPopupMenu.new(x + tmpx, 16 + tmpy, ary[1]) - WS.desktop.add_control(@popup) - @popup.object = self - super - WS.capture(@popup) - end - end - x += tmp + 10 + + def popup(item) + WS.desktop.remove_control(@popup) if @popup + tmpx, tmpy = item.get_global_vertex + @popup = WSPopupMenu.new(tmpx, @font.size + 4 + tmpy, item.item) + WS.desktop.add_control(@popup) + @popup.object = self + WS.capture(@popup) + + @menuitems.each do |ctl| + ctl.popup = @popup end - super - end - - def on_mouse_out - @selected = nil - super end - - def render - x = 5 - @menuitems.each_with_index do |ary, i| - tmp = @font.get_width(ary[0]) - if @selected == i - image = Image.new(tmp + 2, 16, [150, 150, 150]) - self.image.draw(x - 1, 0, image) - end - self.image.draw_font(x, 2, ary[0], @font, :color=>COLOR[:font]) - x += tmp + 10 - end + + def resize(width, height) super + self.height = @min_height end end end diff --git a/lib/core.rb b/lib/core.rb index a18c5d6..e3c2fa5 100644 --- a/lib/core.rb +++ b/lib/core.rb @@ -357,6 +357,12 @@ def remove_control(obj, name=nil) # Sprite#update時に配下のコントロールにもupdateを投げる def update Sprite.update(@childlen) + + if @layout + @min_width = @layout.min_width + @min_height = @layout.min_height + end + super end @@ -551,6 +557,10 @@ class WSLayout attr_accessor :type, :x, :y, :width, :height, :resizable_width, :resizable_height, :obj, :parent attr_accessor :margin_left, :margin_right, :margin_top, :margin_bottom attr_accessor :min_width, :min_height + + #auto_layout時の動作が定義されているtypeの配列 + #各々の動作は同名のインスタンスメソッドを参照 + @@types = [:hbox, :vbox, :hbox_ex, :vbox_ex, :left, :right, :hcenter, :left_ex, :right_ex, :hcenter_ex, :top, :bottom, :vcenter, :top_ex, :bottom_ex, :vcenter_ex] def initialize(type, obj, parent, &b) @type, @obj = type, obj @@ -559,27 +569,28 @@ def initialize(type, obj, parent, &b) @x = @y = 0 @margin_left = @margin_right = @margin_top = @margin_bottom = 0 @resizable_width = @resizable_height = true - @data = [] + @default_data = [] self.instance_eval &b if b end def layout(type=nil, &b) - @data << WSLayout.new(type, @obj, self, &b) + @default_data << WSLayout.new(type, @obj, self, &b) self end def add(o, rsw=nil, rsh=nil) - @data << o + @default_data << o o.resizable_width = rsw if rsw != nil o.resizable_height = rsh if rsh != nil - case @type - when :hbox - @min_width += (o.resizable_width ? o.min_width : o.width) - @min_height = [@min_height, (o.resizable_height ? o.min_height : o.height)].max - when :vbox - @min_height += (o.resizable_height ? o.min_height : o.height) - @min_width = [@min_width, (o.resizable_width ? o.min_width : o.width)].max + w = (o.resizable_width ? o.min_width : o.width) + h = (o.resizable_height ? o.min_height : o.height) + if [:hbox, :hbox_ex, :left, :right, :hcenter, :left_ex, :right_ex, :hcenter_ex].include?(@type) + @min_width += w + @min_height = [@min_height, h].max + else + @min_width = [@min_width, w].max + @min_height += h end end @@ -643,106 +654,490 @@ def adjust_y end end - def auto_layout - case @type - when :hbox # 水平に並べる - # サイズ未定のものをカウント - undef_size_count = @data.count{|o| o.resizable_width} - - # サイズ確定オブジェクトのサイズ合計 - total = @data.inject(0){|t, o| t += (o.resizable_width ? 0 : o.width)} - - # サイズが確定されていないオブジェクトの配列作成 - undef_size_ctl = @data.select{|o| o.resizable_width}.sort_by{|o|o.min_width}.reverse - - width = 0 - rest = (self.width - @margin_left - @margin_right - total).to_f - count = undef_size_ctl.size - - # サイズの大きいほうから残りのすべてをmin_widthにできるかどうかを確認する - undef_size_ctl.each do |o| - if rest < (count * o.min_width) # 入りきらない - rest -= o.min_width # このぶんは確定とする - count -= 1 - else - width = rest / count - break - end - end + def hbox # 水平に並べる + # サイズ未定のものをカウント + undef_size_count = @data.count{|o| o.resizable_width} - # 座標開始位置 - point = self.x + @margin_left + # サイズ確定オブジェクトのサイズ合計 + total = @data.inject(0){|t, o| t += (o.resizable_width ? 0 : o.width)} - case undef_size_count - when 0 # 均等 - # 座標調整 - adjust_x do |o| - tmp = (self.width - @margin_left - @margin_right - total) / (@data.size + 1) # オブジェクトの間隔を足す - point += (tmp > 0 ? tmp : 0) - @new_x = point - point += @new_width - end + # サイズが確定されていないオブジェクトの配列作成 + undef_size_ctl = @data.select{|o| o.resizable_width}.sort_by{|o|o.min_width}.reverse + + width = 0 + rest = (self.width - @margin_left - @margin_right - total).to_f + count = undef_size_ctl.size - else # 最大化するものを含む - # 座標調整 - adjust_x do |o| - @new_x = point - if o.resizable_width # 最大化するオブジェクトを最大化 - @new_width = (width < @new_min_width ? @new_min_width : width) - end - point += @new_width + # サイズの大きいほうから残りのすべてをmin_widthにできるかどうかを確認する + undef_size_ctl.each do |o| + if rest < (count * o.min_width) # 入りきらない + rest -= o.min_width # このぶんは確定とする + count -= 1 + else + width = rest / count + break + end + end + + # 座標開始位置 + point = self.x + @margin_left + + case undef_size_count + when 0 # 均等 + # 座標調整 + adjust_x do |o| + tmp = (self.width - @margin_left - @margin_right - total) / (@data.size + 1) # オブジェクトの間隔を足す + point += (tmp > 0 ? tmp : 0) + @new_x = point + point += @new_width + end + + else # 最大化するものを含む + # 座標調整 + adjust_x do |o| + @new_x = point + if o.resizable_width # 最大化するオブジェクトを最大化 + @new_width = (width < @new_min_width ? @new_min_width : width) end + point += @new_width end + end + end + + def vbox # 垂直に並べる + # サイズ未定のものをカウント + undef_size_count = @data.count{|o| o.resizable_height} - when :vbox # 垂直に並べる - # サイズ未定のものをカウント - undef_size_count = @data.count{|o| o.resizable_height} + # サイズ確定オブジェクトのサイズ合計 + total = @data.inject(0){|t, o| t += (o.resizable_height ? 0 : o.height)} - # サイズ確定オブジェクトのサイズ合計 - total = @data.inject(0){|t, o| t += (o.resizable_height ? 0 : o.height)} + # サイズが確定されていないオブジェクトの配列作成 + undef_size_ctl = @data.select{|o| o.resizable_height}.sort_by{|o|o.min_height}.reverse - # サイズが確定されていないオブジェクトの配列作成 - undef_size_ctl = @data.select{|o| o.resizable_height}.sort_by{|o|o.min_height}.reverse + height = 0 + rest = (self.height - @margin_top - @margin_bottom - total).to_f + count = undef_size_ctl.size - height = 0 - rest = (self.height - @margin_top - @margin_bottom - total).to_f - count = undef_size_ctl.size + # サイズの大きいほうから残りのすべてをmin_heightにできるかどうかを確認する + undef_size_ctl.each do |o| + if rest < (count * o.min_height) # 入りきらない + rest -= o.min_height # このぶんは確定とする + count -= 1 + else + height = rest / count + break + end + end + + # 座標開始位置 + point = self.y + @margin_top + + case undef_size_count + when 0 # 均等 + # 座標調整 + adjust_y do |o| + tmp = (self.height - @margin_top - @margin_bottom - total) / (@data.size + 1) # オブジェクトの間隔を足す + point += (tmp > 0 ? tmp : 0) + @new_y = point + point += @new_height + end - # サイズの大きいほうから残りのすべてをmin_heightにできるかどうかを確認する - undef_size_ctl.each do |o| - if rest < (count * o.min_height) # 入りきらない - rest -= o.min_height # このぶんは確定とする - count -= 1 + else # 最大化するものを含む + # 座標調整 + adjust_y do |o| + @new_y = point + if o.resizable_height # 最大化するオブジェクトを最大化 + @new_height = (height < @new_min_height ? @new_min_height : height) + end + point += @new_height + end + end + end + + def hbox_ex #水平に並べ、はみ出したら下の段に + rest = default_rest = self.width - @margin_left - @margin_right + lines = [[]] + + @data.each do |ctl| + w = (ctl.resizable_width ? ctl.min_width : ctl.width) + + rest -= w + if rest <= 0 + if lines[-1].empty? + lines[-1] << ctl + lines << [] else - height = rest / count - break + lines << [ctl] end + + rest = default_rest - w + else + lines[-1] << ctl end - - # 座標開始位置 - point = self.y + @margin_top - - case undef_size_count - when 0 # 均等 - # 座標調整 - adjust_y do |o| - tmp = (self.height - @margin_top - @margin_bottom - total) / (@data.size + 1) # オブジェクトの間隔を足す - point += (tmp > 0 ? tmp : 0) - @new_y = point - point += @new_height + end + + lines.delete([]) + + lines.map! do |ary| + WSLayout.new(:hbox, @obj, self) do + ary.each do |ctl| + add ctl end - - else # 最大化するものを含む - # 座標調整 - adjust_y do |o| - @new_y = point - if o.resizable_height # 最大化するオブジェクトを最大化 - @new_height = (height < @new_min_height ? @new_min_height : height) - end - point += @new_height + end + end + @min_height = lines.map(&:min_height).inject(&:+) + @min_width = lines.map(&:min_width).max + + @data = lines + + vbox + end + + def vbox_ex #上下に並べ、はみ出したら左の段へ + rest = default_rest = self.height - @margin_top - @margin_bottom + lines = [[]] + + @data.each do |ctl| + w = (ctl.resizable_height ? ctl.min_height : ctl.height) + + rest -= w + if rest <= 0 + if lines[0].empty? + lines[0] << ctl + lines.unshift [] + else + lines.unshift [ctl] end + + rest = default_rest - w + else + lines[0] << ctl end end + + lines.delete([]) + + lines.map! do |ary| + WSLayout.new(:vbox, @obj, self) do + ary.each do |ctl| + add ctl + end + end + end + @min_width = lines.map(&:min_width).inject(&:+) + @min_height = lines.map(&:min_height).max + + @data = lines + + hbox + end + + def left #左に詰める + point = self.x + @margin_left + + adjust_x do |o| + @new_x = point + @new_width = @new_min_width if o.resizable_width + point += @new_width + end + end + + def right #右に詰める + total = @data.map{|ctl| ctl.resizable_width ? ctl.min_width : ctl.width}.inject(&:+) + + point = self.x + self.width - @margin_right - total + + adjust_x do |o| + @new_x = point + @new_width = (o.resizable_width ? o.min_width : o.width) + point += @new_width + end + end + + def hcenter #左右に並べ、中央に固める + total = @data.map{|ctl| ctl.resizable_width ? ctl.min_width : ctl.width}.inject(&:+) + + point = self.x + (self.width + @margin_left - @margin_right - total) / 2 + + adjust_x do |o| + @new_x = point + @new_width = (o.resizable_width ? o.min_width : o.width) + point += @new_width + end + end + + def top #上に詰める + point = self.y + @margin_top + + adjust_y do |o| + @new_y = point + @new_height = @new_min_height if o.resizable_height + point += @new_height + end + end + + def bottom #下に詰める + total = @data.map{|ctl| ctl.resizable_height ? ctl.min_height : ctl.height}.inject(&:+) + + point = self.y + self.height - @margin_bottom - total + + adjust_y do |o| + @new_y = point + @new_height = (o.resizable_height ? o.min_height : o.height) + point += @new_height + end + end + + def vcenter #上下に並べ、中央に固める + total = @data.map{|ctl| ctl.resizable_height ? ctl.min_height : ctl.height}.inject(&:+) + + point = self.y + (self.height + @margin_top - @margin_bottom - total) / 2 + + adjust_y do |o| + @new_y = point + @new_height = (o.resizable_height ? o.min_height : o.height) + point += @new_height + end + end + + def left_ex #左に詰め、はみ出したら下の段へ + rest = default_rest = self.width - @margin_left - @margin_right + lines = [[]] + + @data.each do |ctl| + w = (ctl.resizable_width ? ctl.min_width : ctl.width) + + rest -= w + if rest <= 0 + if lines[-1].empty? + lines[-1] << ctl + lines << [] + else + lines << [ctl] + end + + rest = default_rest - w + else + lines[-1] << ctl + end + end + + lines.delete([]) + + lines.map! do |ary| + WSLayout.new(:left, @obj, self) do + ary.each do |ctl| + add ctl + end + end + end + @min_height = lines.map(&:min_height).inject(&:+) + @min_width = lines.map(&:min_width).max + + @data = lines + + vbox + end + + def right_ex #右に詰め、はみ出したら下の段へ + rest = default_rest = self.width - @margin_left - @margin_right + lines = [[]] + + @data.each do |ctl| + w = (ctl.resizable_width ? ctl.min_width : ctl.width) + + rest -= w + if rest <= 0 + if lines[-1].empty? + lines[-1] << ctl + lines << [] + else + lines << [ctl] + end + + rest = default_rest - w + else + lines[-1] << ctl + end + end + + lines.delete([]) + + lines.map! do |ary| + WSLayout.new(:right, @obj, self) do + ary.each do |ctl| + add ctl + end + end + end + @min_height = lines.map(&:min_height).inject(&:+) + @min_width = lines.map(&:min_width).max + + @data = lines + + vbox + end + + def hcenter_ex #左右に並べ、中央に固める。はみ出したら下の段へ + rest = default_rest = self.width - @margin_left - @margin_right + lines = [[]] + + @data.each do |ctl| + w = (ctl.resizable_width ? ctl.min_width : ctl.width) + + rest -= w + if rest <= 0 + if lines[-1].empty? + lines[-1] << ctl + lines << [] + else + lines << [ctl] + end + + rest = default_rest - w + else + lines[-1] << ctl + end + end + + lines.delete([]) + + lines.map! do |ary| + WSLayout.new(:center, @obj, self) do + ary.each do |ctl| + add ctl + end + end + end + @min_height = lines.map(&:min_height).inject(&:+) + @min_width = lines.map(&:min_width).max + + @data = lines + + vbox + end + + def top_ex #上に詰め、はみ出したら左の段へ + rest = default_rest = self.height - @margin_top - @margin_bottom + lines = [[]] + + @data.each do |ctl| + w = (ctl.resizable_height ? ctl.min_height : ctl.height) + + rest -= w + if rest <= 0 + if lines[0].empty? + lines[0] << ctl + lines.unshift [] + else + lines.unshift [ctl] + end + + rest = default_rest - w + else + lines[0] << ctl + end + end + + lines.delete([]) + + lines.map! do |ary| + WSLayout.new(:top, @obj, self) do + ary.each do |ctl| + add ctl + end + end + end + @min_width = lines.map(&:min_width).inject(&:+) + @min_height = lines.map(&:min_height).max + + @data = lines + + hbox + end + + def bottom_ex #下に詰め、はみ出したら左の段へ + rest = default_rest = self.height - @margin_top - @margin_bottom + lines = [[]] + + @data.each do |ctl| + w = (ctl.resizable_height ? ctl.min_height : ctl.height) + + rest -= w + if rest <= 0 + if lines[0].empty? + lines[0] << ctl + lines.unshift [] + else + lines.unshift [ctl] + end + + rest = default_rest - w + else + lines[0] << ctl + end + end + + lines.delete([]) + + lines.map! do |ary| + WSLayout.new(:bottom, @obj, self) do + ary.each do |ctl| + add ctl + end + end + end + @min_width = lines.map(&:min_width).inject(&:+) + @min_height = lines.map(&:min_height).max + + @data = lines + + hbox + end + + def vcenter_ex #上下に並べ、中央に固める。はみ出したら左の段へ + rest = default_rest = self.height - @margin_top - @margin_bottom + lines = [[]] + + @data.each do |ctl| + w = (ctl.resizable_height ? ctl.min_height : ctl.height) + + rest -= w + if rest <= 0 + if lines[0].empty? + lines[0] << ctl + lines.unshift [] + else + lines.unshift [ctl] + end + + rest = default_rest - w + else + lines[0] << ctl + end + end + + lines.delete([]) + + lines.map! do |ary| + WSLayout.new(:vcenter, @obj, self) do + ary.each do |ctl| + add ctl + end + end + end + @min_width = lines.map(&:min_width).inject(&:+) + @min_height = lines.map(&:min_height).max + + @data = lines + + hbox + end + + def auto_layout + @data = @default_data + + self.__send__(@type) if @@types.include?(@type) @data.each do |o| o.auto_layout if WSLayout === o diff --git a/sample/minsample.rb b/sample/minsample.rb index 1ca39c4..a3f2f61 100644 --- a/sample/minsample.rb +++ b/sample/minsample.rb @@ -147,7 +147,6 @@ def initialize super(100, 340, 300, 200, "LayoutTest") b1 = WS::WSButton.new(nil, nil, 100, nil, "btn1") # オートレイアウトで自動設定させる座標やサイズはnilでよい -# b2 = WS::WSButton.new(0, 0, 100, 20, "btn2") b2 = WS::WSImageButton.new(nil, nil, Image.load('./image/enemyshot2.png'), nil, nil, "btn2") self.client.add_control(b1) self.client.add_control(b2) @@ -186,10 +185,10 @@ def initialize # addメソッドの第2、第3引数でそれぞれresizable_width/resizable_heightを指定できるようにした。 # widthやheightがnilになっている場合、自動的にresizable_width/resizable_heightがtrueになるので指定する必要がなくなった。 # でもnilじゃなく値を入れたときにオートレイアウトさせたければ第2、第3引数を指定する必要がある。 - client.layout(:vbox) do + client.layout(:vbox_ex) do self.margin_top = 10 self.margin_bottom = 10 - layout(:hbox) do + layout(:hbox_ex) do add b1 add img end @@ -208,6 +207,35 @@ def initialize WS.desktop.add_control(t) +class Test2 < WS::WSWindow + def initialize + super(400, 340, 300, 200, "LayoutTest2") + + f_size = @font.size + str = "これはレイアウトを利用した簡易自動改行のデモです。文字を1つひとつ分解して別のコントロールにしてるから、重くなりそうだけど、いい感じでは? by Hyde".split("") + str.map! do |s| + ctl = WS::WSLabel.new(0,0, @font.getWidth(s), f_size + 2, s) #Layoutするので座標は適当 + self.client.add_control(ctl) + ctl + end + + client.layout(:top) do + self.margin_left = 16 + self.margin_right = 16 + self.margin_top = 20 + self.margin_bottom = 12 + layout(:left_ex) do + str.each do |ctl| + add ctl, false, false + end + end + end + end +end +t = Test2.new +WS.desktop.add_control(t) + + # デザイン定義と動作定義のコードを分けるサンプル # フォーム定義(TestWindow1)