theme.monkey2 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423
  1. Namespace mojo.app
  2. Class Theme
  3. Property Path:String()
  4. Return _themePath
  5. End
  6. Property DefaultStyle:Style()
  7. Return _defaultStyle
  8. End
  9. #rem monkeydoc Gets a color from the theme.
  10. If no color named `name` is found, [[Color.Grey]] is returned.
  11. #end
  12. Method GetColor:Color( name:String )
  13. If _colors.Contains( name ) Return _colors[name]
  14. Return Color.Grey
  15. End
  16. #rem monkeydoc Gets a font from the theme.
  17. If no font named `name` is found, [[App.DefaultFont]] is returned.
  18. #end
  19. Method GetFont:Font( name:String )
  20. Local font:=_fonts[name]
  21. If font Return font
  22. Return App.DefaultFont
  23. End
  24. #rem monkeydoc Gets a style from the theme.
  25. If no style named `name` is found, the [[DefaultStyle]] for the theme is returned.
  26. #end
  27. Method GetStyle:Style( name:String )
  28. Local style:=_styles[name]
  29. If style Return style
  30. Return _defaultStyle
  31. End
  32. #rem monkeydoc Loads a font from a file.
  33. If `file` is an absolute path, this method is effectively the same as calling [[Font.Load]].
  34. If `file` is not an absolute path, the font is searched for in the following locations:
  35. * The theme's directory.
  36. * The asset::fonts/ directory.
  37. #end
  38. Method LoadFont:Font( file:String,size:Int )
  39. size*=_themeScale
  40. Local font:Font
  41. If ExtractRootDir( file )
  42. font=Font.Open( file,size )
  43. Else
  44. font=Font.Open( _themeDir+file,size )
  45. If Not font
  46. font=Font.Open( "asset::fonts/"+file,size )
  47. Endif
  48. Endif
  49. If Not font Return App.DefaultFont
  50. Return font
  51. End
  52. #rem monkeydoc Loads an array of icon images from a file.
  53. Loads an array of icons from an image file.
  54. The icons should be square, and laid out in a horizontal 'strip' within the image file.
  55. The width and height of each image is taken from the height of the image file.
  56. The number of icons loaded is the width of the image file divided by its height.
  57. If `file` is not an absolute path, the file is searched for in the following locations:
  58. * The theme's directory.
  59. * The asset::images/ directory.
  60. #end
  61. Method LoadIcons:Image[]( file:String )
  62. Local atlas:Image
  63. If ExtractRootDir( file )
  64. atlas=Image.Load( file )
  65. Else
  66. atlas=Image.Load( _themeDir+file )
  67. If Not atlas
  68. atlas=Image.Load( "asset::images/"+file )
  69. Endif
  70. Endif
  71. If Not atlas Return Null
  72. atlas.Scale=New Vec2f( _themeScale,_themeScale )
  73. Local size:=atlas.Height
  74. Local n:=atlas.Width/size
  75. Local icons:=New Image[n]
  76. For Local i:=0 Until n
  77. icons[i]=New Image( atlas,New Recti( i*size,0,i*size+size,atlas.Height ) )
  78. Next
  79. Return icons
  80. End
  81. #rem monkeydoc Loads a skin from a file.
  82. If `file` is an absolute path, this method is effectively the same as calling [[Skin.Load]].
  83. If `file` is not an absolute path, the skin is searched for in the following locations:
  84. * The theme directory.
  85. * The asset::images/ directory.
  86. #end
  87. Method LoadSkin:Skin( file:String )
  88. Local skin:Skin
  89. If ExtractRootDir( file )
  90. skin=Skin.Load( file )
  91. Else
  92. skin=Skin.Load( _themeDir+file )
  93. If Not skin skin=Skin.Load( "asset::images/"+file )
  94. Endif
  95. If Not skin Return Null
  96. skin.Image.Scale=New Vec2f( _themeScale,_themeScale )
  97. Return skin
  98. End
  99. #rem monkeydoc Loads a theme from a file.
  100. #end
  101. Function Load:Theme( path:String )
  102. Local jobj:=JsonObject.Load( path )
  103. If Not jobj
  104. Print "Failed to load theme:"+path
  105. return New Theme
  106. Endif
  107. Return New Theme( path,jobj )
  108. End
  109. Private
  110. Const _jdefault:=New JsonString( "default" )
  111. #if __TARGET__="android"
  112. Field _themeScale:Int=2
  113. #else
  114. Field _themeScale:Int=1
  115. #endif
  116. Field _themePath:String
  117. Field _themeDir:String
  118. Field _defaultStyle:Style
  119. Field _colors:=New StringMap<Color>
  120. Field _fonts:=New StringMap<Font>
  121. Field _styles:=New StringMap<Style>
  122. Field _jcolors:StringMap<JsonValue>
  123. Field _jfonts:StringMap<JsonValue>
  124. Field _jstyles:StringMap<JsonValue>
  125. Method New()
  126. _themePath=""
  127. _themeDir=""
  128. _defaultStyle=New Style
  129. _defaultStyle.Font=App.DefaultFont
  130. End
  131. Method New( path:String,jobj:JsonObject )
  132. Self.New()
  133. _themePath=path
  134. _themeDir=ExtractDir( _themePath )
  135. _jcolors=jobj["colors"].ToObject()
  136. _jfonts=jobj["fonts"].ToObject()
  137. _jstyles=jobj["styles"].ToObject()
  138. _defaultStyle=LoadStyle( _jdefault )
  139. For Local it:=Eachin _jcolors
  140. LoadColor( New JsonString( it.Key ) )
  141. Next
  142. For Local it:=Eachin _jfonts
  143. LoadFont( New JsonString( it.Key ) )
  144. Next
  145. For Local it:=Eachin _jstyles
  146. LoadStyle( New JsonString( it.Key ) )
  147. Next
  148. _jcolors=Null
  149. _jfonts=Null
  150. _jstyles=Null
  151. End
  152. Method ToRect:Recti( jrect:JsonValue )
  153. Local arr:=jrect.ToArray()
  154. Local l:=0,t:=0,r:=0,b:=0
  155. Select arr.Length
  156. Case 1
  157. l=arr[0].ToNumber() ; r=l
  158. t=arr[0].ToNumber() ; b=t
  159. Case 2
  160. l=arr[0].ToNumber() ; r=l
  161. t=arr[1].ToNumber() ; b=t
  162. Case 4
  163. l=arr[0].ToNumber()
  164. t=arr[1].ToNumber()
  165. r=arr[2].ToNumber()
  166. b=arr[3].ToNumber()
  167. End
  168. l*=_themeScale
  169. t*=_themeScale
  170. r*=_themeScale
  171. b*=_themeScale
  172. Return New Recti( -l,-t,r,b )
  173. End
  174. Method LoadColor:Color( jcolor:JsonValue )
  175. Local jarr:=Cast<JsonArray>( jcolor )
  176. If jarr
  177. Local r:=0.0,g:=0.0,b:=0.0,a:=1.0
  178. Select jarr.Length
  179. Case 1
  180. r=jarr[0].ToNumber()
  181. g=jarr[0].ToNumber()
  182. b=jarr[0].ToNumber()
  183. Case 3
  184. r=jarr[0].ToNumber()
  185. g=jarr[1].ToNumber()
  186. b=jarr[2].ToNumber()
  187. Case 4
  188. r=jarr[0].ToNumber()
  189. g=jarr[1].ToNumber()
  190. b=jarr[2].ToNumber()
  191. a=jarr[3].ToNumber()
  192. Default
  193. Return Color.Magenta
  194. End
  195. Return New Color( r,g,b,a )
  196. Endif
  197. Local str:=jcolor.ToString()
  198. If str.StartsWith( "#" )
  199. Local a:=1.0,r:=0.0,g:=0.0,b:=0.0
  200. If str.Length=4 '#RGB
  201. r=StringToULong( str.Slice( 1,2 ),16 )/15.0
  202. g=StringToULong( str.Slice( 2,3 ),16 )/15.0
  203. b=StringToULong( str.Slice( 3,4 ),16 )/15.0
  204. Else If str.Length=5 '#ARGB
  205. a=StringToULong( str.Slice( 1,2 ),16 )/15.0
  206. r=StringToULong( str.Slice( 2,3 ),16 )/15.0
  207. g=StringToULong( str.Slice( 3,4 ),16 )/15.0
  208. b=StringToULong( str.Slice( 4,5 ),16 )/15.0
  209. Else If str.Length=7 '#RRGGBB
  210. r=StringToULong( str.Slice( 1,3 ),16 )/255.0
  211. g=StringToULong( str.Slice( 3,5 ),16 )/255.0
  212. b=StringToULong( str.Slice( 5,7 ),16 )/255.0
  213. Else If str.Length=9 '#AARRGGBB
  214. a=StringToULong( str.Slice( 1,3 ),16 )/255.0
  215. r=StringToULong( str.Slice( 3,5 ),16 )/255.0
  216. g=StringToULong( str.Slice( 5,7 ),16 )/255.0
  217. b=StringToULong( str.Slice( 7,9 ),16 )/255.0
  218. Else
  219. Return Color.Magenta
  220. Endif
  221. Return New Color( r,g,b,a )
  222. Endif
  223. If _colors.Contains( str ) Return _colors[str]
  224. jcolor=_jcolors[str]
  225. If Not jcolor Return Color.Magenta
  226. Local color:=LoadColor( jcolor )
  227. _colors[str]=color
  228. Return color
  229. End
  230. Method LoadFont:Font( name:JsonValue )
  231. Local str:=name.ToString()
  232. If _fonts.Contains( str ) Return _fonts[str]
  233. Local jfont:=_jfonts[str]
  234. If Not jfont Return App.DefaultFont
  235. Local fname:=jfont.ToString()
  236. Local fsize:=0
  237. Local i:=fname.Find( "," )
  238. If i<>-1
  239. fsize=Int( fname.Slice( i+1 ) )
  240. fname=fname.Slice( 0,i )
  241. Endif
  242. Local font:=LoadFont( fname,fsize )
  243. _fonts[str]=font
  244. Return font
  245. End
  246. Method SetStyle( style:Style,jstyle:StringMap<JsonValue> )
  247. If jstyle.Contains( "backgroundColor" ) style.BackgroundColor=LoadColor( jstyle["backgroundColor"] )
  248. If jstyle.Contains( "borderColor" ) style.BorderColor=LoadColor( jstyle["borderColor"] )
  249. If jstyle.Contains( "textColor" ) style.TextColor=LoadColor( jstyle["textColor"] )
  250. If jstyle.Contains( "iconColor" ) style.IconColor=LoadColor( jstyle["iconColor"] )
  251. If jstyle.Contains( "skinColor" ) style.SkinColor=LoadColor( jstyle["skinColor"] )
  252. If jstyle.Contains( "padding" ) style.Padding=ToRect( jstyle["padding"] )
  253. If jstyle.Contains( "border" ) style.Border=ToRect( jstyle["border"] )
  254. If jstyle.Contains( "margin" ) style.Margin=ToRect( jstyle["margin"] )
  255. If jstyle.Contains( "icons" ) style.Icons=LoadIcons( jstyle["icons"].ToString() )
  256. If jstyle.Contains( "skin" ) style.Skin=LoadSkin( jstyle["skin"].ToString() )
  257. If jstyle.Contains( "font" ) style.Font=LoadFont( jstyle["font"] )
  258. End
  259. Method LoadStyle:Style( name:JsonValue )
  260. Local str:=name.ToString()
  261. If _styles.Contains( str ) Return _styles[str]
  262. Local jobj:=_jstyles[str]
  263. If Not jobj Return _defaultStyle
  264. Local jstyle:=jobj.ToObject()
  265. Local pstyle:Style
  266. If jstyle.Contains( "extends" )
  267. pstyle=LoadStyle( jstyle["extends"] )
  268. Else
  269. pstyle=_defaultStyle
  270. Endif
  271. Local style:=New Style( pstyle )
  272. SetStyle( style,jstyle )
  273. For Local it:=Eachin style.States
  274. SetStyle( it.Value,jstyle )
  275. Next
  276. If jstyle.Contains( "states" )
  277. local jstates:=jstyle["states"].ToObject()
  278. For Local it:=Eachin jstates
  279. Local state:=style.AddState( it.Key )
  280. SetStyle( state,it.Value.ToObject() )
  281. Next
  282. Endif
  283. _styles[str]=style
  284. Return style
  285. End
  286. End