Browse Source

Added theme change support to mojo.app.

Mark Sibly 9 years ago
parent
commit
3272066277

+ 20 - 8
modules/mojo/app/app.monkey2

@@ -157,9 +157,24 @@ Class AppInstance
 		
 		
 		SDL_GL_MakeCurrent( _sdlWindow,_sdlGLContext )
 		SDL_GL_MakeCurrent( _sdlWindow,_sdlGLContext )
 #Endif
 #Endif
-		_defaultFont=Font.Open( "DejaVuSans",16 )
+		_defaultFont=_res.OpenFont( "DejaVuSans",16 )
 		
 		
-		_theme=Theme.Load( GetConfig( "initialTheme","asset::themes/default.json" ) )
+		_theme=New Theme
+		
+		Local themePath:=GetConfig( "initialTheme","default" )
+		
+		Local themeScale:=Float( GetConfig( "initialThemeScale",1 ) )
+		
+		_theme.Load( themePath,New Vec2f( themeScale ) )
+		
+		_theme.ThemeChanged+=Lambda()
+
+			ThemeChanged()
+			
+			RequestRender()
+			
+			UpdateWindows()
+		End
 	End
 	End
 	
 	
 	#rem monkeydoc Fallback font.
 	#rem monkeydoc Fallback font.
@@ -174,12 +189,6 @@ Class AppInstance
 	Property Theme:Theme()
 	Property Theme:Theme()
 	
 	
 		Return _theme
 		Return _theme
-		
-	Setter( theme:Theme )
-	
-		_theme=theme
-		
-		ThemeChanged()
 	End
 	End
 	
 	
 	#rem monkeydoc True if clipboard text is empty.
 	#rem monkeydoc True if clipboard text is empty.
@@ -222,6 +231,8 @@ Class AppInstance
 	#end
 	#end
 	Property KeyView:View()
 	Property KeyView:View()
 	
 	
+		If Not _active Return Null
+	
 		If IsActive( _keyView ) Return _keyView
 		If IsActive( _keyView ) Return _keyView
 		
 		
 		If _modalView Return _modalView
 		If _modalView Return _modalView
@@ -528,6 +539,7 @@ Class AppInstance
 	Field _touchMouse:Bool=False		'Whether mouse is really touch
 	Field _touchMouse:Bool=False		'Whether mouse is really touch
 	Field _captureMouse:Bool=False		'Whether to use SDL_CaptureMouse
 	Field _captureMouse:Bool=False		'Whether to use SDL_CaptureMouse
 	
 	
+	Field _res:=New ResourceManager
 	Field _defaultFont:Font
 	Field _defaultFont:Font
 	Field _theme:Theme
 	Field _theme:Theme
 
 

+ 1 - 1
modules/mojo/app/sdl_rwstream.monkey2

@@ -39,7 +39,7 @@ Class SDL_RWStream Extends Stream
 	Closing the filestream also sets its position and length to 0.
 	Closing the filestream also sets its position and length to 0.
 	
 	
 	#end
 	#end
-	Method Close() Override
+	Method OnClose() Override
 		If Not _rwops Return
 		If Not _rwops Return
 		SDL_RWclose( _rwops )
 		SDL_RWclose( _rwops )
 		_rwops=Null
 		_rwops=Null

+ 48 - 32
modules/mojo/app/skin.monkey2

@@ -1,9 +1,11 @@
 
 
 Namespace mojo.app
 Namespace mojo.app
 
 
+Using std.resource
+
 #rem monkeydoc @hidden
 #rem monkeydoc @hidden
 #end
 #end
-Class Skin
+Class Skin Extends Resource
 
 
 	Property Image:Image()
 	Property Image:Image()
 		Return _image
 		Return _image
@@ -41,11 +43,9 @@ Class Skin
 
 
 	Function Load:Skin( path:String )
 	Function Load:Skin( path:String )
 	
 	
-		Local pixmap:=Pixmap.Load( path )
+		Local pixmap:=Pixmap.Load( path,,True )
 		If Not pixmap Return Null
 		If Not pixmap Return Null
 		
 		
-		pixmap.PremultiplyAlpha()
-		
 		Return New Skin( pixmap )
 		Return New Skin( pixmap )
 	End
 	End
 	
 	
@@ -53,67 +53,83 @@ Class Skin
 	
 	
 	Field _image:Image
 	Field _image:Image
 	Field _bounds:Recti
 	Field _bounds:Recti
