Changeset 94:9520cc0448e5
- Timestamp:
- 10/23/08 12:45:49
(3 months ago)
- Author:
- Diggory Hardy <diggory.hardy@gmail.com>
- branch:
- default
- Message:
Boolean options are now encapsulated within a Content class (currently an experiment).
This should facilitate generic option editing widgets.
-
Files:
-
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
| r93 |
r94 |
|
| 8 | 8 | <WidgetData|button={0:[0x10,50,50]}> |
|---|
| 9 | 9 | <WidgetData|blank={0:[0x2]}> |
|---|
| 10 | | <WidgetData|opts={0:[0x8110,0],1:["optDBox"]}> |
|---|
| | 10 | <WidgetData|opts={0:[0x8110,0],1:["optDBox","MiscOptions"]}> |
|---|
| 11 | 11 | <WidgetData|optDBox={0:[0xC100,0,2,1],1:["optBox","optDesc"]}> |
|---|
| 12 | 12 | <WidgetData|optBox={0:[0xC100,1,1,3],1:["optName","optSep","optVal"]}> |
|---|
| r92 |
r94 |
|
| 1 | 1 | {MT01} |
|---|
| 2 | | {misc} |
|---|
| | 2 | {MiscOptions} |
|---|
| 3 | 3 | <int|maxThreads=1> |
|---|
| 4 | 4 | <bool|exitImmediately=false> |
|---|
| … | … | |
| 7 | 7 | <double|pollInterval=0.01> |
|---|
| 8 | 8 | |
|---|
| 9 | | {font} |
|---|
| | 9 | {FontOptions} |
|---|
| 10 | 10 | <int|lcdFilter=2> |
|---|
| 11 | 11 | <int|renderMode=0x30000> |
|---|
| 12 | 12 | |
|---|
| 13 | | {video} |
|---|
| | 13 | {VideoOptions} |
|---|
| 14 | 14 | <bool|noFrame=false> |
|---|
| 15 | 15 | <bool|resizable=true> |
|---|
| r89 |
r94 |
|
| 434 | 434 | // this bit of renderMode, if set, means read glyph as BGR not RGB when using LCD rendering |
|---|
| 435 | 435 | enum { RENDER_LCD_BGR = 1 << 30 } |
|---|
| 436 | | OptionsFont fontOpts; |
|---|
| 437 | | class OptionsFont : Options { |
|---|
| | 436 | FontOptions fontOpts; |
|---|
| | 437 | class FontOptions : Options { |
|---|
| 438 | 438 | /* renderMode have one of the following values, possibly with bit 31 set (see RENDER_LCD_BGR): |
|---|
| 439 | 439 | * FT_LOAD_TARGET_NORMAL (0x00000) |
|---|
| … | … | |
| 448 | 448 | |
|---|
| 449 | 449 | static this() { |
|---|
| 450 | | fontOpts = new OptionsFont; |
|---|
| 451 | | Options.addOptionsClass (fontOpts, "font"); |
|---|
| | 450 | fontOpts = new FontOptions; |
|---|
| | 451 | Options.addOptionsClass (fontOpts, "FontOptions"); |
|---|
| 452 | 452 | } |
|---|
| 453 | 453 | } |
|---|
| r91 |
r94 |
|
| 14 | 14 | along with this program. If not, see <http://www.gnu.org/licenses/>. */ |
|---|
| 15 | 15 | |
|---|
| 16 | | /** The content system â type agnostic part. |
|---|
| | 16 | /** The content system â common types. |
|---|
| 17 | 17 | */ |
|---|
| 18 | 18 | module mde.gui.content.Content; |
|---|
| … | … | |
| 20 | 20 | import Int = tango.text.convert.Integer; |
|---|
| 21 | 21 | |
|---|
| 22 | | /** Content â universal part. |
|---|
| | 22 | debug { |
|---|
| | 23 | import tango.util.log.Log : Log, Logger; |
|---|
| | 24 | private Logger logger; |
|---|
| | 25 | static this () { |
|---|
| | 26 | logger = Log.getLogger ("mde.gui.content.Content"); |
|---|
| | 27 | } |
|---|
| | 28 | } |
|---|
| | 29 | |
|---|
| | 30 | /** IContent â interface for all Content classes. |
|---|
| 23 | 31 | * |
|---|
| 24 | 32 | * Services like copy/paste could work on universal content. However, they would need to run a |
|---|
| … | … | |
| 56 | 64 | /** Generically return strings. |
|---|
| 57 | 65 | * |
|---|
| 58 | | * Every Content should return a string for i == 0; preferably its value. Other values of i |
|---|
| 59 | | * can be used to return other strings. For unsupported values of i, null should be returned. |
|---|
| 60 | | */ |
|---|
| | 66 | * This serves two purposes: generically returning a string of/related to the content (i == 0), |
|---|
| | 67 | * and returning associated descriptors. Functions should adhere to (or add to) this table. |
|---|
| | 68 | * |
|---|
| | 69 | * $(TABLE |
|---|
| | 70 | * $(TR $(TH i) $(TH returns)) |
|---|
| | 71 | * $(TR $(TD 0) $(TD value)) |
|---|
| | 72 | * $(TR $(TD 1) $(TD Translated name or null)) |
|---|
| | 73 | * $(TR $(TD 2) $(TD Translated description or null)) |
|---|
| | 74 | * $(TR $(TD other) $(TD null)) |
|---|
| | 75 | * ) */ |
|---|
| 61 | 76 | char[] toString (uint i); |
|---|
| 62 | 77 | } |
|---|
| 63 | | /+ |
|---|
| 64 | | /** Extension to interface providing text-specific tools. */ |
|---|
| 65 | | interface IContentText : IContent |
|---|
| | 78 | |
|---|
| | 79 | /** Base class for content containing a simple value. |
|---|
| | 80 | * |
|---|
| | 81 | * All derived classes should support functions to set/get any ValueContent type, but return the |
|---|
| | 82 | * default value of any type other than it's own. */ |
|---|
| | 83 | abstract class ValueContent : IContent |
|---|
| 66 | 84 | { |
|---|
| 67 | | char[] text (); /// Get/set the value. |
|---|
| 68 | | void text (char[] v); /// ditto |
|---|
| 69 | | } |
|---|
| 70 | | +/ |
|---|
| 71 | | /+ FIXME - use content lists or drop? |
|---|
| 72 | | /** Get a content from the list (what list?). */ |
|---|
| 73 | | ContentText getContentText (char[] id) { |
|---|
| 74 | | return new ContentText (id); // forget the list for now |
|---|
| | 85 | protected this () {} |
|---|
| | 86 | |
|---|
| | 87 | void name (char[] n, char[] d = null) { |
|---|
| | 88 | name_ = n; |
|---|
| | 89 | desc_ = d; |
|---|
| | 90 | } |
|---|
| | 91 | protected: |
|---|
| | 92 | char[] name_, desc_;// name and description, loaded by lookup.Translation |
|---|
| 75 | 93 | } |
|---|
| 76 | 94 | |
|---|
| 77 | | /** ditto */ |
|---|
| 78 | | ContentInt getContentInt (char[] id) { |
|---|
| 79 | | return new ContentInt (42); // forget the list for now |
|---|
| 80 | | } |
|---|
| 81 | | +/ |
|---|
| 82 | | |
|---|
| 83 | | /+FIXME - currently unused |
|---|
| 84 | | /** Text content. */ |
|---|
| 85 | | /* May end up extending a universal content type. |
|---|
| 86 | | * Services like copy/paste could work on universal content. |
|---|
| 87 | | * |
|---|
| 88 | | * NOTE: Needs to be a reference type really. |
|---|
| 89 | | * Could alternately be: |
|---|
| 90 | | * alias ContentTextStruct* ContentText |
|---|
| 91 | | * where ContentTextStruct is a struct. */ |
|---|
| 92 | | class ContentText : IContent |
|---|
| | 95 | class BoolContent : ValueContent |
|---|
| 93 | 96 | { |
|---|
| 94 | 97 | this () {} |
|---|
| 95 | | this (char[] text) { |
|---|
| 96 | | text_ = text; |
|---|
| | 98 | this (bool val) { |
|---|
| | 99 | v = val; |
|---|
| 97 | 100 | } |
|---|
| 98 | 101 | |
|---|
| | 102 | /// Get the text. |
|---|
| | 103 | char[] toString (uint i) { |
|---|
| | 104 | debug logger.trace ("BoolContent.toString"); |
|---|
| | 105 | return (i == 0) ? v ? "true" : "false" |
|---|
| | 106 | : (i == 1) ? name_ |
|---|
| | 107 | : (i == 2) ? desc_ |
|---|
| | 108 | : null; |
|---|
| | 109 | } |
|---|
| | 110 | |
|---|
| | 111 | void opAssign (bool val) { |
|---|
| | 112 | v = val; |
|---|
| | 113 | } |
|---|
| | 114 | bool opCall () { |
|---|
| | 115 | return v; |
|---|
| | 116 | } |
|---|
| | 117 | |
|---|
| | 118 | protected bool v; |
|---|
| | 119 | } |
|---|
| | 120 | |
|---|
| | 121 | /** Text content. */ |
|---|
| | 122 | class TextContent : ValueContent |
|---|
| | 123 | { |
|---|
| | 124 | this () {} |
|---|
| | 125 | this (char[] text, char[] name = null) { |
|---|
| | 126 | text_ = text; |
|---|
| | 127 | name_ = name; |
|---|
| | 128 | } |
|---|
| | 129 | /+ |
|---|
| 99 | 130 | ContentText dup () { |
|---|
| 100 | 131 | return new ContentText (text_); |
|---|
| … | … | |
| 108 | 139 | return null; |
|---|
| 109 | 140 | } |
|---|
| 110 | | |
|---|
| 111 | | alias toString text; |
|---|
| | 141 | +/ |
|---|
| 112 | 142 | |
|---|
| 113 | 143 | /// Get the text. |
|---|
| 114 | | char[] toString () { |
|---|
| 115 | | return text_; |
|---|
| | 144 | char[] toString (uint i) { |
|---|
| | 145 | debug logger.trace ("TextContent.toString"); |
|---|
| | 146 | return (i == 0) ? text_ |
|---|
| | 147 | : (i == 1) ? name_ |
|---|
| | 148 | : (i == 2) ? desc_ |
|---|
| | 149 | : null; |
|---|
| 116 | 150 | } |
|---|
| 117 | 151 | |
|---|
| 118 | 152 | protected: |
|---|
| 119 | | //NOTE: need to allow cache-invalidating when text changes! |
|---|
| 120 | 153 | char[] text_; |
|---|
| 121 | 154 | } |
|---|
| 122 | | |
|---|
| | 155 | /+ |
|---|
| 123 | 156 | /** Integer content. */ |
|---|
| 124 | 157 | class ContentInt : IContent |
|---|
| r91 |
r94 |
|
| 26 | 26 | import mde.lookup.Translation; |
|---|
| 27 | 27 | |
|---|
| | 28 | debug { |
|---|
| | 29 | import tango.util.log.Log : Log, Logger; |
|---|
| | 30 | private Logger logger; |
|---|
| | 31 | static this () { |
|---|
| | 32 | logger = Log.getLogger ("mde.gui.content.options"); |
|---|
| | 33 | } |
|---|
| | 34 | } |
|---|
| | 35 | |
|---|
| 28 | 36 | class OptionList |
|---|
| 29 | 37 | { |
|---|
| 30 | | this (Options opts, char[] i18nOptionsName) |
|---|
| 31 | | in { assert (opts !is null, "OptionList: invalid Options instance"); } |
|---|
| 32 | | body { |
|---|
| 33 | | Translation trans = Translation.load (i18nOptionsName); |
|---|
| | 38 | this (char[] optsName) |
|---|
| | 39 | { |
|---|
| | 40 | auto p = optsName in Options.subClasses; |
|---|
| | 41 | if (p is null) { |
|---|
| | 42 | logger.error ("OptionList created with invalid options class name."); |
|---|
| | 43 | return; // list is empty, nothing displayed |
|---|
| | 44 | } |
|---|
| | 45 | Options opts = *p; |
|---|
| 34 | 46 | |
|---|
| 35 | | char[][] list = opts.list!(char[])(); |
|---|
| | 47 | Translation trans = Translation.load ("L10n/"~optsName); |
|---|
| | 48 | char[][] list = opts.list; |
|---|
| 36 | 49 | |
|---|
| 37 | | textOpts.length = list.length; |
|---|
| 38 | | foreach (i,s; list) { |
|---|
| | 50 | textOpts.length = list.length + opts.content.length; |
|---|
| | 51 | size_t i; |
|---|
| | 52 | foreach (s; list) { |
|---|
| 39 | 53 | Translation.Entry transled = trans.getStruct (s); |
|---|
| 40 | | textOpts[i] = new ContentOptionText(opts, s, transled.name, transled.desc); |
|---|
| | 54 | textOpts[i] = new OptionContent(opts, s, transled.name, transled.desc); |
|---|
| | 55 | ++i; |
|---|
| 41 | 56 | } |
|---|
| | 57 | foreach (s, v; opts.content) { |
|---|
| | 58 | Translation.Entry transled = trans.getStruct (s); |
|---|
| | 59 | v.name (transled.name, transled.desc); // set Content name & desc. Only needs doing once |
|---|
| | 60 | textOpts[i++] = v; |
|---|
| | 61 | } |
|---|
| | 62 | |
|---|
| 42 | 63 | } |
|---|
| 43 | 64 | |
|---|
| 44 | | ContentOption[] list () { |
|---|
| | 65 | IContent[] list () { |
|---|
| 45 | 66 | return textOpts; |
|---|
| 46 | 67 | } |
|---|
| 47 | 68 | |
|---|
| 48 | | static OptionList trial () { |
|---|
| 49 | | return new OptionList (miscOpts, "L10n/OptionsMisc"); |
|---|
| 50 | | } |
|---|
| 51 | | |
|---|
| 52 | 69 | protected: |
|---|
| 53 | | ContentOption[] textOpts; |
|---|
| | 70 | IContent[] textOpts; |
|---|
| 54 | 71 | } |
|---|
| 55 | 72 | |
|---|
| 56 | 73 | //FIXME - todo.txt |
|---|
| 57 | | class ContentOptionText : ContentOption |
|---|
| | 74 | class OptionContent : IContent |
|---|
| 58 | 75 | { |
|---|
| 59 | 76 | this (Options o, char[] s, char[] name, char[] desc) { |
|---|
| … | … | |
| 66 | 83 | char[] toString (uint i) { |
|---|
| 67 | 84 | if (i == 0) |
|---|
| 68 | | return opts.get!(char[])(symb); |
|---|
| | 85 | return "dummy"; //opts.get!(char[])(symb); |
|---|
| 69 | 86 | else if (i == 1) |
|---|
| 70 | 87 | return name_; |
|---|
| … | … | |
| 78 | 95 | opts.set!(char[])(symb, v); |
|---|
| 79 | 96 | }+/ |
|---|
| 80 | | } |
|---|
| 81 | | |
|---|
| 82 | | abstract class ContentOption : IContent |
|---|
| 83 | | { |
|---|
| 84 | | // Get the symbol name (useful?) |
|---|
| 85 | | /+ |
|---|
| 86 | | /// Get the translated name |
|---|
| 87 | | char[] name () { |
|---|
| 88 | | return name_; |
|---|
| 89 | | } |
|---|
| 90 | | |
|---|
| 91 | | /// Get the description (translated) |
|---|
| 92 | | char[] description () { |
|---|
| 93 | | return desc_; |
|---|
| 94 | | } |
|---|
| 95 | | +/ |
|---|
| 96 | 97 | protected: |
|---|
| 97 | 98 | Options opts; // the set of options within which our option lies |
|---|
| r93 |
r94 |
|
| 24 | 24 | import mde.gui.renderer.IRenderer; |
|---|
| 25 | 25 | import mde.gui.content.Content; |
|---|
| | 26 | |
|---|
| | 27 | debug { |
|---|
| | 28 | import tango.util.log.Log : Log, Logger; |
|---|
| | 29 | private Logger logger; |
|---|
| | 30 | static this () { |
|---|
| | 31 | logger = Log.getLogger ("mde.gui.widget.TextWidget"); |
|---|
| | 32 | } |
|---|
| | 33 | } |
|---|
| 26 | 34 | |
|---|
| 27 | 35 | /// Basic text widget |
|---|
| … | … | |
| 54 | 62 | { |
|---|
| 55 | 63 | this (IWidgetManager mgr, widgetID id, WidgetData data, IContent c) { |
|---|
| | 64 | debug assert (c, "content is null (code error)"); |
|---|
| 56 | 65 | WDCheck (data, 3, 0); |
|---|
| 57 | 66 | content = c; |
|---|
| r93 |
r94 |
|
| 101 | 101 | debug scope (failure) |
|---|
| 102 | 102 | logger.warn ("TrialContentLayoutWidget: failure"); |
|---|
| 103 | | WDCheck (data, 2, 1); |
|---|
| 104 | | |
|---|
| 105 | | OptionList optsList = OptionList.trial(); |
|---|
| 106 | | rows = optsList.list.length; |
|---|
| | 103 | WDCheck (data, 2, 2); |
|---|
| | 104 | |
|---|
| | 105 | OptionList optsList = new OptionList(data.strings[1]); |
|---|
| 107 | 106 | cols = 1; |
|---|
| 108 | | |
|---|
| 109 | | // Get all sub-widgets |
|---|
| 110 | | subWidgets.length = rows*cols; |
|---|
| 111 | | foreach (i, c; optsList.list) { |
|---|
| 112 | | subWidgets[i] = mgr.makeWidget (data.strings[0], c); |
|---|
| | 107 | if ((rows = optsList.list.length) > 0) { |
|---|
| | 108 | // Get all sub-widgets |
|---|
| | 109 | subWidgets.length = rows*cols; |
|---|
| | 110 | foreach (i, c; optsList.list) { |
|---|
| | 111 | subWidgets[i] = mgr.makeWidget (data.strings[0], c); |
|---|
| | 112 | } |
|---|
| | 113 | } else { |
|---|
| | 114 | rows = 1; |
|---|
| | 115 | subWidgets = [mgr.makeWidget (data.strings[0], new TextContent (data.strings[1], "Invalid Options section"))]; |
|---|
| 113 | 116 | } |
|---|
| 114 | 117 | super (mgr, id, data); |
|---|
| … | … | |
| 177 | 180 | * As such, this must be the first function called after this(). */ |
|---|
| 178 | 181 | void finalize () { |
|---|
| 179 | | logger.trace ("initWidths.length: {}", initWidths.length); |
|---|
| 180 | 182 | if (initWidths.length == cols + rows) { |
|---|
| 181 | 183 | col.setWidths (initWidths[0..cols]); |
|---|
| r89 |
r94 |
|
| 31 | 31 | import mde.exception; |
|---|
| 32 | 32 | |
|---|
| | 33 | public import mde.gui.content.Content; |
|---|
| | 34 | |
|---|
| 33 | 35 | import mde.file.mergetag.Reader; |
|---|
| 34 | 36 | import mde.file.mergetag.Writer; |
|---|
| … | … | |
| 53 | 55 | * files during pre-init (init0 stage). Do not write changes directly to the subclasses or they will |
|---|
| 54 | 56 | * not be saved; instead use set(), for example, miscOpts.set!(char[])("L10n","en-GB"). Use an |
|---|
| 55 | | * example like OptionsMisc as a template for creating a new Options sub-class. |
|---|
| | 57 | * example like MiscOptions as a template for creating a new Options sub-class. |
|---|
| 56 | 58 | * |
|---|
| 57 | 59 | * Optionally, overload the validate() function. This is called after loading, allowing conditions |
|---|
| … | … | |
| 80 | 82 | // the supported types simply by changing this list now (untested). |
|---|
| 81 | 83 | template store(A...) { alias A store; } |
|---|
| 82 | | alias store!(bool, int, double, char[]) TYPES; |
|---|
| | 84 | alias store!(int, double, char[]) TYPES;//FIXME removed bool |
|---|
| 83 | 85 | //BEGIN Templates: internal |
|---|
| 84 | 86 | private { |
|---|
| … | … | |
| 93 | 95 | |
|---|
| 94 | 96 | // Pointer lists |
|---|
| 95 | | template PLists(T, A...) { |
|---|
| 96 | | static if (A.length) { |
|---|
| 97 | | const char[] PLists = T.stringof~"*[ID] opts"~TName!(T)~";\n" ~ PLists!(A); |
|---|
| 98 | | } else |
|---|
| 99 | | const char[] PLists = T.stringof~"*[ID] opts"~TName!(T)~";\n"; |
|---|
| | 97 | template PLists(A...) { |
|---|
| | 98 | static if (A.length) { |
|---|
| | 99 | static if (is (T == bool)) { |
|---|
| | 100 | const char[] PLists = PLists!(A[1..$]); |
|---|
| | 101 | } else |
|---|
| | 102 | const char[] PLists = A[0].stringof~"*[ID] opts"~TName!(A[0])~";\n" ~ PLists!(A[1..$]); |
|---|
| | 103 | } else |
|---|
| | 104 | const char[] PLists = ""; |
|---|
| 100 | 105 | } |
|---|
| 101 | 106 | |
|---|
| … | … | |
| 113 | 118 | // For addTag |
|---|
| 114 | 119 | template addTagMixin(T, A...) { |
|---|
| 115 | | const char[] ifBlock = `if (tp == "`~T.stringof~`") { |
|---|
| 116 | | `~T.stringof~`** p = id in opts`~TName!(T)~`; |
|---|
| 117 | | if (p !is null) **p = parseTo!(`~T.stringof~`) (dt); |
|---|
| 118 | | }`; |
|---|
| | 120 | static if (is(T == bool)) { |
|---|
| | 121 | const char[] ifBlock = `if (tp == "`~T.stringof~`") { |
|---|
| | 122 | auto p = id in opts; |
|---|
| | 123 | if (p) { |
|---|
| | 124 | auto q = cast(BoolContent) (*p); |
|---|
| | 125 | if (q) q = parseTo!(`~T.stringof~`) (dt); |
|---|
| | 126 | } |
|---|
| | 127 | }`; |
|---|
| | 128 | } else |
|---|
| | 129 | const char[] ifBlock = `if (tp == "`~T.stringof~`") { |
|---|
| | 130 | `~T.stringof~`** p = id in opts`~TName!(T)~`; |
|---|
| | 131 | if (p !is null) **p = parseTo!(`~T.stringof~`) (dt); |
|---|
| | 132 | }`; |
|---|
| 119 | 133 | static if (A.length) |
|---|
| 120 | 134 | const char[] addTagMixin = ifBlock~` else `~addTagMixin!(A).addTagMixin; |
|---|
| … | … | |
| 130 | 144 | } else |
|---|
| 131 | 145 | const char[] writeAllMixin = ``; |
|---|
| | 146 | } |
|---|
| | 147 | |
|---|
| | 148 | // For list |
|---|
| | 149 | template listMixin(A...) { |
|---|
| | 150 | static if (A.length) { |
|---|
| | 151 | const char[] listMixin = `ret ~= opts`~TName!(A[0])~`.keys;` ~ listMixin!(A[1..$]); |
|---|
| | 152 | } else |
|---|
| | 153 | const char[] listMixin = ``; |
|---|
| 132 | 154 | } |
|---|
| 133 | 155 | } |
|---|
| … | … | |
| 145 | 167 | assert (((cast(ID) i) in subClasses) is null); // Don't allow a silent replacement |
|---|
| 146 | 168 | } body { |
|---|
| | 169 | c.secName = i; |
|---|
| 147 | 170 | subClasses[cast(ID) i] = c; |
|---|
| 148 | 171 | } |
|---|
| … | … | |
| 234 | 257 | * must be changed in two separate places. */ |
|---|
| 235 | 258 | void set(T) (char[] symbol, T val) { |
|---|
| 236 | | static assert (TIsIn!(T,TYPES), "Options does not support type "~T.stringof); |
|---|
| | 259 | static assert (TIsIn!(T,TYPES) && !is(T == bool), "Options does not support type "~T.stringof); |
|---|
| 237 | 260 | |
|---|
| 238 | 261 | changed = true; // something got set (don't bother checking this isn't what it already was) |
|---|
| … | … | |
| 246 | 269 | } |
|---|
| 247 | 270 | } |
|---|
| 248 | | |
|---|
| | 271 | /+ |
|---|
| 249 | 272 | /** Get option symbol of an Options sub-class. |
|---|
| 250 | 273 | * |
|---|
| … | … | |
| 261 | 284 | logger.error ("Options.get: invalid symbol"); |
|---|
| 262 | 285 | } |
|---|
| 263 | | } |
|---|
| | 286 | }+/ |
|---|
| 264 | 287 | |
|---|
| 265 | 288 | /** List the names of all options of a specific type. */ |
|---|
| 266 | | char[][] list(T) () { |
|---|
| 267 | | static assert (TIsIn!(T,TYPES), "Options does not support type "~T.stringof); |
|---|
| 268 | | |
|---|
| 269 | | mixin (`alias opts`~TName!(T)~` optsVars;`); |
|---|
| 270 | | |
|---|
| 271 | | return optsVars.keys; |
|---|
| | 289 | char[][] list () { |
|---|
| | 290 | char[][] ret; |
|---|
| | 291 | mixin (listMixin!(TYPES)); |
|---|
| | 292 | return ret; |
|---|
| | 293 | } |
|---|
| | 294 | |
|---|
| | 295 | /// Get all Options stored with a ValueContent. |
|---|
| | 296 | ValueContent[char[]] content() { |
|---|
| | 297 | return opts; |
|---|
| 272 | 298 | } |
|---|
| 273 | 299 | |
|---|
| … | … | |
| 276 | 302 | |
|---|
| 277 | 303 | protected { |
|---|
| | 304 | char[] secName; // name of this option setting; set null after translation is loaded |
|---|
| 278 | 305 | OptionChanges optionChanges; // all changes to options (for saving) |
|---|
| 279 | 306 | |
|---|
| 280 | 307 | // The "pointer lists", e.g. char[]*[ID] optscharA; |
|---|
| 281 | | mixin (PLists!(TYPES)); |
|---|
| | 308 | mixin (PLists!(TYPES)); //FIXME adds unused optsbool |
|---|
| | 309 | ValueContent[char[]] opts; // generic list of option values |
|---|
| 282 | 310 | } |
|---|
| 283 | 311 | |
|---|
| … | … | |
| 296 | 324 | //BEGIN Templates: impl & optionsThis |
|---|
| 297 | 325 | private { |
|---|
| | 326 | // Replace, e.g., bool, with BoolContent |
|---|
| | 327 | template contentName(A) { |
|---|
| | 328 | static if (is(A == bool)) { |
|---|
| | 329 | const char[] contentName = "BoolContent"; |
|---|
| | 330 | } else static if (is(A == int)) { |
|---|
| | 331 | const char[] contentName = "int";// no IntContent yet |
|---|
| | 332 | } else static if (is(A == double)) { |
|---|
| | 333 | const char[] contentName = "double"; |
|---|
| | 334 | } else static if (is(A == char[])) { |
|---|
| | 335 | const char[] contentName = "char[]"; |
|---|
| | 336 | } else |
|---|
| | 337 | static assert (false, "unsuppurted type: "~ A); |
|---|
| | 338 | } |
|---|
| 298 | 339 | // Return index of first comma, or halts if not found. |
|---|
| 299 | 340 | template cIndex(char[] A) { |
|---|
| … | … | |
| 314 | 355 | const size_t scIndex = 1 + scIndex!(A[1..$]); |
|---|
| 315 | 356 | } |
|---|
| 316 | | // Look for "type symbols;" in A and return symbols as a comma separated list of names |
|---|
| 317 | | // (even if type is encountered more than once). Output may contain spaces and, if |
|---|
| 318 | | // non-empty, will contain a trailing comma. Assumes scIndex always returns less than A.$. |
|---|
| | 357 | /* Look for "type symbols;" in A and return symbols as a comma separated list of names |
|---|
| | 358 | (even if type is encountered more than once). Output may contain spaces and will have a |
|---|
| | 359 | trailing comma unless no match was found in which case an empty string is returned. |
|---|
| | 360 | Assumes scIndex always returns less than A.$ . */ |
|---|
| 319 | 361 | template parseT(char[] type, char[] A) { |
|---|
| 320 | 362 | static if (A.length < type.length + 1) // end of input, no match |
|---|
| … | … | |
| 328 | 370 | const char[] parseT = parseT!(type, A[scIndex!(A)+1 .. $]); |
|---|
| 329 | 371 | } |
|---|
| 330 | | // May have a trailing comma. Assumes cIndex always returns less than A.$. |
|---|
| | 372 | // May have a trailing comma. Assumes cIndex always returns less than A.$ . |
|---|
| 331 | 373 | template aaVars(char[] A) { |
|---|
| 332 | 374 | static if (A.length == 0) |
|---|
| … | … | |
| 337 | 379 | const char[] aaVars = "\""~A[0..cIndex!(A)]~"\"[]:&"~A[0..cIndex!(A)] ~ "," ~ |
|---|
| 338 | 380 | aaVars!(A[cIndex!(A)+1..$]); |
|---|
| | 381 | } |
|---|
| | 382 | // May have a trailing comma. Assumes cIndex always returns less than A.$ . |
|---|
| | 383 | template aaVarsBool(char[] A) {//FIXME |
|---|
| | 384 | static if (A.length == 0) |
|---|
| | 385 | const char[] aaVarsBool = ""; |
|---|
| | 386 | else static if (A[0] == ' ') |
|---|
| | 387 | const char[] aaVarsBool = aaVarsBool!(A[1..$]); |
|---|
| | 388 | else |
|---|
| | 389 | const char[] aaVarsBool = "\""~A[0..cIndex!(A)]~"\"[]:"~A[0..cIndex!(A)] ~ "," ~ |
|---|
| | 390 | aaVarsBool!(A[cIndex!(A)+1..$]); |
|---|
| 339 | 391 | } |
|---|
| 340 | 392 | // strip Trailing Comma |
|---|
| … | … | |
| 354 | 406 | const char[] listOrNull = "["~A~"]"; |
|---|
| 355 | 407 | } |
|---|
| | 408 | // if B is empty return an empty string otherswise return what's below: |
|---|
| | 409 | template catOrNothing(char[] A,char[] B) { |
|---|
| | 410 | static if (B.length) |
|---|
| | 411 | const char[] catOrNothing = A~` `~sTC!(B)~";\n"; |
|---|
| | 412 | else |
|---|
| | 413 | const char[] catOrNothing = ``; |
|---|
| | 414 | } |
|---|
| | 415 | // foreach decl... |
|---|
| | 416 | template createBCs(char[] A) { |
|---|
| | 417 | static if (A.length == 0) |
|---|
| | 418 | const char[] createBCs = ""; |
|---|
| | 419 | else static if (A[0] == ' ') |
|---|
| | 420 | const char[] createBCs = createBCs!(A[1..$]); |
|---|
| | 421 | else |
|---|
| | 422 | const char[] createBCs = A[0..cIndex!(A)]~ " = new BoolContent (false);\n"~ |
|---|
| | 423 | createBCs!(A[cIndex!(A)+1..$]); |
|---|
| | 424 | } |
|---|
| 356 | 425 | // for recursing on TYPES |
|---|
| 357 | 426 | template optionsThisInternal(char[] A, B...) { |
|---|
| 358 | 427 | static if (B.length) { |
|---|
| | 428 | static if (is(B[0] == bool)) {//FIXME |
|---|
| | 429 | const char[] optionsThisInternal = createBCs!(parseT!(B[0].stringof,A))~ |
|---|
| | 430 | `opts = `~listOrNull!(sTC!(aaVarsBool!(parseT!(B[0].stringof,A))))~";\n" ~ |
|---|
| | 431 | optionsThisInternal!(A,B[1..$]); |
|---|
| | 432 | } else |
|---|
| 359 | 433 | const char[] optionsThisInternal = `opts`~TName!(B[0])~` = `~listOrNull!(sTC!(aaVars!(parseT!(B[0].stringof,A))))~";\n" ~ optionsThisInternal!(A,B[1..$]); |
|---|
| 360 | 434 | } else |
|---|
| 361 | 435 | const char[] optionsThisInternal = ``; |
|---|
| 362 | 436 | } |
|---|
| | 437 | template declValsInternal(char[] A, B...) { |
|---|
| | 438 | static if (B.length) { |
|---|
| | 439 | const char[] declValsInternal = catOrNothing!(contentName!(B[0]),parseT!(B[0].stringof,A)) ~ declValsInternal!(A,B[1..$]); |
|---|
| | 440 | } else |
|---|
| | 441 | const char[] declValsInternal = ``; |
|---|
| | 442 | } |
|---|
| 363 | 443 | } protected { |
|---|
| | 444 | /** Declares the values. |
|---|
| | 445 | * |
|---|
| | 446 | * Basic types are replaced with a ValueContent class to keep the option synchronized and |
|---|
| | 447 | * generalize use. */ |
|---|
| | 448 | template declVals(char[] A) { |
|---|
| | 449 | const char[] declVals = declValsInternal!(A, TYPES,bool); |
|---|
| | 450 | } |
|---|
| 364 | 451 | /** Produces the implementation code to go in the constuctor. */ |
|---|
| 365 | 452 | template optionsThis(char[] A) { |
|---|
| 366 | 453 | const char[] optionsThis = |
|---|
| 367 | 454 | "optionChanges = new OptionChanges;\n" ~ |
|---|
| 368 | | optionsThisInternal!(A,TYPES); |
|---|
| | 455 | optionsThisInternal!(A,TYPES,bool); |
|---|
| 369 | 456 | } |
|---|
| 370 | 457 | /+ Needs too many custom parameters to be worth it? Plus makes class less readable. |
|---|
| … | … | |
| 382 | 469 | * In case this() needs to be customized, mixin(impl!(A)) is equivalent to: |
|---|
| 383 | 470 | * --- |
|---|
| 384 | | * mixin (A~` |
|---|
| | 471 | * mixin (declVals!(A)~` |
|---|
| 385 | 472 | this () { |
|---|
| 386 | 473 | `~optionsThis!(A)~` |
|---|
| … | … | |
| 396 | 483 | * class, but doing so would rather decrease readability of any implementation. */ |
|---|
| 397 | 484 | template impl(char[] A /+, char[] symb+/) { |
|---|
| 398 | | const char[] impl = A~"\nthis(){\n"~optionsThis!(A)~"}"; |
|---|
| | 485 | const char[] impl = declVals!(A)~"\nthis(){\n"~optionsThis!(A)~"}"; |
|---|
| 399 | 486 | // ~"\nstatic this(){\n"~optClassAdd!(symb)~"}" |
|---|
| 400 | 487 | } |
|---|
| … | … | |
| 467 | 554 | |
|---|
| 468 | 555 | /** A home for all miscellaneous options, at least for now. */ |
|---|
| 469 | | OptionsMisc miscOpts; |
|---|
| 470 | | class OptionsMisc : Options { |
|---|
| 471 | | mixin (impl!("bool exitImmediately; int maxThreads, logOptions; double pollInterval; char[] L10n, a,b,c,g,z;")); |
|---|
| | 556 | MiscOptions miscOpts; |
|---|
| | 557 | class MiscOptions : Options { |
|---|
| | 558 | const A = "bool exitImmediately; int maxThreads, logOptions; double pollInterval; char[] L10n;"; |
|---|
| | 559 | //pragma (msg, impl!(A)); |
|---|
| | 560 | mixin (impl!(A)); |
|---|
| 472 | 561 | |
|---|
| 473 | 562 | void validate() { |
|---|
| … | … | |
| 482 | 571 | |
|---|
| 483 | 572 | static this() { |
|---|
| 484 | | miscOpts = new OptionsMisc; |
|---|
| 485 | | Options.addOptionsClass (miscOpts, "misc"); |
|---|
| | 573 | miscOpts = new MiscOptions; |
|---|
| | 574 | Options.addOptionsClass (miscOpts, "MiscOptions"); |
|---|
| 486 | 575 | } |
|---|
| 487 | 576 | } |
|---|
| r91 |
r94 |
|
| 183 | 183 | |
|---|
| 184 | 184 | // a debugging option: |
|---|
| 185 | | imde.run = !args.contains("q") && !miscOpts.exitImmediately; |
|---|
| | 185 | imde.run = !args.contains("q") && !miscOpts.exitImmediately(); |
|---|
| 186 | 186 | debug logger.trace ("Init: applied pre-init options"); |
|---|
| 187 | 187 | |
|---|
| r91 |
r94 |
|
| 49 | 49 | |
|---|
| 50 | 50 | /** All video options. */ |
|---|
| 51 | | class OptionsVideo : Options { |
|---|
| | 51 | class VideoOptions : Options { |
|---|
| 52 | 52 | mixin (impl!("bool fullscreen,hardware,resizable,noFrame; int screenW,screenH,windowW,windowH;")); |
|---|
| 53 | 53 | } |
|---|
| … | … | |
| 76 | 76 | // Window creation flags and size |
|---|
| 77 | 77 | flags = SDL_OPENGL; |
|---|
| 78 | | if (vidOpts.hardware) flags |= SDL_HWSURFACE | SDL_DOUBLEBUF; |
|---|
| | 78 | if (videoOpts.hardware()) flags |= SDL_HWSURFACE | SDL_DOUBLEBUF; |
|---|
| 79 | 79 | else flags |= SDL_SWSURFACE; |
|---|
| 80 | 80 | int w, h; |
|---|
| 81 | | if (vidOpts.fullscreen) { |
|---|
| | 81 | if (videoOpts.fullscreen()) { |
|---|
| 82 | 82 | flags |= SDL_FULLSCREEN; |
|---|
| 83 | | w = vidOpts.screenW; |
|---|
| 84 | | h = vidOpts.screenH; |
|---|
| | 83 | w = videoOpts.screenW; |
|---|
| | 84 | h = videoOpts.screenH; |
|---|
| 85 | 85 | } |
|---|
| 86 | 86 | else { |
|---|
| 87 | | if (vidOpts.resizable) flags |= SDL_RESIZABLE; |
|---|
| 88 | | if (vidOpts.noFrame) flags |= SDL_NOFRAME; |
|---|
| 89 | | w = vidOpts.windowW; |
|---|
| 90 | | h = vidOpts.windowH; |
|---|
| | 87 | if (videoOpts.resizable()) flags |= SDL_RESIZABLE; |
|---|
| | 88 | if (videoOpts.noFrame()) flags |= SDL_NOFRAME; |
|---|
| | 89 | w = videoOpts.windowW; |
|---|
| | 90 | h = videoOpts.windowH; |
|---|
| 91 | 91 | } |
|---|
| 92 | 92 | |
|---|
| … | … | |
| 156 | 156 | void resizeEvent (int w, int h) { |
|---|
| 157 | 157 | // Save new size to config |
|---|
| 158 | | if (vidOpts.fullscreen) { // probably resizeEvent only called when not fullscreen |
|---|
| 159 | | vidOpts.set!(int) ("screenW", w); |
|---|
| 160 | | vidOpts.set!(int) ("screenH", h); |
|---|
| | 158 | if (videoOpts.fullscreen()) { // probably resizeEvent only called when not fullscreen |
|---|
| | 159 | videoOpts.set!(int) ("screenW", w); |
|---|
| | 160 | videoOpts.set!(int) ("screenH", h); |
|---|
| 161 | 161 | } else { |
|---|
| 162 | | vidOpts.set!(int) ("windowW", w); |
|---|
| 163 | | vidOpts.set!(int) ("windowH", h); |
|---|
| | 162 | videoOpts.set!(int) ("windowW", w); |
|---|
| | 163 | videoOpts.set!(int) ("windowH", h); |
|---|
| 164 | 164 | } |
|---|
| 165 | 165 | |
|---|
| … | … | |
| 263 | 263 | logger = Log.getLogger ("mde.setup.Screen"); |
|---|
| 264 | 264 | |
|---|
| 265 | | vidOpts = new OptionsVideo; |
|---|
| 266 | | Options.addOptionsClass (vidOpts, "video"); |
|---|
| | 265 | videoOpts = new VideoOptions; |
|---|
| | 266 | Options.addOptionsClass (videoOpts, "VideoOptions"); |
|---|
| 267 | 267 | } |
|---|
| 268 | 268 | |
|---|
| … | … | |
| 272 | 272 | IDrawable[] drawables; |
|---|
| 273 | 273 | Logger logger; |
|---|
| 274 | | OptionsVideo vidOpts; |
|---|
| | 274 | VideoOptions videoOpts; |
|---|
| 275 | 275 | } |
|---|