From 677923d14e041be7c8a772b26996809f94ab7fcb Mon Sep 17 00:00:00 2001 From: Paolo Capriotti Date: Sun, 7 Feb 2010 19:23:42 +0000 Subject: [PATCH] Improve theme configuration dialog. --- lib/board/table.rb | 6 +-- lib/games/games.rb | 21 ++++++++ lib/mainwindow.rb | 7 +-- lib/plugins/clocks/bubbles.rb | 2 +- lib/plugins/themes/lib/theme.rb | 14 +++--- lib/plugins/themes/loader.rb | 92 +++++++++++++++++++++++++++++------ lib/qtutils.rb | 103 ++++++++++++++++++++++++++++++++++++++-- lib/theme_prefs.rb | 78 ++++++++++++++++++++++-------- 8 files changed, 271 insertions(+), 52 deletions(-) diff --git a/lib/board/table.rb b/lib/board/table.rb index ff6ce51..7862ea9 100644 --- a/lib/board/table.rb +++ b/lib/board/table.rb @@ -14,9 +14,10 @@ class Table < Qt::GraphicsView attr_reader :elements, :scene, :theme, :game private :game, :scene, :theme - def initialize(scene, loader, parent) + def initialize(scene, loader, theme_loader, parent) super(@scene = scene, parent) @loader = loader + @theme_loader = theme_loader end def reset(match) @@ -34,8 +35,7 @@ class Table < Qt::GraphicsView end end - theme_loader = @loader.get_matching(:theme_loader).new - @theme = theme_loader.load(@game) + @theme = @theme_loader.load(@game) @elements = create_elements relayout diff --git a/lib/games/games.rb b/lib/games/games.rb index c2c03a8..97af860 100644 --- a/lib/games/games.rb +++ b/lib/games/games.rb @@ -45,6 +45,13 @@ class Game end end end + + def self.new_list(parent) + games = GAMES.map do |id, g| + [g.class.data(:name), id.to_s] + end.sort + Qt::ListWidget.from_a(parent, games, lambda{|id| get(id.to_sym) }) + end def self.categories @@categories ||= to_enum(:each).map {|name, game| game.class.data(:category) }.uniq.sort @@ -53,6 +60,20 @@ class Game private + class GameListWidgetItem < Qt::ListWidgetItem + GAME_ID_ROLE = Qt::UserRole + + def initialize(name, id, list) + super(name, list) + set_data(GAME_ID_ROLE, Qt::Variant.new(id)) + end + + def game + id = data(GAME_ID_ROLE).toString.to_sym + Game.get(id) + end + end + class GameProxy def initialize(game, context) @game = game diff --git a/lib/mainwindow.rb b/lib/mainwindow.rb index 5bbd35c..cc69013 100644 --- a/lib/mainwindow.rb +++ b/lib/mainwindow.rb @@ -35,6 +35,7 @@ class MainWindow < KDE::XmlGuiWindow super nil @loader = loader + @theme_loader = @loader.get_matching(:theme_loader).new startup(game) setup_actions @@ -88,7 +89,7 @@ private regular_action :configure_themes, :icon => 'game-config-theme', :text => KDE.i18n("Configure &Themes...") do - dialog = ThemePrefs.new(@loader, self) + dialog = ThemePrefs.new(@loader, @theme_loader, self) dialog.show end @@ -110,7 +111,7 @@ private def create_view(opts = { }) scene = Scene.new - table = Table.new(scene, @loader, @view) + table = Table.new(scene, @loader, @theme_loader, @view) contr = Controller.new(table, @field) movelist = @loader.get_matching(:movelist).new(contr) v = View.new(table, contr, movelist) @@ -136,7 +137,7 @@ private @engine_loader = @loader.get_matching(:engine_loader).new @engine_loader.reload - + @console = Console.new(nil) console_dock = Qt::DockWidget.new(self) console_dock.widget = @console diff --git a/lib/plugins/clocks/bubbles.rb b/lib/plugins/clocks/bubbles.rb index c306603..cffd962 100644 --- a/lib/plugins/clocks/bubbles.rb +++ b/lib/plugins/clocks/bubbles.rb @@ -19,7 +19,7 @@ class BubblesClock < Qt::GraphicsItemGroup include ClockDisplay plugin :name => 'Bubbles Clock Skin', - :interface => :clock2 + :interface => :clock attr_reader :items, :rect, :clock diff --git a/lib/plugins/themes/lib/theme.rb b/lib/plugins/themes/lib/theme.rb index b766797..251650b 100644 --- a/lib/plugins/themes/lib/theme.rb +++ b/lib/plugins/themes/lib/theme.rb @@ -6,13 +6,15 @@ # (at your option) any later version. class Theme - attr_reader :pieces, :board, - :clock, :layout + def self.components + [ :pieces, :board, :clock, :layout ] + end + + attr_reader *components def initialize(opts = { }) - @pieces = opts[:pieces] - @board = opts[:board] - @clock = opts[:clock] - @layout = opts[:layout] + self.class.components.each do |component| + instance_variable_set("@#{component}", opts[component]) + end end end diff --git a/lib/plugins/themes/loader.rb b/lib/plugins/themes/loader.rb index 317476a..99d45cd 100644 --- a/lib/plugins/themes/loader.rb +++ b/lib/plugins/themes/loader.rb @@ -14,18 +14,24 @@ class ThemeLoader plugin :name => 'Default Theme Loader', :interface => :theme_loader + FALLBACK_SPEC = { :pieces => CelticPieces, + :board => XBoardBackground, + :clock => XBoardClock, + :layout => XBoardLayout } + def initialize config = KDE::Global.config.group('Themes') if config.exists @themes_cat = { } config.group("Categories").each_group do |cat| - @themes_cat[cat.name] = cat.entry_map.with_symbol_keys + @themes_cat[cat.name] = cat.entry_map.maph {|k,v| [k.to_sym, eval(v) ] } end @themes = { } config.group("Games").each_group do |game| - @themes[game.name] = game.entry_map.with_symbol_keys + @themes[game.name] = game.entry_map.maph {|k,v| [k.to_sym, eval(v) ] } end else + # default theme configuration @themes_cat = { 'Chess' => { :pieces => CelticPieces, :board => XBoardBackground, @@ -40,22 +46,80 @@ class ThemeLoader end end + def set(type, name, component, klass) + hash = type == :game ? @themes : @themes_cat + name = name.to_s + hash[name] ||= { } + hash[name][component] = klass + end + def load(game, opts = { }) - spec = @themes[game.class.data(:id)] - unless spec - _, spec = @themes_cat.find do |category, theme| - game.class.data(:category) == category - end - spec ||= @themes_cat['Chess'] - end + spec = load_spec(:game => game) Theme.new( - :pieces => spec[:pieces].new(:shadow => true), - :board => spec[:board].new(:game => game), - :clock => spec[:clock], - :layout => spec[:layout].new(game) + :pieces => read_spec(spec, :pieces).new(:shadow => true), + :board => read_spec(spec, :board).new(:game => game), + :clock => read_spec(spec, :clock), + :layout => read_spec(spec, :layout).new(game) ) end + def load_spec(opts) + spec = nil + if opts[:game] + name = opts[:game].class.data(:id).to_s + spec = @themes[name] || { } + spec[:fallback] = load_spec(:category => opts[:game].class.data(:category)) + else + spec = @themes_cat[opts[:category]] || { } + spec[:fallback] = FALLBACK_SPEC + end + spec + end + def save + themes_config = KDE::Global.config.group('Themes') + themes_config.delete_group + + game_config = themes_config.group('Games') + game_config.delete_group + + @themes.each do |game, components| + game_group = game_config.group(game) + game_group.delete_group + + components.each do |component, klass| + if klass.respond_to? :new + game_group.write_entry(component.to_s, klass.name) + end + end + end + + game_config.sync + + cat_config = themes_config.group('Categories') + cat_config.delete_group + + @themes_cat.each do |cat, components| + cat_group = cat_config.group(cat) + cat_group.delete_group + + components.each do |component, klass| + if klass.respond_to? :new + cat_group.write_entry(component.to_s, klass.name) + end + end + end + + cat_config.sync + end + + private + + def read_spec(spec, component) + klass = spec[component] + if not klass and spec[:fallback] + klass = read_spec(spec[:fallback], component) + end + klass end -end \ No newline at end of file +end diff --git a/lib/qtutils.rb b/lib/qtutils.rb index c745e63..c243d2b 100644 --- a/lib/qtutils.rb +++ b/lib/qtutils.rb @@ -23,10 +23,12 @@ module Enumerable end class Hash - def with_symbol_keys - result = { } - each do |key, value| - result[key.to_sym] = value + def maph + { }.tap do |result| + each do |key, value| + key, value = yield key, value + result[key] = value + end end end end @@ -192,6 +194,97 @@ class Qt::Timer end end +module ListLike + module ClassMethods + # + # Create a list from an array of pairs (text, data) + # The data for each item can be retrieved using the + # item's get method. + # Note that if an array element is not a pair, its + # value will be used both for the text and for the + # data. + # + # For example: list.current_item.get + # + def from_a(parent, array, extract_data = nil) + extract_data ||= lambda {|data| data } + item_factory = create_item_factory(extract_data) + new(parent).tap do |list| + array.each do |text, data| + item_factory.new(text, list, data || text) + end + list.extract_data = extract_data + end + end + end + + attr_accessor :extract_data + + def select_item(&blk) + (0...count).each do |i| + if blk[item(i).get] + self.current_index = i + break i + end + end + nil + end + + def self.included(base) + base.extend ClassMethods + end +end + +class Qt::ListWidget + FROM_A_DATA_ROLE = Qt::UserRole + include ListLike + + def self.create_item_factory(extract_data) + Class.new(Qt::ListWidgetItem) do + define_method(:initialize) do |text, list, data| + super(text, list) + set_data(FROM_A_DATA_ROLE, Qt::Variant.new(data)) + end + + define_method(:get) do + extract_data[data(FROM_A_DATA_ROLE).value] + end + end + end + + def current_index=(i) + self.current_row = i + end +end + +class KDE::ComboBox + include ListLike + + class Item + def initialize(data) + @data = data + end + + def get + @data + end + end + + def self.create_item_factory(extract_data) + Factory.new do |text, list, data| + list.add_item(text, Qt::Variant.new(data)) + end + end + + def current_item + item(current_index) + end + + def item(i) + Item.new(extract_data[item_data(i).value]) + end +end + module ModelUtils def removing_rows(parent, first, last) if first > last @@ -287,7 +380,7 @@ module ActionHandler end end -class KDE::Config +class KDE::ConfigGroup def each_group group_list.each do |g| yield group(g) diff --git a/lib/theme_prefs.rb b/lib/theme_prefs.rb index 5bdd265..a8daf91 100644 --- a/lib/theme_prefs.rb +++ b/lib/theme_prefs.rb @@ -8,32 +8,53 @@ require 'qtutils' class ThemePrefs < KDE::Dialog - def initialize(loader, parent) + def initialize(loader, theme_loader, parent) super(parent) @loader = loader + @theme_loader = theme_loader widget = Qt::Frame.new(self) layout = Qt::HBoxLayout.new(widget) @tabs = Qt::TabWidget.new(widget) layout.add_widget(@tabs) - @games = Qt::ListWidget.new(nil) - @categories = Qt::ListWidget.new(nil) - @tabs.add_tab(@games, KDE::i18n('&Games')) - @tabs.add_tab(@categories, KDE::i18n('&Categories')) + @lists = { + :game => Game.new_list(nil), + :category => Qt::ListWidget.from_a(nil, Game.categories) + } + @tabs.add_tab(@lists[:game], KDE::i18n('&Games')) + @tabs.add_tab(@lists[:category], KDE::i18n('&Categories')) + @tabs.current_index = 0 + @lists[:game].current_index = 0 info_layout = Qt::VBoxLayout.new layout.add_layout(info_layout) - @pieces = new_labelled(KDE::ComboBox, '&Pieces:', widget, info_layout) - @board = new_labelled(KDE::ComboBox, '&Board:', widget, info_layout) - @layout = new_labelled(KDE::ComboBox, '&Layout:', widget, info_layout) - @clock = new_labelled(KDE::ComboBox, '&Clock:', widget, info_layout) + @pieces = new_labelled(combo_factory(:pieces), '&Pieces:', widget, info_layout) + @board = new_labelled(combo_factory(:board), '&Board:', widget, info_layout) + @layout = new_labelled(combo_factory(:layout), '&Layout:', widget, info_layout) + @clock = new_labelled(combo_factory(:clock), '&Clock:', widget, info_layout) info_layout.add_stretch self.main_widget = widget - - fill_games - fill_categories + + update + @tabs.on('currentChanged(int)') { update } + @lists.each {|type, list| list.on(:itemSelectionChanged) { update(type) } } + on(:okClicked) { @theme_loader.save } + end + + private + + def current_type + if @tabs.current_widget == @lists[:game] + :game + else + :category + end + end + + def item_name(type, data) + type == :game ? data.class.data(:id) : data end def new_labelled(widget_factory, label, parent, layout) @@ -45,17 +66,34 @@ class ThemePrefs < KDE::Dialog widget end - private - - def fill_games - Game.each do |name, game| - @games.add_item(game.class.data(:name)) + def combo_factory(name) + Factory.new do |parent| + themes = @loader.get_all_matching(name).map do |plugin| + [plugin.plugin_name, plugin.name] + end + KDE::ComboBox.from_a(parent, themes, method(:eval)).tap do |combo| + combo.on('currentIndexChanged(int)') do + type = current_type + item = @lists[type].current_item + @theme_loader.set(type, item_name(type, item.get), name, combo.current_item.get) if item + end + end end end - def fill_categories - Game.categories.each do |category| - @categories.add_item(category) + def update(type = nil) + type ||= current_type + if @lists[type].current_item + item = @lists[type].current_item.get + theme = @theme_loader.load_spec(type => item) + theme.each do |component, klass| + combo = instance_variable_get("@#{component}") + if combo + combo.select_item do |data| + data == klass + end + end + end end end end -- 2.11.4.GIT