-	Field _rect:Recti
 	
 	
 	Field _x0:Int,_x1:Int,_x2:Int,_x3:Int
 	Field _x0:Int,_x1:Int,_x2:Int,_x3:Int
 	Field _y0:Int,_y1:Int,_y2:Int,_y3:Int
 	Field _y0:Int,_y1:Int,_y2:Int,_y3:Int
 	
 	
 	Method New( pixmap:Pixmap )
 	Method New( pixmap:Pixmap )
 	
 	
-		Local _scale:Recti
-		Local _fill:Recti
+		Local stretch:Recti
+		Local padding:Recti
 	
 	
 		For Local x:=1 Until pixmap.Width-1
 		For Local x:=1 Until pixmap.Width-1
 			Local p:=pixmap.GetPixelARGB( x,0 )
 			Local p:=pixmap.GetPixelARGB( x,0 )
 			If p=UInt( $ff000000 )
 			If p=UInt( $ff000000 )
-				If Not _scale.min.x _scale.min.x=x
-				_scale.max.x=x+1
+				If Not stretch.min.x stretch.min.x=x
+				stretch.max.x=x+1
 			Endif
 			Endif
 			p=pixmap.GetPixelARGB( x,pixmap.Height-1 )
 			p=pixmap.GetPixelARGB( x,pixmap.Height-1 )
 			If p=UInt( $ff000000 )
 			If p=UInt( $ff000000 )
-				If Not _fill.min.x _fill.min.x=x
-				_fill.max.x=x+1
+				If Not padding.min.x padding.min.x=x
+				padding.max.x=x+1
 			Endif
 			Endif
 		Next
 		Next
 		
 		
 		For Local y:=1 Until pixmap.Height-1
 		For Local y:=1 Until pixmap.Height-1
 			Local p:=pixmap.GetPixelARGB( 0,y )
 			Local p:=pixmap.GetPixelARGB( 0,y )
 			If p=UInt( $ff000000 )
 			If p=UInt( $ff000000 )
-				If Not _scale.min.y _scale.min.y=y
-				_scale.max.y=y+1
+				If Not stretch.min.y stretch.min.y=y
+				stretch.max.y=y+1
 			Endif
 			Endif
 			p=pixmap.GetPixelARGB( pixmap.Width-1,y )
 			p=pixmap.GetPixelARGB( pixmap.Width-1,y )
 			If p=UInt( $ff000000 )
 			If p=UInt( $ff000000 )
-				If Not _fill.min.y _fill.min.y=y
-				_fill.max.y=y+1
+				If Not padding.min.y padding.min.y=y
+				padding.max.y=y+1
 			Endif
 			Endif
 		Next
 		Next
 		
 		
-		If _scale.min.x And _scale.min.y
-			pixmap=pixmap.Window( 1,1,pixmap.Width-2,pixmap.Height-2 )
-			If Not _fill.min.x Or Not _fill.min.y _fill=_scale
-			_scale-=New Vec2i( 1,1 )
-			_fill-=New Vec2i( 1,1 )
+		If stretch.min.x And stretch.min.y
+			pixmap=pixmap.Window( 1,1,pixmap.Width-2,pixmap.Height-2 ).Copy()
+			If Not padding.min.x Or Not padding.min.y padding=stretch
+			stretch-=New Vec2i( 1,1 )
+			padding-=New Vec2i( 1,1 )
 		Else
 		Else
-			_scale=New Recti( pixmap.Width/3,pixmap.Height/3,pixmap.Width*2/3,pixmap.Height*2/3 )
-			_fill=_scale
+			stretch=New Recti( pixmap.Width/2,pixmap.Height/2,pixmap.Width*2+1,pixmap.Height*2+1 )
+			padding=stretch
 		Endif
 		Endif
 		
 		
-		_rect=New Recti( 0,0,pixmap.Width,pixmap.Height )
-		
 		_x0=0
 		_x0=0
-		_x1=_scale.min.x
-		_x2=_scale.max.x
-		_x3=_rect.max.x
+		_x1=stretch.min.x
+		_x2=stretch.max.x
+		_x3=pixmap.Width
 		
 		
 		_y0=0
 		_y0=0
-		_y1=_scale.min.y
-		_y2=_scale.max.y
-		_y3=_rect.max.y
+		_y1=stretch.min.y
+		_y2=stretch.max.y
+		_y3=pixmap.Height
 		
 		
 		_image=New Image( pixmap )
 		_image=New Image( pixmap )
