This commit is contained in:
AJ Kelly
2010-12-28 13:47:02 -06:00
5 changed files with 307 additions and 24 deletions
+88
View File
@@ -0,0 +1,88 @@
ThemePrefs, v0.7
----------------
ThemePrefs is a system for handling theme-specific preferences in a standard,
efficient, and predictable way. All you do, basically, is declare a table of
preferences and options and use GetThemePref(name) to get your preference
values as needed. This system will handle all disk I/O as well.
Additionally, it can work in combination with ThemePrefsRows to generate
OptionsRows for all preferences, saving you (most of) the trouble with
integrating it into your theme. More on that later.
The prefs are written, using IniFile, into Save/ThemePrefs.ini, written under
the section corresponding to your theme name (if ThemeInfo exists, it will
use the Name field listed there; otherwise, it will use the folder name).
----------------------
Section 1: Declaration
----------------------
To declare preferences for your theme to use, make a table with named
tables in it; its name is used for the preference, and the tables hold
the options for that preference. All you need to know for now is that
Default is required; for more options, check ThemePrefsRows.
After the table is declared, pass it to the function ThemePrefs.InitAll().
(If you're planning not to use any OptionsRows at all, you can just use
ThemePrefs.Init() to save some processing time, but seriously give the
ThemePrefsRows system a look before you disregard it.)
[code]
local Prefs =
{
BoolPref = { Default = false },
IntPref = { Default = 3 },
}
ThemePrefs.InitAll( Prefs )
[/code]
If your theme is "default", this will generate a ThemePrefs.ini like so:
[default]
BoolPref=false
IntPref=3
----------------
Section 2: Usage
----------------
You can access the value with ThemePrefs.Get(name) or GetThemePref(name):
both calls work identically. The return type will be based on whatever you set
it to, but the subsystem will always try number, then bool, then string, in
that order. Use true and false for bools, not 1 and 0.
For example, if you wanted to branch something in the metrics based on
the value of BoolPref (which is a boolean), you could use the following:
NextScreen=GetThemePref('BoolPref') and "ScreenTrue" or "ScreenFalse"
If you want to set a preference value for some reason, you can use either
ThemePrefs.Set(name, value) or SetThemePref(name, value), but the point of
this API is to simplify and standardize prefs handling, so you really should
not need it. (Use ThemePrefsRows instead.)
-----------------------
Section 3: Localization
-----------------------
ThemePrefs has two strings that can be localized, to be placed under
[ThemePrefs] in the appropriate language INI.
IniFileMissing - displayed when IniFile.lua is not found, which means
ThemePrefs cannot be loaded from or written to disk
UnknownPreference - displayed when an attempt is made to read or write
a preference that does not exist in the system. Uses %s for the
name of the unknown preference.
---------------------
Section 4: Disclaimer
---------------------
Though this is working fine so far, it is still experimental! If you run
into problems, or have suggestions, comments, questions, etc., please talk
to "vyhd" in #sm-ssc on irc.badnik.net, as it's his code to maintain; he
hates subjecting other people to his code (and occasionally vice versa),
but will gladly address concerns that come up.
+145
View File
@@ -0,0 +1,145 @@
ThemePrefsRows, v0.5
--------------------
ThemePrefsRows is an optional system that works with ThemePrefs to generate
Lua-based OptionsRows for every preference used in the system. All you do is
declare a table of preferences and pass it to ThemePrefs.InitAll(), which will
also initialize ThemePrefs. Note that you do need to add an entry for each
preference in [OptionTitles] and [OptionExplanations] to give the row its name
and describe what it does; I hope to find a (clean) way to do it from Lua.
----------------------
Section 1: Declaration
----------------------
Declaring preferences is the same basic concept as ThemePrefs; see that doc
for the basics. So I'll go into the stuff that's important to this API.
As mentioned in ThemePrefs, the table contains options. The possible options
and their types are outlaid here. (The pref's type is defined by Default.)
Default: whatever the value is set to by default. For sanity's sake, you
should have this in your Choices table.
Choices (table): an indexed array containing all the possible OptionsRow
choices. If you're using a Values table, the array values should be
strings. If not, you can use any type that can cast to a string.
Values (table): indexed array of the pref's type containing the value used when
the Choices entry with the same index is chosen in the row. If that
confused you, don't worry: it'll make more sense in the example.
This is optional, if your pref type can cast to strings.
Params (table): optional modifications to the OptionsRow. I'll cover this in
more detail in a later section. You probably won't need it.
To make this clearer, have an example of a valid prefs table:
[code]
local prefs =
{
BoolPref =
{
Default = false,
Choices = { "On", "Off" },
Values = { true, false },
},
IntPref =
{
Default = 3,
Choices = { 1, 2, 3, 4, 5 },
Params = { SelectType = "ShowOneInRow" }
}
}
ThemePrefs.InitAll( prefs )
[/code]
So when "On" is selected, BoolPref will be set to true, and selecting "Off"
will set it false. Simple enough, yeah? This will scale to any type you like.
Enumerations, strings, whatever.
-----------------
Section 2: Params
-----------------
We did promise a more in-depth look at Params, so here it is.
The following arguments and values are currently accepted for Params:
LayoutType (string): "ShowAllInRow" (default), "ShowOneInRow".
ExportOnChange (bool): currently disabled in the source, but available to
set in case it's enabled later. If true, the Save function (which
handles actually setting the preference) is called whenever a value
is changed rather than when the screen changes.
EnabledForPlayers (function): has 'self' as an argument, returns a table of
PlayerNumbers who are allowed to select stuff. Not important yet.
ReloadRowMessages (table): contains an indexed array of strings. Whenever
any of the messages in this table are broadcast, the Load function
(which loads the list of possible options and sets the selected
value/s) is called again.
LoadSelections (function): takes self, list, pn, overrides the default
function. This is advanced usage, so you should probably know
what you're doing to use it. Probably unnecessary.
SaveSelections (function): takes self, list, pn, and overrides the default
function. This is advanced as well, for the same reasons. Fortunately,
you probably won't need it.
----------------
Section 3: Usage
----------------
To get the OptionsRow for the preference you want, use ThemePrefRow(name)
or ThemePrefsRows.GetRow(name); they're the same function. You'd use this
as, for example in the metrics:
Line1="lua,ThemePrefRow('BoolPref')"
Unfortunately, you do need to do some more work with the Language INIs too.
For each declared preference, you need an entry under [OptionTitles] for its
title and an entry under [OptionExplanations] for its description. The key for
both is the name of the preference. With the above example, that'd be e.g.
[OptionTitles]
(...)
BoolPref=BoolPref
IntPref=IntPref
[OptionExplanations]
(...)
BoolPref=Toggles a simple boolean preference between true and false.
IntPref=Sets an integer value between 1 and 5.
-----------------------
Section 4: Localization
-----------------------
ThemePrefsRows has three themable strings:
NoDefaultInValues - if the default value isn't actually in choices or values,
this is displayed. Takes %s for the affected preference's name.
TypeMismatch - if the default type and a value type mismatch, this is shown.
Takes %s, %d, and %s for the default's type, the index of the
mismatching value, and the value's type respectively.
ChoicesSizeMismatch - if the Choices and Values arrays have different
lengths, this is displayed. Takes %d and %d for the size of
Choices and Values, respectively.
---------------------
Section 5: Disclaimer
---------------------
This isn't as tested as I'd like. For this version, it remains experimental.
Quite experimental. If you run into problems, please let me know so I can
fix them.
Please direct all questions, comments, complaints, bug reports, etc. to "vyhd"
in #sm-ssc on irc.badnik.net or whatever other form of communication you like.
+11 -1
View File
@@ -1947,6 +1947,7 @@ W5=Boo
Held=Held
MaxCombo=Max Combo
Miss=Miss
[NativeLanguageNames]
Afar=Afar
Abkhazian=аҧсуа бызшәа
@@ -2274,4 +2275,13 @@ Karaoke=Karaoke
Lights_Cabinet=Lights
[ScreenHowToInstallSongs]
HeaderText=Installing Songs
HeaderText=Installing Songs
[ThemePrefs]
IniFileMissing=IniFile missing. Can't read or write from disk :(
UnknownPreference=Unknown preference "%s"
[ThemePrefsRows]
ChoicesSizeMismatch=Choices and Values have different sizes (%d, %d)
NoDefaultInValues=Preference "%s" has no values matching default
TypeMismatch=Type mismatch between default (%s) and value %d (%s)
+19 -12
View File
@@ -3,12 +3,16 @@ ThemePrefs: handles the underlying structure for ThemePrefs, so any themes
built off of this can simply declare their prefs and default values, and
access them through this system.
v0.7: Dec. 15, 2010. Initial version.
v0.7.1: Dec. 28, 2010. Added language support.
v0.7.0: Dec. 15, 2010. Initial version.
vyhd wrote this for sm-ssc. <3 you guys
--]]
-- If we don't have IniFile, we can't read to or write from disk
if not IniFile then
Warn( "ThemePrefs: IniFile missing. Can't read/write from disk :(" )
-- local function to handle themed error strings
-- (and to ensure we're getting all of them from the same section)
local function GetString( name )
return THEME:GetString( "ThemePrefs", name )
end
function PrintTable( tbl )
@@ -74,6 +78,9 @@ ThemePrefs =
-- Only read from disk once, when _fallback calls this; we just
-- need the base set once to add prefs onto.
Init = function( prefs, bLoadFromDisk )
-- If we don't have IniFile, we can't read/write from/to disk
if not IniFile then Warn( GetString("IniFileMissing") ) end
Trace( ("ThemePrefs.Init(prefs, %s)"):format(tostring(bLoadFromDisk)) )
if bLoadFromDisk then
Trace( "ThemePrefs.Init: loading from disk" )
@@ -83,20 +90,20 @@ ThemePrefs =
Trace( "ThemePrefs.Init: not loading from disk" )
-- create the section if it doesn't exist
local name = GetThemeName()
PrefsTable[name] = PrefsTable[name] and PrefsTable[name] or { }
local section = GetThemeName()
PrefsTable[section] = PrefsTable[section] and PrefsTable[section] or { }
Trace( "Using section " .. name )
Trace( "Using section " .. section )
-- if the key doesn't exist, add it with our default value
for k, tbl in pairs(prefs) do
if not PrefsTable[name][k] then
if not PrefsTable[section][k] then
Trace( k .. " doesn't exist, creating" )
PrefsTable[name][k] = tbl.Default
PrefsTable[section][k] = tbl.Default
end
end
PrintTable( PrefsTable[name] )
PrintTable( PrefsTable[section] )
end,
Load = function()
@@ -117,7 +124,7 @@ ThemePrefs =
Trace( ("ThemePrefs.Get(%s)"):format(name) )
local tbl = ResolveTable(name)
if tbl then return tbl[name] end
Warn( ("GetThemePref: unknown preference \"%s\""):format(name) )
Warn( "Get: "..GetString("UnknownPreference"):format(name) )
return nil
end,
@@ -125,7 +132,7 @@ ThemePrefs =
Trace( ("ThemePrefs.Set(%s, %s)"):format(name, tostring(value)) )
local tbl = ResolveTable(name)
if tbl then tbl[name] = value; NeedsSaved = true; return end
Warn( ("SetThemePref: unknown preference \"%s\""):format(name) )
Warn( "Set: "..GetString("UnknownPreference"):format(name) )
end,
};
+44 -11
View File
@@ -3,8 +3,11 @@ ThemePrefsRows: you give it the choices, values, and params, and it'll
generate the rest; quirky behavior to be outlined below. Documentation
will be provided once this system is stabilized.
v0.6: Dec. 27, 2010. Fix Choices not necessarily being strings.
v0.5: Dec. 15, 2010. Initial version. Not very well tested.
v0.5.2: Dec. 28, 2010. Throw an error for default/value type mismatches.
v0.5.1: Dec. 27, 2010. Fix Choices not necessarily being strings.
v0.5.0: Dec. 15, 2010. Initial version. Not very well tested.
vyhd wrote this for sm-ssc
--]]
-- unless overridden, these parameters will be used for the OptionsRow
@@ -23,7 +26,12 @@ local DefaultParams =
SaveSelections = nil,
}
local function DefaultLoadSelections( pref, default, choices, values )
-- local alias to simplify error reporting
local GetString( name )
return THEME:GetString( "ThemePrefsRows", name )
end
local function DefaultLoad( pref, default, choices, values )
return function(self, list, pn)
local val = ThemePrefs.Get( pref )
@@ -37,13 +45,13 @@ local function DefaultLoadSelections( pref, default, choices, values )
if values[i] == default then list[i] = true return end
end
-- set to the first value and throw a warning
Warn( ("LoadSelections: preference \"%s\"'s default not in Values"):format(pref) )
-- set to the first value and output a warning
Warn( GetString("NoDefaultInValues"):format(pref) )
list[1] = true
end
end
local function DefaultSaveSelections( pref, choices, values )
local function DefaultSave( pref, choices, values )
local msg = "ThemePrefChanged"
local params = { Name = pref }
@@ -55,14 +63,31 @@ local function DefaultSaveSelections( pref, choices, values )
end
end
-- This function checks for mismatches between the default value and the
-- values table passed to the ThemePrefRow, e.g. it will return false if
-- you have a boolean default and an integer value. I'm somewhat stricter
-- about types than Lua is because I don't like the unpredictability and
-- complexity of coercing values transparently in a bunch of places.
local function TypesMatch( Values, Default )
local DefaultType = type(Default)
for i, value in ipairs(Values) do
local ValueType = type(value)
if ValueType ~= DefaultType then
Warn( GetString("TypeMismatch"):format(DefaultType, i, ValueType) )
return false
end
end
return true
end
local function CreateThemePrefRow( pref, tbl )
Trace( "CreateThemePrefRow( " .. pref .. " )" )
-- can't make an option handler without options
if not tbl.Choices then return nil end
local Choices = tbl.Choices
local Default = tbl.Default
local Values = tbl.Values and tbl.Values or Choices
local Params = tbl.Params and tbl.Params or { }
@@ -71,6 +96,15 @@ local function CreateThemePrefRow( pref, tbl )
Choices[i] = tostring( Chances[i] )
end
-- check to see that Values and Choices have the same length
if #Choices ~= #Values then
Warn( GetString("ChoicesSizeMismatch") )
return nil
end
-- check to see that everything in Values matches the type of Default
if not TypesMatch( Values, Default ) then return nil end
-- set the name and choices here; we'll do the rest below
local Handler = { Name = pref, Choices = Choices }
@@ -83,11 +117,11 @@ local function CreateThemePrefRow( pref, tbl )
-- if we don't have LoadSelections and SaveSelections, make them
if not Handler.LoadSelections then
Handler.LoadSelections = DefaultLoadSelections( pref, tbl.Default, Choices, Values )
Handler.LoadSelections = DefaultLoad( pref, Default, Choices, Values )
end
if not Handler.SaveSelections then
Handler.SaveSelections = DefaultSaveSelections( pref, Choices, Values )
Handler.SaveSelections = DefaultSave( pref, Choices, Values )
end
return Handler
@@ -125,7 +159,6 @@ ThemePrefRow = ThemePrefsRows.GetRow
ThemePrefs.InitAll = function( prefs )
Trace( "ThemePrefs.InitAll( prefs )" )
-- HACK: for now, don't worry about extraneous file I/O
ThemePrefs.Init( prefs, true )
ThemePrefsRows.Init( prefs )
end