| 1 |
//--------------------------------------------------------------------- |
|---|
| 2 |
/* |
|---|
| 3 |
Copyright: |
|---|
| 4 |
|
|---|
| 5 |
luigi/theme.d -- theme support for 'luigi' user interface library. |
|---|
| 6 |
|
|---|
| 7 |
Copyright (C) 2006 William V. Baxter III |
|---|
| 8 |
|
|---|
| 9 |
This software is provided 'as-is', without any express or implied |
|---|
| 10 |
warranty. In no event will the authors be held liable for any |
|---|
| 11 |
damages arising from the use of this software. |
|---|
| 12 |
|
|---|
| 13 |
Permission is granted to anyone to use this software for any |
|---|
| 14 |
purpose, including commercial applications, and to alter it and |
|---|
| 15 |
redistribute it freely, subject to the following restrictions: |
|---|
| 16 |
|
|---|
| 17 |
1. The origin of this software must not be misrepresented; you must |
|---|
| 18 |
not claim that you wrote the original software. If you use this |
|---|
| 19 |
software in a product, an acknowledgment in the product |
|---|
| 20 |
documentation would be appreciated but is not required. |
|---|
| 21 |
|
|---|
| 22 |
2. Altered source versions must be plainly marked as such, and must |
|---|
| 23 |
not be misrepresented as being the original software. |
|---|
| 24 |
3. This notice may not be removed or altered from any source distribution. |
|---|
| 25 |
|
|---|
| 26 |
William Baxter wbaxter@gmail.com |
|---|
| 27 |
*/ |
|---|
| 28 |
module luigi.theme; |
|---|
| 29 |
|
|---|
| 30 |
|
|---|
| 31 |
import luigi.base; |
|---|
| 32 |
import luigi.opengl; |
|---|
| 33 |
import luigi.gldraw; |
|---|
| 34 |
import luigi.font; |
|---|
| 35 |
import luigi.event; |
|---|
| 36 |
|
|---|
| 37 |
import luigi.gui; |
|---|
| 38 |
//import luigi.widgets.widget; |
|---|
| 39 |
|
|---|
| 40 |
//-------------------------------------------------------------------------- |
|---|
| 41 |
/** An abstract interface for themes used by Luigi */ |
|---|
| 42 |
interface Theme |
|---|
| 43 |
{ |
|---|
| 44 |
/** Called before and after refreshing the gui by Overlay. |
|---|
| 45 |
* This is where all common 2D GL state can be set up and restored by |
|---|
| 46 |
* the theme. |
|---|
| 47 |
*/ |
|---|
| 48 |
void begin_drawing(Rect r); |
|---|
| 49 |
void end_drawing(); |
|---|
| 50 |
|
|---|
| 51 |
/** Draws the given item */ |
|---|
| 52 |
void draw(Widget item); |
|---|
| 53 |
|
|---|
| 54 |
/** Sizes used for layout */ |
|---|
| 55 |
Size minimum_size(Widget item, Size bounds); |
|---|
| 56 |
Size preferred_size(Widget item, Size bounds); |
|---|
| 57 |
|
|---|
| 58 |
/** Event handling */ |
|---|
| 59 |
void on_key(Widget item, KeyEvent ev); |
|---|
| 60 |
void on_mouse_button(Widget item, MouseButtonEvent ev); |
|---|
| 61 |
void on_mouse_move(Widget item, MouseMoveEvent ev); |
|---|
| 62 |
void on_mouse_wheel(Widget item, MouseWheelEvent ev); |
|---|
| 63 |
void on_focus(Widget item, FocusEvent ev); |
|---|
| 64 |
|
|---|
| 65 |
// need sizes |
|---|
| 66 |
// need states? naw can get it from control |
|---|
| 67 |
} |
|---|
| 68 |
|
|---|
| 69 |
class AbstractTheme : Theme |
|---|
| 70 |
{ |
|---|
| 71 |
abstract override void draw(Widget item); |
|---|
| 72 |
abstract override Size minimum_size(Widget item, Size bounds); |
|---|
| 73 |
abstract override Size preferred_size(Widget item, Size bounds); |
|---|
| 74 |
|
|---|
| 75 |
void begin_drawing(Rect r) {} |
|---|
| 76 |
void end_drawing() {} |
|---|
| 77 |
|
|---|
| 78 |
//============================================================================ |
|---|
| 79 |
// Event functions |
|---|
| 80 |
override void on_key(Widget item, KeyEvent ev) { |
|---|
| 81 |
// this really isn't that theme specific... |
|---|
| 82 |
item.on_key(ev); |
|---|
| 83 |
} |
|---|
| 84 |
override void on_mouse_button(Widget item, MouseButtonEvent ev) { |
|---|
| 85 |
item.on_mouse_button(ev); |
|---|
| 86 |
} |
|---|
| 87 |
override void on_mouse_move(Widget item, MouseMoveEvent ev) { |
|---|
| 88 |
item.on_mouse_move(ev); |
|---|
| 89 |
} |
|---|
| 90 |
override void on_mouse_wheel(Widget item, MouseWheelEvent ev) { |
|---|
| 91 |
item.on_mouse_wheel(ev); |
|---|
| 92 |
} |
|---|
| 93 |
override void on_focus(Widget item, FocusEvent ev) { |
|---|
| 94 |
item.on_focus(ev); |
|---|
| 95 |
} |
|---|
| 96 |
} |
|---|
| 97 |
|
|---|
| 98 |
/** This template/mixin provides helper functions used by themes to |
|---|
| 99 |
* manage per-class instance information. |
|---|
| 100 |
*/ |
|---|
| 101 |
template WidgetClassInfoDict(FuncT) |
|---|
| 102 |
{ |
|---|
| 103 |
/** Lookup Widget class-specific data in a table. |
|---|
| 104 |
* |
|---|
| 105 |
* This is like an ordinary AA lookup keyed on ClassInfo, |
|---|
| 106 |
* but if a key is missing it recursively tries all the ClassInfo |
|---|
| 107 |
* objects for the base classes as well. |
|---|
| 108 |
* If it finds a match, that match is copied to |
|---|
| 109 |
* table[item.classinfo] for faster future lookups. |
|---|
| 110 |
*/ |
|---|
| 111 |
FuncT lookup(inout FuncT[ClassInfo] table, Widget item) { |
|---|
| 112 |
// Search for func in the table, going up class hierarchy |
|---|
| 113 |
ClassInfo start = item.classinfo; |
|---|
| 114 |
if (start in table) return table[start]; |
|---|
| 115 |
ClassInfo stop = Widget.classinfo; |
|---|
| 116 |
for(auto cinfo=start; cinfo!=stop; cinfo=cinfo.base) |
|---|
| 117 |
{ |
|---|
| 118 |
if (cinfo in table) { |
|---|
| 119 |
FuncT f = table[cinfo]; |
|---|
| 120 |
table[start] = f; // cache result for later |
|---|
| 121 |
return f; |
|---|
| 122 |
} |
|---|
| 123 |
} |
|---|
| 124 |
return null; |
|---|
| 125 |
} |
|---|
| 126 |
/** Same as lookup(table,item) but use the mixin's builtin m_table */ |
|---|
| 127 |
FuncT lookup(Widget item) { |
|---|
| 128 |
return lookup(m_table,item); |
|---|
| 129 |
} |
|---|
| 130 |
|
|---|
| 131 |
/** Add an element to the ClassInfo-keyed table */ |
|---|
| 132 |
void add(inout FuncT[ClassInfo] table, ClassInfo c, FuncT f) { |
|---|
| 133 |
table[c] = f; |
|---|
| 134 |
} |
|---|
| 135 |
/** Same as add(table,c,f) but use the mixin's builtin m_table */ |
|---|
| 136 |
void add(ClassInfo c, FuncT f) { |
|---|
| 137 |
add(m_table, c, f); |
|---|
| 138 |
} |
|---|
| 139 |
FuncT[ClassInfo] m_table; |
|---|
| 140 |
} |
|---|
| 141 |
|
|---|
| 142 |
|
|---|
| 143 |
class ThemeBase : AbstractTheme |
|---|
| 144 |
{ |
|---|
| 145 |
alias Size delegate(Widget item, Size bounds) SizeFunc; |
|---|
| 146 |
alias void delegate(Widget item) DrawFunc; |
|---|
| 147 |
|
|---|
| 148 |
alias void delegate(Widget item, MouseButtonEvent ev) ButtonFunc; |
|---|
| 149 |
alias void delegate(Widget item, MouseMoveEvent ev) MoveFunc; |
|---|
| 150 |
alias void delegate(Widget item, KeyEvent ev) KeyFunc; |
|---|
| 151 |
|
|---|
| 152 |
void addHandlers(alias Class)(DrawFunc draw, SizeFunc best_sz, SizeFunc min_sz=null ) |
|---|
| 153 |
{ |
|---|
| 154 |
m_drawFuncs.add(Class.classinfo, draw); |
|---|
| 155 |
m_bestsizeFuncs.add(Class.classinfo, best_sz); |
|---|
| 156 |
m_minsizeFuncs.add(Class.classinfo, (min_sz is null)?best_sz : min_sz); |
|---|
| 157 |
} |
|---|
| 158 |
void addEventHandlers(alias Class)(ButtonFunc btn, MoveFunc move=null, KeyFunc key=null ) |
|---|
| 159 |
{ |
|---|
| 160 |
m_btnFuncs.add(Class.classinfo, btn); |
|---|
| 161 |
if (move !is null) |
|---|
| 162 |
m_moveFuncs.add(Class.classinfo, move); |
|---|
| 163 |
if (key !is null) |
|---|
| 164 |
m_moveFuncs.add(Class.classinfo, move); |
|---|
| 165 |
} |
|---|
| 166 |
|
|---|
| 167 |
protected: |
|---|
| 168 |
mixin WidgetClassInfoDict!(DrawFunc) m_drawFuncs; |
|---|
| 169 |
mixin WidgetClassInfoDict!(SizeFunc) m_minsizeFuncs; |
|---|
| 170 |
mixin WidgetClassInfoDict!(SizeFunc) m_bestsizeFuncs; |
|---|
| 171 |
mixin WidgetClassInfoDict!(ButtonFunc) m_btnFuncs; |
|---|
| 172 |
mixin WidgetClassInfoDict!(MoveFunc) m_moveFuncs; |
|---|
| 173 |
mixin WidgetClassInfoDict!(KeyFunc) m_keyFuncs; |
|---|
| 174 |
|
|---|
| 175 |
public: |
|---|
| 176 |
|
|---|
| 177 |
override void draw(Widget item) { |
|---|
| 178 |
DrawFunc f = m_drawFuncs.lookup(item); |
|---|
| 179 |
if (f) f(item); |
|---|
| 180 |
} |
|---|
| 181 |
|
|---|
| 182 |
override Size minimum_size(Widget item, Size bounds) { |
|---|
| 183 |
SizeFunc f = m_minsizeFuncs.lookup(item); |
|---|
| 184 |
if (f) return f(item,bounds); |
|---|
| 185 |
return Size(0,0); |
|---|
| 186 |
} |
|---|
| 187 |
|
|---|
| 188 |
override Size preferred_size(Widget item, Size bounds) { |
|---|
| 189 |
SizeFunc f = m_bestsizeFuncs.lookup(item); |
|---|
| 190 |
if (f) { |
|---|
| 191 |
return f(item,bounds); |
|---|
| 192 |
} |
|---|
| 193 |
return Size(50,22); |
|---|
| 194 |
} |
|---|
| 195 |
|
|---|
| 196 |
//============================================================================ |
|---|
| 197 |
// Event functions |
|---|
| 198 |
override void on_key(Widget item, KeyEvent ev) |
|---|
| 199 |
{ |
|---|
| 200 |
KeyFunc f = m_keyFuncs.lookup(item); |
|---|
| 201 |
if (f) f(item, ev); |
|---|
| 202 |
if (!f || ev.alive) { item.on_key(ev); } |
|---|
| 203 |
} |
|---|
| 204 |
override void on_mouse_button(Widget item, MouseButtonEvent ev) |
|---|
| 205 |
{ |
|---|
| 206 |
ButtonFunc f = m_btnFuncs.lookup(item); |
|---|
| 207 |
if (f) f(item, ev); |
|---|
| 208 |
if (!f || ev.alive) { item.on_mouse_button(ev); } |
|---|
| 209 |
} |
|---|
| 210 |
override void on_mouse_move(Widget item, MouseMoveEvent ev) |
|---|
| 211 |
{ |
|---|
| 212 |
MoveFunc f = m_moveFuncs.lookup(item); |
|---|
| 213 |
if (f) |
|---|
| 214 |
f(item, ev); |
|---|
| 215 |
if (!f || ev.alive) { item.on_mouse_move(ev); } |
|---|
| 216 |
} |
|---|
| 217 |
} |
|---|