-		_bounds=New Recti( -_fill.min,_rect.max-_fill.max )
-	
+
+		_bounds=New Recti( -padding.min.x,-padding.min.y,_x3-padding.max.x,_y3-padding.max.y )
+		
+		AddDependancy( _image )
 	End
 	End
 End
 End
 
 
+Class ResourceManager Extension
+
+	Method OpenSkin:Skin( path:String )
+	
+		Local slug:="Skin:name="+StripDir( StripExt( path ) )
+		
+		Local skin:=Cast<Skin>( OpenResource( slug ) )
+		If skin Return skin
+		
+		Local pixmap:=OpenPixmap( path,,True )
+		If pixmap skin=New Skin( pixmap )
+
+		AddResource( slug,skin )
+		Return skin
+	End
+
+End

+ 11 - 1
modules/mojo/app/style.monkey2

@@ -12,10 +12,19 @@ Class Style
 		Init( style )
 		Init( style )
 	End
 	End
 	
 	
+	Method New( name:String,style:Style )
+		_name=name
+		Init( style )
+	End
+	
 	Method Copy:Style()
 	Method Copy:Style()
 		Return New Style( Self )
 		Return New Style( Self )
 	End
 	End
 	
 	
+	Method Set( style:Style )
+		Init( style )
+	End
+	
 	Method GetState:Style( name:String )
 	Method GetState:Style( name:String )
 		Local state:=_states[name]
 		Local state:=_states[name]
 		If Not state Return Self
 		If Not state Return Self
@@ -256,7 +265,7 @@ Class Style
 		Local bgcolor:=BackgroundColor
 		Local bgcolor:=BackgroundColor
 		If bgcolor.a
 		If bgcolor.a
 			canvas.Color=bgcolor
 			canvas.Color=bgcolor
-			canvas.DrawRect( bounds.X,bounds.Y,bounds.Width,bounds.Height )
+			canvas.DrawRect( bounds )
 		Endif
 		Endif
 		
 		
 		Local skin:=Skin
 		Local skin:=Skin
@@ -309,6 +318,7 @@ Class Style
 		_iconColor=style._iconColor
 		_iconColor=style._iconColor
 		_icons=style._icons.Slice( 0 )
 		_icons=style._icons.Slice( 0 )
 		_font=style._font
 		_font=style._font
+		_states.Clear()
 		If style._states
 		If style._states
 			For Local it:=Eachin style._states
 			For Local it:=Eachin style._states
 				_states[it.Key]=New Style( it.Value )
 				_states[it.Key]=New Style( it.Value )

+ 146 - 110
modules/mojo/app/theme.monkey2

@@ -3,26 +3,28 @@ Namespace mojo.app
 
 
 Class Theme
 Class Theme
 
 
-	Property Path:String()
+	Field ThemeChanged:Void()
 
 
-		Return _themePath
+	Method New()
+		_defaultStyle=New Style
+		_defaultStyle.Font=App.DefaultFont
 	End
 	End
 	
 	
-	Property DefaultStyle:Style()
-	
-		Return _defaultStyle
-	End
+	Property Scale:Vec2f()
 	
 	
-	#rem monkeydoc Gets a color from the theme.
+		Return _themeScale
 	
 	
-	If no color named `name` is found, [[Color.Grey]] is returned.
+	Setter( scale:Vec2f )
+		If scale=_themeScale Return
+		
+		_themeScale=scale
+		
+		Reload()
+	End
 	
 	
-	#end
-	Method GetColor:Color( name:String )
+	Property DefaultStyle:Style()
 	
 	
-		If _colors.Contains( name ) Return _colors[name]
-		
-		Return Color.Grey
+		Return _defaultStyle
 	End
 	End
 	
 	
 	#rem monkeydoc Gets a font from the theme.
 	#rem monkeydoc Gets a font from the theme.
@@ -38,6 +40,18 @@ Class Theme
 		Return App.DefaultFont
 		Return App.DefaultFont
 	End
 	End
 	
 	
