Mercurial > projects > mde
diff mde/lookup/Options.d @ 94:9520cc0448e5
Boolean options are now encapsulated within a Content class (currently an experiment).
This should facilitate generic option editing widgets.
author | Diggory Hardy <diggory.hardy@gmail.com> |
---|---|
date | Thu, 23 Oct 2008 17:45:49 +0100 |
parents | 97e6dce08037 |
children | 2a364c7d82c9 |
line wrap: on
line diff
--- a/mde/lookup/Options.d Tue Oct 21 11:35:15 2008 +0100 +++ b/mde/lookup/Options.d Thu Oct 23 17:45:49 2008 +0100 @@ -30,6 +30,8 @@ import mde.setup.paths; import mde.exception; +public import mde.gui.content.Content; + import mde.file.mergetag.Reader; import mde.file.mergetag.Writer; import mde.file.mergetag.DataSet; @@ -52,7 +54,7 @@ * should be used for reading variables, and via the addOptionsClass() hook will be loaded from * files during pre-init (init0 stage). Do not write changes directly to the subclasses or they will * not be saved; instead use set(), for example, miscOpts.set!(char[])("L10n","en-GB"). Use an -* example like OptionsMisc as a template for creating a new Options sub-class. +* example like MiscOptions as a template for creating a new Options sub-class. * * Optionally, overload the validate() function. This is called after loading, allowing conditions * to be enforced on variables. Use set!()() to change the variables. If an exception is thrown, @@ -79,7 +81,7 @@ // All supported types, for generic handling via templates. It should be possible to change // the supported types simply by changing this list now (untested). template store(A...) { alias A store; } - alias store!(bool, int, double, char[]) TYPES; + alias store!(int, double, char[]) TYPES;//FIXME removed bool //BEGIN Templates: internal private { // Get name of a type. Basically just stringof, but special handling for arrays. @@ -92,11 +94,14 @@ } // Pointer lists - template PLists(T, A...) { + template PLists(A...) { static if (A.length) { - const char[] PLists = T.stringof~"*[ID] opts"~TName!(T)~";\n" ~ PLists!(A); + static if (is (T == bool)) { + const char[] PLists = PLists!(A[1..$]); + } else + const char[] PLists = A[0].stringof~"*[ID] opts"~TName!(A[0])~";\n" ~ PLists!(A[1..$]); } else - const char[] PLists = T.stringof~"*[ID] opts"~TName!(T)~";\n"; + const char[] PLists = ""; } // True if type is one of A @@ -112,10 +117,19 @@ // For addTag template addTagMixin(T, A...) { - const char[] ifBlock = `if (tp == "`~T.stringof~`") { - `~T.stringof~`** p = id in opts`~TName!(T)~`; - if (p !is null) **p = parseTo!(`~T.stringof~`) (dt); - }`; + static if (is(T == bool)) { + const char[] ifBlock = `if (tp == "`~T.stringof~`") { + auto p = id in opts; + if (p) { + auto q = cast(BoolContent) (*p); + if (q) q = parseTo!(`~T.stringof~`) (dt); + } +}`; + } else + const char[] ifBlock = `if (tp == "`~T.stringof~`") { + `~T.stringof~`** p = id in opts`~TName!(T)~`; + if (p !is null) **p = parseTo!(`~T.stringof~`) (dt); +}`; static if (A.length) const char[] addTagMixin = ifBlock~` else `~addTagMixin!(A).addTagMixin; else @@ -130,6 +144,14 @@ } else const char[] writeAllMixin = ``; } + + // For list + template listMixin(A...) { + static if (A.length) { + const char[] listMixin = `ret ~= opts`~TName!(A[0])~`.keys;` ~ listMixin!(A[1..$]); + } else + const char[] listMixin = ``; + } } //END Templates: internal @@ -144,6 +166,7 @@ assert (c !is null); // Instance must be created before calling addOptionsClass assert (((cast(ID) i) in subClasses) is null); // Don't allow a silent replacement } body { + c.secName = i; subClasses[cast(ID) i] = c; } @@ -233,7 +256,7 @@ * via hash-maps, which is a little slower than direct access but necessary since the option * must be changed in two separate places. */ void set(T) (char[] symbol, T val) { - static assert (TIsIn!(T,TYPES), "Options does not support type "~T.stringof); + static assert (TIsIn!(T,TYPES) && !is(T == bool), "Options does not support type "~T.stringof); changed = true; // something got set (don't bother checking this isn't what it already was) @@ -245,7 +268,7 @@ logger.error ("Options.set: invalid symbol"); } } - + /+ /** Get option symbol of an Options sub-class. * * Using this method to read an option is not necessary, but allows for generic use. */ @@ -260,25 +283,30 @@ // log and ignore: logger.error ("Options.get: invalid symbol"); } - } + }+/ /** List the names of all options of a specific type. */ - char[][] list(T) () { - static assert (TIsIn!(T,TYPES), "Options does not support type "~T.stringof); - - mixin (`alias opts`~TName!(T)~` optsVars;`); - - return optsVars.keys; + char[][] list () { + char[][] ret; + mixin (listMixin!(TYPES)); + return ret; + } + + /// Get all Options stored with a ValueContent. + ValueContent[char[]] content() { + return opts; } /// Variable validate function. This implementation does nothing. void validate() {} protected { + char[] secName; // name of this option setting; set null after translation is loaded OptionChanges optionChanges; // all changes to options (for saving) // The "pointer lists", e.g. char[]*[ID] optscharA; - mixin (PLists!(TYPES)); + mixin (PLists!(TYPES)); //FIXME adds unused optsbool + ValueContent[char[]] opts; // generic list of option values } //BEGIN Mergetag loading/saving code @@ -295,6 +323,19 @@ //BEGIN Templates: impl & optionsThis private { + // Replace, e.g., bool, with BoolContent + template contentName(A) { + static if (is(A == bool)) { + const char[] contentName = "BoolContent"; + } else static if (is(A == int)) { + const char[] contentName = "int";// no IntContent yet + } else static if (is(A == double)) { + const char[] contentName = "double"; + } else static if (is(A == char[])) { + const char[] contentName = "char[]"; + } else + static assert (false, "unsuppurted type: "~ A); + } // Return index of first comma, or halts if not found. template cIndex(char[] A) { static if (A.length == 0) @@ -313,9 +354,10 @@ else const size_t scIndex = 1 + scIndex!(A[1..$]); } - // Look for "type symbols;" in A and return symbols as a comma separated list of names - // (even if type is encountered more than once). Output may contain spaces and, if - // non-empty, will contain a trailing comma. Assumes scIndex always returns less than A.$. + /* Look for "type symbols;" in A and return symbols as a comma separated list of names + (even if type is encountered more than once). Output may contain spaces and will have a + trailing comma unless no match was found in which case an empty string is returned. + Assumes scIndex always returns less than A.$ . */ template parseT(char[] type, char[] A) { static if (A.length < type.length + 1) // end of input, no match const char[] parseT = ""; @@ -327,7 +369,7 @@ else // no match const char[] parseT = parseT!(type, A[scIndex!(A)+1 .. $]); } - // May have a trailing comma. Assumes cIndex always returns less than A.$. + // May have a trailing comma. Assumes cIndex always returns less than A.$ . template aaVars(char[] A) { static if (A.length == 0) const char[] aaVars = ""; @@ -337,6 +379,16 @@ const char[] aaVars = "\""~A[0..cIndex!(A)]~"\"[]:&"~A[0..cIndex!(A)] ~ "," ~ aaVars!(A[cIndex!(A)+1..$]); } + // May have a trailing comma. Assumes cIndex always returns less than A.$ . + template aaVarsBool(char[] A) {//FIXME + static if (A.length == 0) + const char[] aaVarsBool = ""; + else static if (A[0] == ' ') + const char[] aaVarsBool = aaVarsBool!(A[1..$]); + else + const char[] aaVarsBool = "\""~A[0..cIndex!(A)]~"\"[]:"~A[0..cIndex!(A)] ~ "," ~ + aaVarsBool!(A[cIndex!(A)+1..$]); + } // strip Trailing Comma template sTC(char[] A) { static if (A.length && A[$-1] == ',') @@ -353,19 +405,54 @@ else const char[] listOrNull = "["~A~"]"; } + // if B is empty return an empty string otherswise return what's below: + template catOrNothing(char[] A,char[] B) { + static if (B.length) + const char[] catOrNothing = A~` `~sTC!(B)~";\n"; + else + const char[] catOrNothing = ``; + } + // foreach decl... + template createBCs(char[] A) { + static if (A.length == 0) + const char[] createBCs = ""; + else static if (A[0] == ' ') + const char[] createBCs = createBCs!(A[1..$]); + else + const char[] createBCs = A[0..cIndex!(A)]~ " = new BoolContent (false);\n"~ + createBCs!(A[cIndex!(A)+1..$]); + } // for recursing on TYPES template optionsThisInternal(char[] A, B...) { static if (B.length) { + static if (is(B[0] == bool)) {//FIXME + const char[] optionsThisInternal = createBCs!(parseT!(B[0].stringof,A))~ + `opts = `~listOrNull!(sTC!(aaVarsBool!(parseT!(B[0].stringof,A))))~";\n" ~ + optionsThisInternal!(A,B[1..$]); + } else const char[] optionsThisInternal = `opts`~TName!(B[0])~` = `~listOrNull!(sTC!(aaVars!(parseT!(B[0].stringof,A))))~";\n" ~ optionsThisInternal!(A,B[1..$]); } else const char[] optionsThisInternal = ``; } + template declValsInternal(char[] A, B...) { + static if (B.length) { + const char[] declValsInternal = catOrNothing!(contentName!(B[0]),parseT!(B[0].stringof,A)) ~ declValsInternal!(A,B[1..$]); + } else + const char[] declValsInternal = ``; + } } protected { + /** Declares the values. + * + * Basic types are replaced with a ValueContent class to keep the option synchronized and + * generalize use. */ + template declVals(char[] A) { + const char[] declVals = declValsInternal!(A, TYPES,bool); + } /** Produces the implementation code to go in the constuctor. */ template optionsThis(char[] A) { const char[] optionsThis = "optionChanges = new OptionChanges;\n" ~ - optionsThisInternal!(A,TYPES); + optionsThisInternal!(A,TYPES,bool); } /+ Needs too many custom parameters to be worth it? Plus makes class less readable. /** Produces the implementation code to go in the static constuctor. */ @@ -381,7 +468,7 @@ * * In case this() needs to be customized, mixin(impl!(A)) is equivalent to: * --- - * mixin (A~` + * mixin (declVals!(A)~` this () { `~optionsThis!(A)~` }`); @@ -395,7 +482,7 @@ * Extending: mixins could also be used for the static this() {...} or even the whole * class, but doing so would rather decrease readability of any implementation. */ template impl(char[] A /+, char[] symb+/) { - const char[] impl = A~"\nthis(){\n"~optionsThis!(A)~"}"; + const char[] impl = declVals!(A)~"\nthis(){\n"~optionsThis!(A)~"}"; // ~"\nstatic this(){\n"~optClassAdd!(symb)~"}" } } @@ -466,9 +553,11 @@ */ /** A home for all miscellaneous options, at least for now. */ -OptionsMisc miscOpts; -class OptionsMisc : Options { - mixin (impl!("bool exitImmediately; int maxThreads, logOptions; double pollInterval; char[] L10n, a,b,c,g,z;")); +MiscOptions miscOpts; +class MiscOptions : Options { + const A = "bool exitImmediately; int maxThreads, logOptions; double pollInterval; char[] L10n;"; + //pragma (msg, impl!(A)); + mixin (impl!(A)); void validate() { // Try to enforce sensible values, whilst being reasonably flexible: @@ -481,7 +570,7 @@ } static this() { - miscOpts = new OptionsMisc; - Options.addOptionsClass (miscOpts, "misc"); + miscOpts = new MiscOptions; + Options.addOptionsClass (miscOpts, "MiscOptions"); } }