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