+	#rem monkeydoc Gets a color from the theme.
+	
+	If no color named `name` is found, [[Color.Grey]] is returned.
+	
+	#end
+	Method GetColor:Color( name:String )
+	
+		If _colors.Contains( name ) Return _colors[name]
+		
+		Return Color.Grey
+	End
+	
 	#rem monkeydoc Gets a style from the theme.
 	#rem monkeydoc Gets a style from the theme.
 	
 	
 	If no style named `name` is found, the [[DefaultStyle]] for the theme is returned.
 	If no style named `name` is found, the [[DefaultStyle]] for the theme is returned.
@@ -62,26 +76,30 @@ Class Theme
 	* The asset::fonts/ directory.
 	* The asset::fonts/ directory.
 	
 	
 	#end
 	#end
-	Method LoadFont:Font( file:String,size:Int )
+	Method OpenFont:Font( path:String,size:Float )
 	
 	
-		size*=_themeScale
-	
-		Local font:Font
-		
-		If ExtractRootDir( file )
-			font=Font.Open( file,size )
-		Else
-			font=Font.Open( _themeDir+file,size )
-			If Not font 
-				font=Font.Open( "asset::fonts/"+file,size )
-			Endif
-		Endif
+		size*=_themeScale.y
 		
 		
+		Local font:Font
+		If Not ExtractRootDir( path ) font=_res.OpenFont( "theme::"+path,size )
+		If Not font font=_res.OpenFont( path,size )
 		If Not font Return App.DefaultFont
 		If Not font Return App.DefaultFont
 		
 		
 		Return font
 		Return font
 	End
 	End
 	
 	
+	Method OpenImage:Image( path:String )
+	
+		Local image:Image
+		If Not ExtractRootDir( path ) image=_res.OpenImage( "theme::"+path )
+		If Not image image=_res.OpenImage( path )
+		If Not image Return null
+		
+		image.Scale=_themeScale
+		
+		Return image
+	End
+	
 	#rem monkeydoc Loads an array of icon images from a file.
 	#rem monkeydoc Loads an array of icon images from a file.
 	
 	
 	Loads an array of icons from an image file.
 	Loads an array of icons from an image file.
@@ -92,41 +110,39 @@ Class Theme
 	
 	
 	The number of icons loaded is the width of the image file divided by its height.
 	The number of icons loaded is the width of the image file divided by its height.
 	
 	
-	If `file` is not an absolute path, the file is searched for in the following locations:
-	
-	* The theme's directory.
-	
-	* The asset::images/ directory.
-	
 	#end
 	#end
-	Method LoadIcons:Image[]( file:String )
+	Method OpenIcons:Image[]( path:String )
 	
 	
-		Local atlas:Image
+		Local slug:="Icons:"+StripDir( StripExt( path ) )
 		
 		
-		If ExtractRootDir( file )
-			atlas=Image.Load( file )
-		Else
-			atlas=Image.Load( _themeDir+file )
-			If Not atlas
-				atlas=Image.Load( "asset::images/"+file )
-			Endif
-		Endif
+		Local icons:=Cast<Icons>( _res.OpenResource( slug ) )
+		If Not icons
 
 
-		If Not atlas Return Null
-		
-		atlas.Scale=New Vec2f( _themeScale,_themeScale )
-		
-		Local size:=atlas.Height
-		
-		Local n:=atlas.Width/size
+			Local atlas:=OpenImage( path )
+			If Not atlas Return Null
 		
 		
-		Local icons:=New Image[n]
+			Local size:=atlas.Rect.Height
+			Local n:=atlas.Rect.Width/size
+	
+			icons=New Icons
+			icons.atlas=atlas
+			icons.images=New Image[n]
+			
+			For Local i:=0 Until n
+				Local image:=New Image( atlas,New Recti( i*size,0,i*size+size,size ) )
+				icons.images[i]=image
+			Next
+			
+			icons.AddDependancy( atlas )
+
+			_res.AddResource( slug,icons )
+		Endif
 		
 		
-		For Local i:=0 Until n
-			icons[i]=New Image( atlas,New Recti( i*size,0,i*size+size,atlas.Height ) )
+		For Local image:=Eachin icons.images
+			image.Scale=_themeScale
 		Next
 		Next
 		
 		
-		Return icons
+		Return icons.images
 	End
 	End
 	
 	
 	#rem monkeydoc Loads a skin from a file.
 	#rem monkeydoc Loads a skin from a file.
@@ -140,77 +156,80 @@ Class Theme
 	* The asset::images/ directory.
 	* The asset::images/ directory.
 	
 	
 	#end
 	#end
-	Method LoadSkin:Skin( file:String )
+	Method OpenSkin:Skin( path:String )
 	
 	
 		Local skin:Skin
 		Local skin:Skin
-
-		If ExtractRootDir( file )
-			skin=Skin.Load( file )
-		Else
-			skin=Skin.Load( _themeDir+file )
-			If Not skin skin=Skin.Load( "asset::images/"+file )
-		Endif
+		If Not ExtractRootDir( path ) skin=_res.OpenSkin( "theme::"+path )
+		If Not skin skin=_res.OpenSkin( path )
 		
 		
 		If Not skin Return Null
 		If Not skin Return Null
 		
 		
-		skin.Image.Scale=New Vec2f( _themeScale,_themeScale )
+		skin.Image.Scale=_themeScale
 		
 		
 		Return skin
 		Return skin
 	End
 	End
-
-	#rem monkeydoc Loads a theme from a file.
 	
 	
-	#end
-	Function Load:Theme( path:String )
+	Method Load:Bool( path:String,scale:Vec2f=New Vec2f( 1 ) )
+	
+		If Not ExtractRootDir( path ) path="theme::"+path
+		
+		If Not ExtractExt( path ) path+=".json"
 	
 	
 		Local jobj:=JsonObject.Load( path )
 		Local jobj:=JsonObject.Load( path )
-		If Not jobj 
+		If Not jobj
 			Print "Failed to load theme:"+path
 			Print "Failed to load theme:"+path
-			return New Theme
+			return False
 		Endif
 		Endif
 		
 		
-		Return New Theme( path,jobj )
+		_jcolors=jobj["colors"].ToObject()
+		_jfonts=jobj["fonts"].ToObject()
+		_jstyles=jobj["styles"].ToObject()
+		
+		_themeScale=scale
+		
+		Reload()
+		
+		Return True
 	End
 	End
 
 
 	Private
 	Private
 	
 	
+	Class Icons Extends Resource
+		Field atlas:Image
+		Field images:Image[]
+	End
+	
 	Const _jdefault:=New JsonString( "default" )
 	Const _jdefault:=New JsonString( "default" )
+	
+	Field _res:ResourceManager
 
 
-#if __TARGET__="android"	
-	Field _themeScale:Int=2
-#else
-	Field _themeScale:Int=1
-#endif
-
-	Field _themePath:String
-	Field _themeDir:String
+	Field _themeScale:Vec2f=New Vec2f( 1,1 )
+	
+	Field _jcolors:StringMap<JsonValue>
+	Field _jfonts:StringMap<JsonValue>
+	Field _jstyles:StringMap<JsonValue>
 	
 	
 	Field _defaultStyle:Style
 	Field _defaultStyle:Style
 	
 	
-	Field _colors:=New StringMap<Color>
 	Field _fonts:=New StringMap<Font>
 	Field _fonts:=New StringMap<Font>
+	Field _colors:=New StringMap<Color>
 	Field _styles:=New StringMap<Style>
 	Field _styles:=New StringMap<Style>
+	Field _cstyles:=New StringMap<Style>
 	
 	
-	Field _jcolors:StringMap<JsonValue>
-	Field _jfonts:StringMap<JsonValue>
-	Field _jstyles:StringMap<JsonValue>
-	
-	Method New()
-		_themePath=""
-		_themeDir=""
-		_defaultStyle=New Style
-		_defaultStyle.Font=App.DefaultFont
+	Method Unload()
+		_fonts.Clear()
+		_colors.Clear()
+		_styles.Clear()
+		_defaultStyle=Null
 	End
 	End
 	
 	
-	Method New( path:String,jobj:JsonObject )
-		Self.New()
-	
-		_themePath=path
-		_themeDir=ExtractDir( _themePath )
+	Method Reload()
 	
 	
-		_jcolors=jobj["colors"].ToObject()
-		_jfonts=jobj["fonts"].ToObject()
-		_jstyles=jobj["styles"].ToObject()
+		Unload()
+		
+		Local res:=_res
+		
+		_res=New ResourceManager
 		
 		
 		_defaultStyle=LoadStyle( _jdefault )
 		_defaultStyle=LoadStyle( _jdefault )
 		
 		
@@ -226,9 +245,9 @@ Class Theme
 			LoadStyle( New JsonString( it.Key ) )
 			LoadStyle( New JsonString( it.Key ) )
 		Next
 		Next
 		
 		
-		_jcolors=Null
-		_jfonts=Null
-		_jstyles=Null
+		If res res.Discard()
+		
+		ThemeChanged()
 	End
 	End
 	
 	
 	Method ToRect:Recti( jrect:JsonValue )
 	Method ToRect:Recti( jrect:JsonValue )
@@ -251,10 +270,10 @@ Class Theme
 			b=arr[3].ToNumber()
 			b=arr[3].ToNumber()
 		End
 		End
 		
 		
-		l*=_themeScale
-		t*=_themeScale
-		r*=_themeScale
-		b*=_themeScale
+		l=l*_themeScale.x
+		t=t*_themeScale.y
+		r=r*_themeScale.x
+		b=b*_themeScale.y
 		
 		
 		Return New Recti( -l,-t,r,b )
 		Return New Recti( -l,-t,r,b )
 	End
 	End
@@ -349,8 +368,8 @@ Class Theme
 			fname=fname.Slice( 0,i )
 			fname=fname.Slice( 0,i )
 		Endif
 		Endif
 
 
-		Local font:=LoadFont( fname,fsize )
-
+		Local font:=OpenFont( fname,fsize )
+		
 		_fonts[str]=font
 		_fonts[str]=font
 		
 		
 		Return font
 		Return font
@@ -368,8 +387,8 @@ Class Theme
 		If jstyle.Contains( "border" ) style.Border=ToRect( jstyle["border"] )
 		If jstyle.Contains( "border" ) style.Border=ToRect( jstyle["border"] )
 		If jstyle.Contains( "margin" ) style.Margin=ToRect( jstyle["margin"] )
 		If jstyle.Contains( "margin" ) style.Margin=ToRect( jstyle["margin"] )
 		
 		
-		If jstyle.Contains( "icons" ) style.Icons=LoadIcons( jstyle["icons"].ToString() )
-		If jstyle.Contains( "skin" ) style.Skin=LoadSkin( jstyle["skin"].ToString() )
+		If jstyle.Contains( "icons" ) style.Icons=OpenIcons( jstyle["icons"].ToString() )
+		If jstyle.Contains( "skin" ) style.Skin=OpenSkin( jstyle["skin"].ToString() )
 
 
 		If jstyle.Contains( "font" ) style.Font=LoadFont( jstyle["font"] )
 		If jstyle.Contains( "font" ) style.Font=LoadFont( jstyle["font"] )
 	End
 	End
@@ -385,6 +404,8 @@ Class Theme
 		
 		
 		Local jstyle:=jobj.ToObject()
 		Local jstyle:=jobj.ToObject()
 		
 		
+		'get parent style
+		'
 		Local pstyle:Style
 		Local pstyle:Style
 		
 		
 		If jstyle.Contains( "extends" )
 		If jstyle.Contains( "extends" )
@@ -392,9 +413,21 @@ Class Theme
 		Else
 		Else
 			pstyle=_defaultStyle
 			pstyle=_defaultStyle
 		Endif
 		Endif
-		
-		Local style:=New Style( pstyle )
-		
+
+		'create new style
+		'		
+		Local style:Style
+		
+		Local cstyle:=_cstyles[str]
+		If cstyle
+			style=cstyle
+			style.Set( pstyle )
+		Else
+			style=New Style( str,pstyle )
+		Endif
+
+		'initialize
+		'		
 		SetStyle( style,jstyle )
 		SetStyle( style,jstyle )
 		
 		
 		For Local it:=Eachin style.States
 		For Local it:=Eachin style.States
@@ -402,6 +435,8 @@ Class Theme
 			SetStyle( it.Value,jstyle )
 			SetStyle( it.Value,jstyle )
 		Next
 		Next
 		
 		
+		'create states
+		'
 		If jstyle.Contains( "states" )
 		If jstyle.Contains( "states" )
 		
 		
 			local jstates:=jstyle["states"].ToObject()
 			local jstates:=jstyle["states"].ToObject()
@@ -414,7 +449,8 @@ Class Theme
 			Next
 			Next
 		
 		
 		Endif
 		Endif
-
+		
+		_cstyles[str]=style
 		_styles[str]=style
 		_styles[str]=style
 		
 		
 		Return style
 		Return style

+ 29 - 25
modules/mojo/app/view.monkey2

@@ -8,8 +8,8 @@ Class View
 	#rem monkeydoc Invoked when a view becomes visble and active.
 	#rem monkeydoc Invoked when a view becomes visble and active.
 	#end
 	#end
 	Field Activated:Void()
 	Field Activated:Void()
-	
-	#rem monkeydoc Invoked when a view becomes is not longer visble or active.
+
+	#rem monkeydoc Invoked when a view is no longer visble or active.
 	#end
 	#end
 	Field Deactivated:Void()
 	Field Deactivated:Void()
 
 
@@ -19,12 +19,14 @@ Class View
 			_themeSeq=1
 			_themeSeq=1
 			App.ThemeChanged+=Lambda()
 			App.ThemeChanged+=Lambda()
 				_themeSeq+=1
 				_themeSeq+=1
-				If _themeSeq<0 _themeSeq=1
+				If _themeSeq=$40000000 _themeSeq=1
 			End
 			End
 		Endif
 		Endif
 		
 		
 		_style=New Style( App.Theme.DefaultStyle )
 		_style=New Style( App.Theme.DefaultStyle )
 		
 		
+		_styleSeq=_themeSeq
+		
 		InvalidateStyle()
 		InvalidateStyle()
 	End
 	End
 
 
@@ -144,7 +146,7 @@ Class View
 	| "float"			| View floats within its layout frame according to the view [[Gravity]].
 	| "float"			| View floats within its layout frame according to the view [[Gravity]].
 	| "fill-x"			| View is resized on the x axis and floats on the y axis.
 	| "fill-x"			| View is resized on the x axis and floats on the y axis.
 	| "fill-y"			| View is resized on the y axis and floats on the x axs.
 	| "fill-y"			| View is resized on the y axis and floats on the x axs.
-	| "stretch"			| View is stretched to fit its layout frame.
+	| "stretch"			| View is stretched non-uniformly to fit its layout frame.
 	| "letterbox"		| View is uniformly stretched on both axii and centered within its layout frame.
 	| "letterbox"		| View is uniformly stretched on both axii and centered within its layout frame.
 	| "letterbox-int"	| View is uniformly stretched on both axii and centered within its layout frame. Scale factors are integrized.
 	| "letterbox-int"	| View is uniformly stretched on both axii and centered within its layout frame. Scale factors are integrized.
 
 
@@ -328,11 +330,9 @@ Class View
 	
 	
 	#rem monkeydoc Adds a child view to this view.
 	#rem monkeydoc Adds a child view to this view.
 	
 	
-	AddChildView is normally used internally by 'layout' views. However you can also add a child view to any view
-	directly by calling this method.
+	AddChildView is normally used internally by 'layout' views. However you can also add a child view to any view directly by calling this method.
 	
 	
-	If you use this method to add a child view to a view, it is your responsiblity to also manage the child view's frame using
-	the [[Frame]] property.
+	If you use this method to add a child view to a view, it is your responsiblity to also manage the child view's frame using the [[Frame]] property.
 
 
 	#end
 	#end
 	Method AddChildView( view:View )
 	Method AddChildView( view:View )
@@ -540,7 +540,7 @@ Class View
 	#end
 	#end
 	Method InvalidateStyle()
 	Method InvalidateStyle()
 	
 	
-		_styleSeq=-1
+		_styleSeq|=$40000000
 		
 		
 		App.RequestRender()
 		App.RequestRender()
 	End
 	End
@@ -551,15 +551,9 @@ Class View
 	
 	
 		If _styleSeq=_themeSeq Return
 		If _styleSeq=_themeSeq Return
 		
 		
-		If _styleSeq<>-1
+		Local themeChanged:=(_styleSeq & $3fffffff<>_themeSeq)
 		
 		
-			Local name:=_style.Name
-			If name
-				Local style:=App.Theme.GetStyle( name )
-				If style _style=style
-			Endif
-
-		Endif
+		_styleSeq=_themeSeq
 	
 	
 		_rstyle=_style
 		_rstyle=_style
 		
 		
@@ -571,8 +565,8 @@ Class View
 		
 		
 		_styleBounds=_rstyle.Bounds
 		_styleBounds=_rstyle.Bounds
 		
 		
-		_styleSeq=_themeSeq
-				
+		If themeChanged OnThemeChanged()
+		
 		OnValidateStyle()
 		OnValidateStyle()
 	End
 	End
 	
 	
@@ -599,10 +593,12 @@ Class View
 		
 		
 		Local size:=OnMeasure()
 		Local size:=OnMeasure()
 		
 		
-		If _minSize.x size.x=Max( size.x,_minSize.x )
-		If _minSize.y size.y=Max( size.y,_minSize.y )
-		If _maxSize.x size.x=Min( size.x,_maxSize.x )
-		If _maxSize.y size.y=Min( size.y,_maxSize.y )
+		Local scale:=App.Theme.Scale
+		
+		If _minSize.x size.x=Max( size.x,Int( _minSize.x*scale.x ) )
+		If _minSize.y size.y=Max( size.y,Int( _minSize.y*scale.y ) )
+		If _maxSize.x size.x=Min( size.x,Int( _maxSize.x*scale.x ) )
+		If _maxSize.y size.y=Min( size.y,Int( _maxSize.y*scale.y ) )
 		
 		
 		_measuredSize=size
 		_measuredSize=size
 		
 		
@@ -726,7 +722,7 @@ Class View
 	#rem monkeydoc @hidden
 	#rem monkeydoc @hidden
 	#end
 	#end
 	Method Render( canvas:Canvas )
 	Method Render( canvas:Canvas )
-
+	
 		If Not _visible Return
 		If Not _visible Return
 		
 		
 		canvas.BeginRender( _bounds,_matrix )
 		canvas.BeginRender( _bounds,_matrix )
@@ -746,6 +742,14 @@ Class View
 
 
 	Protected
 	Protected
 	
 	
+	#rem monkeydoc Called during layout if theme has changed.
+	
+	This is called immediately before [[OnValidateStyle]] if the theme has changed.
+
+	#end
+	Method OnThemeChanged() Virtual
+	End
+	
 	#rem monkeydoc Called during layout if [[Style]] or [[StyleState]] have changed.
 	#rem monkeydoc Called during layout if [[Style]] or [[StyleState]] have changed.
 
 
 	Views can use this method to cache [[RenderStyle]] properties if necessary.
 	Views can use this method to cache [[RenderStyle]] properties if necessary.
@@ -883,7 +887,7 @@ Class View
 	
 	
 	Global _themeSeq:Int
 	Global _themeSeq:Int
 	
 	
-	Field _styleSeq:Int=-1
+	Field _styleSeq:Int=0
 	
 	
 	Field _parent:View
 	Field _parent:View
 	Field _window:Window
 	Field _window:Window

+ 10 - 2
modules/mojo/app/window.monkey2

@@ -188,6 +188,13 @@ Class Window Extends View
 	
 	
 	Protected
 	Protected
 	
 	
+	#rem monkeydoc @hidden
+	#end
+	Method OnThemeChanged() override
+	
+		_clearColor=App.Theme.GetColor( "windowClearColor" )
+	End
+	
 	#rem monkeydoc Window event handler.
 	#rem monkeydoc Window event handler.
 	
 	
 	Called when the window is sent a window event.
 	Called when the window is sent a window event.
@@ -358,6 +365,7 @@ Class Window Extends View
 	End
 	End
 	
 	
 	Method Init( title:String,rect:Recti,flags:WindowFlags )
 	Method Init( title:String,rect:Recti,flags:WindowFlags )
+		Style=GetStyle( "Window" )
 	
 	
 		Local x:=(flags & WindowFlags.CenterX) ? SDL_WINDOWPOS_CENTERED Else rect.X
 		Local x:=(flags & WindowFlags.CenterX) ? SDL_WINDOWPOS_CENTERED Else rect.X
 		Local y:=(flags & WindowFlags.CenterY) ? SDL_WINDOWPOS_CENTERED Else rect.Y
 		Local y:=(flags & WindowFlags.CenterY) ? SDL_WINDOWPOS_CENTERED Else rect.Y
@@ -400,10 +408,10 @@ Class Window Extends View
 		_frame=GetFrame()
 		_frame=GetFrame()
 		Frame=_frame
 		Frame=_frame
 		
 		
-		_canvas=New Canvas( _frame.Width,_frame.Height )
-		
 		_clearColor=App.Theme.GetColor( "windowClearColor" )
 		_clearColor=App.Theme.GetColor( "windowClearColor" )
 		
 		
+		_canvas=New Canvas( _frame.Width,_frame.Height )
+		
 		SetWindow( Self )
 		SetWindow( Self )
 		
 		
 		UpdateActive()
 		UpdateActive()