Browse Source

tweaks to mojo in prep. for mojox.

Mark Sibly 9 years ago
parent
commit
ee94b4bcc9

+ 205 - 91
modules/mojo/app/app.monkey2

@@ -1,9 +1,10 @@
 
+#Import "native/async.cpp"
+
 Namespace mojo.app
 
-#Import "native/async.cpp"
-#Import "assets/Roboto-Regular.ttf@/mojo"
-#Import "assets/RobotoMono-Regular.ttf@/mojo"
+'#Import "assets/Roboto-Regular.ttf@/mojo"
+'#Import "assets/RobotoMono-Regular.ttf@/mojo"
 
 #rem monkeydoc The global AppInstance instance.
 #end
@@ -40,15 +41,31 @@ Class AppInstance
 	#rem monkeydoc @hidden
 	#end
 	Field NextIdle:Void()	
+
+	#rem monkeydoc @hidden
+	#end
+	Field ThemeChanged:Void()
+	
+	#rem monkeydoc Invoked when app is activated.
+	#end
+	Field Activated:Void()
+	
+	#rem monkeydoc Invoked when app is deactivated.
+	#end
+	Field Deactivated:Void()
 	
 	#rem monkeydoc Key event filter.
 	
+	To prevent the event from being sent to a view, a filter can eat the event using [[Event.Eat]].
+	
 	Functions should check if the event has already been 'eaten' by checking the event's [[Event.Eaten]] property before processing the event.
 	
 	#end
 	Field KeyEventFilter:Void( event:KeyEvent )
 
 	#rem monkeydoc MouseEvent filter.
+	
+	To prevent the event from being sent to a view, a filter can eat the event using [[Event.Eat]].
 
 	Functions should check if the event has already been 'eaten' by checking the event's [[Event.Eaten]] property before processing the event.
 	
@@ -67,8 +84,6 @@ Class AppInstance
 		
 		SDL_Init( SDL_INIT_EVERYTHING & ~SDL_INIT_AUDIO )
 		
-		_sdlThread=SDL_ThreadID()
-		
 		Keyboard.Init()
 		
 		Mouse.Init()
@@ -83,40 +98,38 @@ Class AppInstance
 		SDL_GL_SetAttribute( SDL_GL_DEPTH_SIZE,Int( GetConfig( "GL_depth_buffer_enabled",0 ) ) )
 		SDL_GL_SetAttribute( SDL_GL_STENCIL_SIZE,Int( GetConfig( "GL_stencil_buffer_enabled",0 ) ) )
 
-		_dummyWindow=New Window( "<hidden>",Self )
-		
+		'create dummy window/context
+		Local _sdlWindow:=SDL_CreateWindow( "<dummy>",0,0,0,0,SDL_WINDOW_HIDDEN|SDL_WINDOW_OPENGL )
+		Assert( _sdlWindow,"FATAL ERROR: SDL_CreateWindow failed" )
+
+		Local _sdlGLContext:=SDL_GL_CreateContext( _sdlWindow )
+		Assert( _sdlGLContext,"FATAL ERROR: SDL_GL_CreateContext failed" )
+		SDL_GL_MakeCurrent( _sdlWindow,_sdlGLContext )	
+
 #Endif
-		_defaultFont=Font.Open( DefaultFontName,16 )
+		_defaultFont=Font.Open( "asset::fonts/DejaVuSans.ttf",16 )
 		
-		_defaultMonoFont=Font.Open( DefaultMonoFontName,16 )
-
-		Local style:=Style.GetStyle( "" )
-		style.DefaultFont=_defaultFont
-		style.DefaultColor=Color.White
+		_theme=Theme.Load( GetConfig( "initialTheme","asset::themes/default.json" ) )
 	End
 	
-	#rem monkeydoc @hidden
-	#end
-	Property DefaultFontName:String()
-		Return "asset::mojo/Roboto-Regular.ttf"
-	End
-	
-	#rem monkeydoc @hidden
-	#end
-	Property DefaultMonoFontName:String()
-		Return "asset::mojo/RobotoMono-Regular.ttf"
-	End
-	
-	#rem monkeydoc @hidden
+	#rem monkeydoc Fallback font.
 	#end
 	Property DefaultFont:Font()
+	
 		Return _defaultFont
 	End
 	
-	#rem monkeydoc @hidden
+	#rem monkeydoc The current theme.
 	#end
-	Property DefaultMonoFont:Font()
-		Return _defaultMonoFont
+	Property Theme:Theme()
+	
+		Return _theme
+		
+	Setter( theme:Theme )
+	
+		_theme=theme
+		
+		ThemeChanged()
 	End
 	
 	#rem monkeydoc True if clipboard text is empty.
@@ -159,16 +172,15 @@ Class AppInstance
 	#end
 	Property KeyView:View()
 	
-		Local window:=ActiveWindow
-		If window Return window.KeyView
+		If IsActive( _keyView ) Return _keyView
 		
-		Return Null
+		If _modalView Return _modalView
+		
+		Return _activeWindow
 		
 	Setter( keyView:View )
-
-		Local window:=ActiveWindow
-		If window window.KeyView=keyView
-
+	
+		_keyView=keyView
 	End
 	
 	#rem monkeydoc The current mouse view.
@@ -208,15 +220,40 @@ Class AppInstance
 #Endif
 
 	End
+	
+	#rem monkeydoc True if app is active.
+	
+	An app is active if any of its windows has system input focus.
+	
+	If Active is true, then [[ActiveWindow]] will always be non-null.
+	
+	If Active is false, then [[ActiveWindow]] will always be null.
+	
+	#end
+	Property Active:Bool()
+	
+		Return _active
+	End
 
-	#rem monkeydoc The current active window.
+	#rem monkeydoc The currently active window.
+	
+	The active window is the window that has system input focus.
+	
+	If ActiveWindow is non-null, then [[Active]] will always be true.
 	
-	The active window is the window that has input focus.
+	If ActiveWindow is null, then [[Active]] will always be false.
 	
 	#end
 	Property ActiveWindow:Window()
+
+		#rem	
+		If Not _activeWindow
+			Local windows:=Window.VisibleWindows()
+			If windows _activeWindow=windows[0]
+		Endif
+		#end
 	
-		Return Window.VisibleWindows()[0]
+		Return _activeWindow
 	End
 	
 	#rem monkeydoc Approximate frames per second rendering rate.
@@ -246,6 +283,23 @@ Class AppInstance
 		Return _mouseLocation
 	End
 	
+	Property ModalView:View()
+	
+		Return _modalView
+	End
+	
+	#rem monkeydoc @hidden
+	#end
+	Method WaitIdle()
+		Local future:=New Future<Bool>
+		
+		Idle+=Lambda()
+			future.Set( True )
+		End
+		
+		future.Get()
+	End
+	
 	#rem monkeydoc @hidden
 	#end
 	Method GetConfig:String( name:String,defValue:String )
@@ -273,14 +327,21 @@ Class AppInstance
 	#rem monkeydoc @hidden
 	#end
 	Method BeginModal( view:View )
+	
 		_modalStack.Push( _modalView )
+		
 		_modalView=view
+		
+		RequestRender()
 	End
 	
 	#rem monkeydoc @hidden
 	#end
 	Method EndModal()
+	
 		_modalView=_modalStack.Pop()
+		
+		RequestRender()
 	End
 	
 	#rem monkeydoc Terminate the app.
@@ -293,7 +354,7 @@ Class AppInstance
 	#rem monkeydoc Request that the app render itself.
 	#end
 	Method RequestRender()
-
+	
 		_requestRender=True
 	End
 
@@ -309,17 +370,57 @@ Class AppInstance
 	
 		UpdateEvents()
 		
-		If Not _requestRender Return
+		UpdateWindows()
+	End
+	
+	#rem @hiddden
+	#end
+	Method IsActive:Bool( view:View )
+	
+		Return view And view.Active And (Not _modalView Or view.IsChildOf( _modalView ))
+	End
+	
+	#rem @hiddden
+	#end
+	Method ActiveViewAtMouseLocation:View()
+	
+		If Not _window Return Null
+		
+		Local view:=_window.FindViewAtWindowPoint( _mouseLocation )
+		If IsActive( view ) Return view
 		
+		Return Null
+	End
+
+	#rem @hidden
+	#end	
+	Method UpdateWindows()
+	
+		Local render:=_requestRender
 		_requestRender=False
 		
-		UpdateFPS()
-			
+		If render UpdateFPS()
+		
 		For Local window:=Eachin Window.VisibleWindows()
-			window.Update()
-			window.Render()
-		Next
-			
+			window.UpdateWindow( render )
+		End
+
+		If _mouseView And Not IsActive( _mouseView )
+			SendMouseEvent( EventType.MouseUp,_mouseView )
+			_mouseView=Null
+		Endif
+		
+		If _hoverView And Not IsActive( _hoverView )
+			SendMouseEvent( EventType.MouseLeave,_hoverView )
+			_hoverView=Null
+		Endif
+		
+		If Not _hoverView
+			_hoverView=ActiveViewAtMouseLocation()
+			If _mouseView And _hoverView<>_mouseView _hoverView=Null
+			If _hoverView SendMouseEvent( EventType.MouseEnter,_hoverView )
+		Endif
+		
 	End
 	
 	#rem monkeydoc @hidden
@@ -355,24 +456,23 @@ Class AppInstance
 
 	Private
 	
-	Field _sdlThread:SDL_threadID
-	
 	Field _config:StringMap<String>
 	
-	Field _dummyWindow:Window
-
 	Field _defaultFont:Font
-	Field _defaultMonoFont:Font
-		
-	Field _requestRender:Bool
+	Field _theme:Theme
+
+	Field _active:Bool
+	Field _activeWindow:Window
 	
+	Field _keyView:View
 	Field _hoverView:View
 	Field _mouseView:View
 	
+	Field _requestRender:Bool
 	Field _fps:Float
 	Field _fpsFrames:Int
 	Field _fpsMillis:Int
-	
+
 	Field _window:Window
 	Field _key:Key
 	Field _rawKey:Key
@@ -381,6 +481,7 @@ Class AppInstance
 	Field _mouseButton:MouseButton
 	Field _mouseLocation:Vec2i
 	Field _mouseWheel:Vec2i
+	Field _mouseClicks:Int=0
 	
 	Field _modalView:View
 	Field _modalStack:=New Stack<View>
@@ -424,16 +525,11 @@ Class AppInstance
 		NextIdle=Null
 		idle()
 		
-		For Local window:=Eachin Window.VisibleWindows()
-'			window.Update()
-		Next
-
 	End
 	
 	Method SendKeyEvent( type:EventType )
 	
 		Local view:=KeyView
-		If view And Not view.ReallyEnabled view=Null
 		
 		Local event:=New KeyEvent( type,view,_key,_rawKey,_modifiers,_keyChar )
 		
@@ -441,28 +537,43 @@ Class AppInstance
 		
 		If event.Eaten Return
 		
-		If _modalView And Not view.IsChildOf( _modalView ) Return
-		
-		If view 
-			view.SendKeyEvent( event )
-		Else If ActiveWindow
-			ActiveWindow.SendKeyEvent( event )
-		Endif
+		If view view.SendKeyEvent( event )
 	End
 	
 	Method SendMouseEvent( type:EventType,view:View )
 	
 		Local location:=view.TransformWindowPointToView( _mouseLocation )
 		
-		Local event:=New MouseEvent( type,view,location,_mouseButton,_mouseWheel,_modifiers )
+		Local event:=New MouseEvent( type,view,location,_mouseButton,_mouseWheel,_modifiers,_mouseClicks )
 		
 		MouseEventFilter( event )
 		
 		If event.Eaten Return
 		
-		If _modalView And Not view.IsChildOf( _modalView ) Return
-		
 		view.SendMouseEvent( event )
+		
+		If event.Eaten Return
+		
+		Select type
+		Case EventType.MouseDown
+		
+			Select _mouseButton
+			Case MouseButton.Left
+			
+				SendMouseEvent( EventType.MouseClick,view )
+				
+				If _mouseClicks And Not (_mouseClicks & 1)
+				
+					SendMouseEvent( EventType.MouseDoubleClick,view )
+					
+				End
+			
+			Case MouseButton.Right
+
+				SendMouseEvent( EventType.MouseRightClick,view )
+			End
+		End
+		
 	End
 	
 	Method SendWindowEvent( type:EventType )
@@ -535,7 +646,8 @@ Class AppInstance
 			
 			If Not _mouseView
 			
-				Local view:=_window.FindViewAtWindowPoint( _mouseLocation )
+				Local view:=ActiveViewAtMouseLocation()
+
 				If view
 '#If __HOSTOS__<>"linux"
 					SDL_CaptureMouse( SDL_TRUE )
@@ -544,7 +656,14 @@ Class AppInstance
 				Endif
 			Endif
 				
-			If _mouseView SendMouseEvent( EventType.MouseDown,_mouseView )
+			If _mouseView 
+			
+				_mouseClicks=mevent->clicks
+				
+				SendMouseEvent( EventType.MouseDown,_mouseView )
+				
+				_mouseClicks=0
+			Endif
 		
 		Case SDL_MOUSEBUTTONUP
 		
@@ -576,8 +695,7 @@ Class AppInstance
 			
 			_mouseLocation=New Vec2i( mevent->x,mevent->y )
 			
-			Local view:=_window.FindViewAtWindowPoint( _mouseLocation )
-
+			Local view:=ActiveViewAtMouseLocation()
 			If _mouseView And view<>_mouseView view=Null
 			
 			If view<>_hoverView
@@ -639,12 +757,24 @@ Class AppInstance
 			
 			Case SDL_WINDOWEVENT_FOCUS_GAINED
 			
+				Local active:=_active
+				_activeWindow=_window
+				_active=True
+				
 				SendWindowEvent( EventType.WindowGainedFocus )
+				
+				If active<>_active Activated()
 			
 			Case SDL_WINDOWEVENT_FOCUS_LOST
 			
+				Local active:=_active
+				_activeWindow=Null
+				_active=False
+			
 				SendWindowEvent( EventType.WindowLostFocus )
 				
+				If active<>_active Deactivated()
+				
 			Case SDL_WINDOWEVENT_LEAVE
 			
 				If _hoverView
@@ -680,13 +810,6 @@ Class AppInstance
 	
 	Method EventFilter:Int( userData:Void Ptr,event:SDL_Event Ptr )
 	
-		#rem
-		If SDL_ThreadID()<>_sdlThread 
-			Print "Yikes! EventFilter running in non-main thread..."
-			Return 1
-		Endif
-		#end
-			
 		Select event[0].type
 		Case SDL_WINDOWEVENT
 
@@ -707,16 +830,7 @@ Class AppInstance
 			
 				SendWindowEvent( EventType.WindowResized )
 				
-				If _requestRender
-				
-					_requestRender=False
-					
-					For Local window:=Eachin Window.VisibleWindows()
-						window.Update()
-						window.Render()
-					Next
-					
-				Endif
+				UpdateWindows()
 			
 				Return 0
 

BIN
modules/mojo/app/assets/DejaVuSansMono.ttf


BIN
modules/mojo/app/assets/Inconsolata-g.ttf


BIN
modules/mojo/app/assets/Roboto-Regular.ttf


BIN
modules/mojo/app/assets/RobotoMono-Regular.ttf


BIN
modules/mojo/app/assets/checkmark_icons.png


+ 0 - 327
modules/mojo/app/assets/htmlview_master_css.css

@@ -1,327 +0,0 @@
-html {
-    display: block;
-    height:100%;
-    width:100%;
-	position: relative;
-}
-
-head {
-    display: none
-}
-
-meta {
-    display: none
-}
-
-title {
-    display: none
-}
-
-link {
-    display: none
-}
-
-style {
-    display: none
-}
-
-script {
-    display: none
-}
-
-body {
-	display:block; 
-	margin:8px; 
-    height:100%;
-    width:100%;
-}
-
-p {
-	display:block; 
-	margin-top:1em; 
-	margin-bottom:1em;
-}
-
-b, strong {
-	display:inline; 
-	font-weight:bold;
-}
-
-i, em {
-	display:inline; 
-	font-style:italic;
-}
-
-center 
-{
-	text-align:center;
-	display:block;
-}
-
-a:link
-{
-	text-decoration: underline;
-	color: #00f;
-	cursor: pointer;
-}
-
-h1, h2, h3, h4, h5, h6, div {
-	display:block;
-}
-
-h1 {
-	font-weight:bold; 
-	margin-top:0.67em; 
-	margin-bottom:0.67em; 
-	font-size: 2em;
-}
-
-h2 {
-	font-weight:bold; 
-	margin-top:0.83em; 
-	margin-bottom:0.83em; 
-	font-size: 1.5em;
-}
-
-h3 {
-	font-weight:bold; 
-	margin-top:1em; 
-	margin-bottom:1em; 
-	font-size:1.17em;
-}
-
-h4 {
-	font-weight:bold; 
-	margin-top:1.33em; 
-	margin-bottom:1.33em
-}
-
-h5 {
-	font-weight:bold; 
-	margin-top:1.67em; 
-	margin-bottom:1.67em;
-	font-size:.83em;
-}
-
-h6 {
-	font-weight:bold; 
-	margin-top:2.33em; 
-	margin-bottom:2.33em;
-	font-size:.67em;
-} 
-
-br {
-	display:inline-block;
-}
-
-br[clear="all"]
-{
-	clear:both;
-}
-
-br[clear="left"]
-{
-	clear:left;
-}
-
-br[clear="right"]
-{
-	clear:right;
-}
-
-span {
-	display:inline
-}
-
-img {
-	display: inline-block;
-}
-
-img[align="right"]
-{
-	float: right;
-}
-
-img[align="left"]
-{
-	float: left;
-}
-
-hr {
-    display: block;
-    margin-top: 0.5em;
-    margin-bottom: 0.5em;
-    margin-left: auto;
-    margin-right: auto;
-    border-style: inset;
-    border-width: 1px
-}
-
-
-/***************** TABLES ********************/
-
-table {
-    display: table;
-    border-style: solid;
-    border-collapse: separate;
-    border-spacing: 2px;
-    border-top-color:gray;
-    border-left-color:gray;
-    border-bottom-color:black;
-    border-right-color:black;
-}
-
-tbody, tfoot, thead {
-	display:table-row-group;
-	vertical-align:middle;
-}
-
-tr {
-    display: table-row;
-    vertical-align: inherit;
-    border-color: inherit;
-}
-
-td, th {
-    display: table-cell;
-    vertical-align: inherit;
-    border-width:1px;
-    padding:1px;
-}
-
-th {
-	font-weight: bold;
-}
-
-table[border] {
-    border-style:solid;
-}
-
-table[border|=0] {
-    border-style:none;
-}
-
-table[border] td, table[border] th {
-    border-style:solid;
-    border-top-color:black;
-    border-left-color:black;
-    border-bottom-color:gray;
-    border-right-color:gray;
-}
-
-table[border|=0] td, table[border|=0] th {
-    border-style:none;
-}
-
-caption {
-	display: table-caption;
-}
-
-td[nowrap], th[nowrap] {
-	white-space:nowrap;
-}
-
-tt, code, kbd, samp {
-    font-family: monospace
-}
-
-pre, xmp, plaintext, listing {
-    display: block;
-    font-family: monospace;
-    white-space: pre;
-    margin: 1em 0
-}
-
-/***************** LISTS ********************/
-
-ul, menu, dir {
-    display: block;
-    list-style-type: disc;
-    margin-top: 1em;
-    margin-bottom: 1em;
-    margin-left: 0;
-    margin-right: 0;
-    padding-left: 40px
-}
-
-ol {
-    display: block;
-    list-style-type: decimal;
-    margin-top: 1em;
-    margin-bottom: 1em;
-    margin-left: 0;
-    margin-right: 0;
-    padding-left: 40px
-}
-
-li {
-    display: list-item;
-}
-
-ul ul, ol ul {
-    list-style-type: circle;
-}
-
-ol ol ul, ol ul ul, ul ol ul, ul ul ul {
-    list-style-type: square;
-}
-
-dd {
-    display: block;
-    margin-left: 40px;
-}
-
-dl {
-    display: block;
-    margin-top: 1em;
-    margin-bottom: 1em;
-    margin-left: 0;
-    margin-right: 0;
-}
-
-dt {
-    display: block;
-}
-
-ol ul, ul ol, ul ul, ol ol {
-    margin-top: 0;
-    margin-bottom: 0
-}
-
-blockquote {
-	display: block;
-	margin-top: 1em;
-	margin-bottom: 1em;
-	margin-left: 40px;
-	margin-left: 40px;
-}
-
-/*********** FORM ELEMENTS ************/
-
-form {
-	display: block;
-	margin-top: 0em;
-}
-
-option {
-	display: none;
-}
-
-input, textarea, keygen, select, button, isindex {
-	margin: 0em;
-	color: initial;
-	line-height: normal;
-	text-transform: none;
-	text-indent: 0;
-	text-shadow: none;
-	display: inline-block;
-}
-input[type="hidden"] {
-	display: none;
-}
-
-
-article, aside, footer, header, hgroup, nav, section 
-{
-	display: block;
-}

BIN
modules/mojo/app/assets/monkey_font.png


BIN
modules/mojo/app/assets/treenode_collapsed.png


BIN
modules/mojo/app/assets/treenode_expanded.png


+ 23 - 4
modules/mojo/app/event.monkey2

@@ -10,6 +10,9 @@ Namespace mojo.app
 | KeyUp				| Key up event.
 | KeyChar			| Key char event.
 | MouseDown			| Mouse button down event.
+| MouseClick		| Mouse left click event.
+| MouseRightClick	| Mouse right click event.
+| MouseDoubleClick	| Mouse double left click event.
 | MouseUp			| Mouse button up event.
 | MouseMove			| Mouse movement event.
 | MouseWheel		| Mouse wheel event.
@@ -30,6 +33,9 @@ Enum EventType
 	KeyChar,
 	
 	MouseDown,
+	MouseClick,
+	MouseRightClick,
+	MouseDoubleClick,
 	MouseUp,
 	MouseMove,
 	MouseWheel,
@@ -42,7 +48,7 @@ Enum EventType
 	WindowGainedFocus,
 	WindowLostFocus,
 	
-	Eaten=$80000000
+	Eaten=$40000000
 End
 
 #rem monkeydoc The Event class.
@@ -145,15 +151,16 @@ Class MouseEvent Extends Event
 
 	#rem monkeydoc @hidden
 	#end
-	Method New( type:EventType,view:View,location:Vec2i,button:MouseButton,wheel:Vec2i,modifiers:Modifier )
+	Method New( type:EventType,view:View,location:Vec2i,button:MouseButton,wheel:Vec2i,modifiers:Modifier,clicks:Int )
 		Super.New( type,view )
 		_location=location
 		_button=button
 		_wheel=wheel
 		_modifiers=modifiers
+		_clicks=clicks
 	End
 	
-	#rem monkeydoc Mouse location.
+	#rem monkeydoc Mouse location in View.
 	#end
 	Property Location:Vec2i()
 		Return _location
@@ -177,13 +184,25 @@ Class MouseEvent Extends Event
 		Return _modifiers
 	End
 	
+	#rem monkeydoc Number of mouse clicks.
+	#end
+	Property Clicks:Int()
+		Return _clicks
+	End
+	
+	Method TransformToView:MouseEvent( view:View )
+		If view=View Return self
+		Local location:=view.TransformPointFromView( _location,_view )
+		Return New MouseEvent( Type,View,location,_button,_wheel,_modifiers,_clicks )
+	End
+	
 	Private
 	
 	Field _location:Vec2i
 	Field _button:MouseButton
 	Field _wheel:Vec2i
 	Field _modifiers:Modifier
-	
+	Field _clicks:Int
 End
 
 #rem monkeydoc The WindowEvent class.

+ 156 - 89
modules/mojo/app/style.monkey2

@@ -6,46 +6,37 @@ Namespace mojo.app
 Class Style
 
 	Method New()
-		Init( "",Null,False )
 	End
 	
 	Method New( style:Style )
-		Init( "",style,True )
+		Init( style )
 	End
 	
-	#rem monkeydoc @hidden
-	#end
-	Method New( name:String )
-		Init( name,Null,False )
-	End
-	
-	#rem monkeydoc @hidden
-	#end
-	Method New( name:String,style:Style )
-		Init( name,style,True )
+	Method Copy:Style()
+		Return New Style( Self )
 	End
 	
-	#rem monkeydoc @hidden
-	#end
-	Method AddState:Style( state:String,srcState:String="" )
-	
-		Local style:=New Style
-		
-		style.Init( "",GetState( srcState ),False )
-		
-		_states[state]=style
+	Method GetState:Style( name:String )
+		Local state:=_states[name]
+		If Not state Return Self
 		
-		Return style
+		Return state
+	End
+
+	Method AddState:Style( name:String )
+		Local state:=_states[name]
+		If state Return state
+
+		state=New Style( Self )
+		_states[name]=state
+
+		Return state
 	End
 	
-	#rem monkeydoc @hidden
+	#rem monkeydoc Name of the style.
 	#end
-	Method GetState:Style( state:String )
-	
-		Local style:=_states[state]
-		If style Return style
-		
-		Return Self
+	Property Name:String()
+		Return _name
 	End
 
 	#rem monkeydoc Background color.
@@ -104,35 +95,50 @@ Class Style
 		_margin=margin
 	End
 	
-	#rem monkeydoc Default canvas color.
+	#rem monkeydoc Color to use when drawing text.
 	#end
-	Property DefaultColor:Color()
-		Return _color
+	Property TextColor:Color()
+		Return _textColor
 	Setter( color:Color )
-		_color=color
+		_textColor=color
 	End
-
-	#rem monkeydoc Default canvas font.
-	#end	
+	
+	#rem monkeydoc Color to use when drawing icons.
+	#end
+	Property IconColor:Color()
+		Return _iconColor
+	Setter( color:Color )
+		_iconColor=color
+	End
+	
+	#rem monkeydoc Font to use when drawing text.
+	
+	Deprecated! Just use [[Font]] instead...
+	
+	#end
 	Property DefaultFont:Font()
 		Return _font
 	Setter( font:Font )
 		_font=font
 	End
 	
-	#rem monkeydoc @hidden
+	#rem monkeydoc Font to use when drawing text.
 	#end
-	Method SetImage( name:String,image:Image )
-		_images[name]=image
+	Property Font:Font()
+		Return _font
+	Setter( font:Font )
+		_font=font
 	End
 	
-	#rem monkeydoc @hidden
+	#rem monkeydoc Custom icons.
 	#end
-	Method GetImage:Image( name:String )
-		Return _images[name]
+	Property Icons:Image[]()
+		Return _icons
+	Setter( icons:Image[] )
+		_icons=icons
 	End
 	
-	#rem monkeydoc @hidden
+	#rem monkeydoc Total style bounds.
 	#end
 	Property Bounds:Recti()
 		Local bounds:=Padding
@@ -142,6 +148,83 @@ Class Style
 		bounds+=Margin
 		Return bounds
 	End
+
+	#rem monkeydoc Measure text.
+	#end
+	Method MeasureText:Vec2i( text:String )
+	
+		If Not text Return New Vec2i( 0,0 )
+		
+		If text.Contains( "~n" )
+
+			Local lines:=text.Split( "~n" ),w:=0
+
+			For Local line:=Eachin lines
+				w=Max( w,Int( _font.TextWidth( line ) ) )
+			Next
+			Return New Vec2i( w,_font.Height * lines.Length )
+		Else
+			Return New Vec2i( _font.TextWidth( text ),_font.Height )
+		Endif
+	End
+	
+	Method DrawText( canvas:Canvas,text:String,x:Int,y:Int,handlex:Float=0,handley:Float=0 )
+
+		Local font:=canvas.Font
+		Local color:=canvas.Color
+		
+		canvas.Font=_font
+		canvas.Color=_textColor
+		
+		canvas.DrawText( text,x,y,handlex,handley )
+		
+		canvas.Font=font
+		canvas.Color=color
+	End
+	
+	Method DrawText( canvas:Canvas,text:String,rect:Recti,gravity:Vec2f )
+	
+		If Not text Return
+	
+		Local size:=MeasureText( text )
+		
+		Local x:=rect.Left + (rect.Width-size.x) * gravity.x
+		Local y:=rect.Top + (rect.Height-size.y) * gravity.y
+		
+		Local font:=canvas.Font
+		Local color:=canvas.Color
+		
+		canvas.Font=_font
+		canvas.Color=_textColor
+		
+		If text.Contains( "~n" )
+		
+			Local lines:=text.Split( "~n" )
+			
+			For Local line:=Eachin lines
+			
+				If line canvas.DrawText( line,x + (size.x-_font.TextWidth( line )) * gravity.x,y )
+				y+=_font.Height
+			Next
+		
+		Else If text<>"~n"
+		
+			canvas.DrawText( text,x,y )
+		Endif
+		
+		canvas.Font=font
+		canvas.Color=color
+	End
+	
+	Method DrawIcon( canvas:Canvas,icon:Image,x:Int,y:Int )
+	
+		Local color:=canvas.Color
+		canvas.Color=_iconColor
+		
+		canvas.DrawImage( icon,x,y )
+		
+		canvas.Color=color
+	End
 	
 	#rem monkeydoc @hidden
 	#end
@@ -185,31 +268,21 @@ Class Style
 		Endif
 		
 		canvas.Font=_font
-		canvas.Color=_color
-		
+		canvas.Color=Color.White
 	End
 	
+	'***** INTERNAL *****
+
 	#rem monkeydoc @hidden
 	#end
-	Function GetStyle:Style( name:String )
-	
-		Local style:=_styles[name]
-		If style Return style
-		
-		Local i:=name.Find( ":" )
-		If i<>-1 Return GetStyle( name.Slice( 0,i ) )
-		
-		If Not _defaultStyle _defaultStyle=New Style
-		Return _defaultStyle
+	Property States:StringMap<Style>()
+		If Not _states _states=New StringMap<Style>
+		Return _states
 	End
-
-	Private
-	
-	Global _defaultStyle:Style
-	Global _styles:=New StringMap<Style>
 	
-	Field _states:=New StringMap<Style>
+	Private
 	
+	Field _name:String
 	Field _bgcolor:Color=Color.None
 	Field _padding:Recti
 	Field _skin:Skin
@@ -217,35 +290,29 @@ Class Style
 	Field _border:Recti
 	Field _bdcolor:Color=Color.None
 	Field _margin:Recti
-	Field _color:Color
+	Field _textColor:Color=Color.Black
+	Field _iconColor:Color=Color.White
+	Field _icons:Image[]
 	Field _font:Font
-	Field _images:=New StringMap<Image>
-	
-	Method Init( name:String,style:Style,copyStates:Bool )
-	
-		If Not style style=_defaultStyle
-		
-		If style
-			_bgcolor=style._bgcolor
-			_padding=style._padding
-			_skin=style._skin
-			_skcolor=style._skcolor
-			_border=style._border
-			_bdcolor=style._bdcolor
-			_margin=style._margin
-			_color=style._color
-			_font=style._font
-			_images=style._images.Copy()
-			
-			If copyStates
-				For Local it:=Eachin style._states
-					_states[it.Key]=New Style( it.Value )
-				Next
-			Endif
-		Endif
-		
-		If name
-			_styles[name]=Self
+	Field _states:=New StringMap<Style>
+
+	Method Init( style:Style )
+		If Not style Return
+		_bgcolor=style._bgcolor
+		_padding=style._padding
+		_skin=style._skin
+		_skcolor=style._skcolor
+		_border=style._border
+		_bdcolor=style._bdcolor
+		_margin=style._margin
+		_textColor=style._textColor
+		_iconColor=style._iconColor
+		_icons=style._icons.Slice( 0 )
+		_font=style._font
+		If style._states
+			For Local it:=Eachin style._states
+				_states[it.Key]=New Style( it.Value )
+			Next
 		Endif
 	End
 	

+ 277 - 153
modules/mojo/app/view.monkey2

@@ -5,15 +5,30 @@ Namespace mojo.app
 #end
 Class View
 
-	Method New()
+	#rem monkeydoc Invoked when a view becomes visble and active.
+	#end
+	Field Activated:Void()
 	
-		_style=New Style
+	#rem monkeydoc Invoked when a view becomes is not longer visble or active.
+	#end
+	Field Deactivated:Void()
+
+	Method New()
+		
+		If Not _themeSeq
+			_themeSeq=1
+			App.ThemeChanged+=Lambda()
+				_themeSeq+=1
+				If _themeSeq<0 _themeSeq=1
+			End
+		Endif
+		
+		_style=New Style( App.Theme.DefaultStyle )
+		
+		InvalidateStyle()
 	End
 
-	#rem monkeydoc @hidden View visible flag.
-	
-	Use [[ReallyVisible]] to test if the view is really visible.
-	
+	#rem monkeydoc View visibility state.
 	#end
 	Property Visible:Bool()
 	
@@ -23,26 +38,17 @@ Class View
 		If visible=_visible Return
 	
 		_visible=visible
-	End
-
-	#rem monkeydoc @hidden View visibility state.
-	
-	True if the view's visibility flag is set AND all its parent visibility flags up to the root window are also set.
-	
-	#end
-	Property ReallyVisible:Bool()
-	
-		Return _visible And (Not _parent Or _parent.ReallyVisible)
+		
+		RequestRender()
+		
+		UpdateActive()
 	End
 	
-	#rem monkeydoc @hidden View enabled flag.
-	
-	Use [[ReallyEnabled]] to test if the view is really enabled.
-	
+	#rem monkeydoc View enabled state.
 	#end
 	Property Enabled:Bool()
 	
-		Return _enabled
+		Return _enabled And (Not _parent Or _parent.Enabled)
 	
 	Setter( enabled:Bool )
 		If enabled=_enabled Return
@@ -50,16 +56,42 @@ Class View
 		_enabled=enabled
 		
 		InvalidateStyle()
+		
+		UpdateActive()
 	End
 
-	#rem monkeydoc @hidden View enabled state.
+	#rem monkeydoc View active state.
+	
+	A view is active it is visible, enabled, attached to a window and all its parents are also active.
 	
-	True if the view's enabled flag is set AND all its parent enabled flags are set AND [[ReallyVisible]] is also true. 
+	Events are only sent to active windows.
 	
+	#end	
+	Property Active:Bool()
+	
+		Return _active
+	End
+	
+	#rem monkeydoc Whether the view accepts key events.
+	#end
+	Property AcceptsKeyEvents:Bool()
+	
+		Return _acceptsKeyEvents
+	
+	Setter( acceptsKeyEvents:Bool )
+	
+		_acceptsKeyEvents=acceptsKeyEvents
+	End
+	
+	#rem monkeydoc Whether the view accepts mouse events.
 	#end
-	Property ReallyEnabled:Bool()
+	Property AcceptsMouseEvents:Bool()
 	
-		Return _enabled And _visible And (Not _parent Or _parent.ReallyEnabled)
+		Return _acceptsMouseEvents
+	
+	Setter( acceptsMouseEvents:Bool )
+	
+		_acceptsMouseEvents=acceptsMouseEvents
 	End
 	
 	#rem monkeydoc View style.
@@ -76,7 +108,7 @@ Class View
 		InvalidateStyle()
 	End
 	
-	#rem monkeydoc @hidden
+	#rem monkeydoc View style state.
 	#end
 	Property StyleState:String()
 	
@@ -90,7 +122,10 @@ Class View
 		InvalidateStyle()
 	End
 	
-	#rem monkeydoc @hidden
+	#rem monkeydoc View render style.
+	
+	This is the style used to render the view, and is dependant on [[Style]] and [[StyleState]].
+	
 	#end
 	Property RenderStyle:Style()
 	
@@ -105,11 +140,14 @@ Class View
 	
 	| Layout mode		| Description
 	|:------------------|:-----------
-	| "resize"			| View is resized to fit its layout frame.
+	| "fill"			| View is resized to fit its layout frame.
+	| "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-y"			| View is resized on the y axis and floats on the x axs.
 	| "stretch"			| View is stretched to fit its layout frame.
 	| "letterbox"		| View is uniformly stretched on both axii and centered within its layout frame.
-	| "float"			| View floats within its layout frame according to the view [[Gravity]].
-	
+	| "letterbox-int"	| View is uniformly stretched on both axii and centered within its layout frame. Scale factors are integrized.
+
 	#end
 	Property Layout:String()
 
@@ -125,7 +163,7 @@ Class View
 	
 	The 'frame' the view is contained in.
 	
-	Note that the frame rect is in 'parent space' coordinates, and is usually set by the parent view when layout occurs.
+	Note that the frame rect is in parent space coordinates, and is usually set by the parent view when layout occurs.
 	
 	#end	
 	Property Frame:Recti()
@@ -133,7 +171,6 @@ Class View
 		Return _frame
 	
 	Setter( frame:Recti )
-		If frame=_frame Return
 	
 		_frame=frame
 	End
@@ -172,8 +209,6 @@ Class View
 	Setter( minSize:Vec2i )
 	
 		_minSize=minSize
-		
-		InvalidateStyle()
 	End
 	
 	#rem monkeydoc Maximum view size.
@@ -185,8 +220,6 @@ Class View
 	Setter( maxSize:Vec2i )
 	
 		_maxSize=maxSize
-		
-		InvalidateStyle()
 	End
 	
 	#rem monkeydoc View content rect.
@@ -269,37 +302,71 @@ Class View
 		Return _rmatrix
 	End
 	
-	#rem monkeydoc @hidden
+	#rem monkeydoc The parent view of this view.
 	#end
 	Property Parent:View()
 	
 		Return _parent
 	End
 	
-	#rem monkeydoc @hidden
+	#rem monkeydoc The Window this view is attached to, if any.
+	#end
+	Property Window:Window()
+	
+		Return _window
+	End
+	
+	#rem monkeydoc Gets a style.
+	
+	This is a convenience method equivalent to App.Theme.GetStyle( name ).
+	
+	#end
+	Method GetStyle:Style( name:String )
+	
+		Return App.Theme.GetStyle( name )
+	End
+	
+	#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.
+	
+	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
-	Method AddChild( view:View )
+	Method AddChildView( view:View )
 	
 		If Not view Return
 		
-		Assert( Not view._parent )
-		
+		Assert( Not view._parent,"View already has a parent" )
+
+		Assert( Not Cast<Window>( view ),"Windows cannot be child views" )
+	
+		view._parent=Self
+		view.SetWindow( _window )
 		_children.Add( view )
 		
-		view._parent=Self
+		RequestRender()
+		
+		view.UpdateActive()
 	End
 	
-	#rem monkeydoc @hidden
+	#rem monkeydoc Removes a child view from this view.
 	#end
-	Method RemoveChild( view:View )
+	Method RemoveChildView( view:View )
 	
 		If Not view Return
 		
-		Assert( view._parent=Self )
-		
+		Assert( view._parent=Self,"View is not a child view" )
+
+		view._parent=Null
+		view.SetWindow( Null )
 		_children.Remove( view )
 		
-		view._parent=Null
+		RequestRender()
+		
+		view.UpdateActive()
 	End
 	
 	#rem monkeydoc @hidden
@@ -342,7 +409,7 @@ Class View
 	
 	#rem monkeydoc Transforms a point from another view.
 	
-	Transforms `point` in coordinates local to 'view' to coodinates local to this view.
+	Transforms `point` in coordinates local to 'view' to coordinates local to this view.
 	
 	@param point The point to transform.
 	
@@ -388,7 +455,14 @@ Class View
 		Return New Recti( TransformPointFromView( rect.min,view ),TransformPointFromView( rect.max,view ) )
 	End
 	
-	#rem monkeydoc @hidden
+	#rem monkeydoc Transforms a point in window coordinates to view coordinates.
+
+	Transforms `point` in window coordinates to coordinates local to this view.
+	
+	@param point The point to transform.
+	
+	@return The transformed point.
+	
 	#end
 	Method TransformWindowPointToView:Vec2i( point:Vec2i )
 	
@@ -397,73 +471,54 @@ Class View
 		Return New Vec2i( Round( t.x ),Round( t.y ) )
 	End
 	
-	
-	#rem monkeydoc Makes this view the 'key' view.
+	#rem monkeydoc Makes this view the key view.
 	
 	The key view is the view that receives keyboard events.
 	
 	#end
 	Method MakeKeyView()
-	
-		If Not ReallyEnabled Return
-	
-		OnMakeKeyView()
-	End
-	
-	#rem monkeydoc @hidden
-	#end
-	Method SendMouseEvent( event:MouseEvent )
-	
-		If Not ReallyEnabled
-			Select event.Type
-			Case EventType.MouseUp,EventType.MouseLeave
-				OnMouseEvent( event )
-			End
-			Return
-		Endif
-	
-		OnMouseEvent( event )
+
+		Local oldKeyView:=App.KeyView
+		If oldKeyView=Self Return
 		
-		If event.Eaten Return
-	
-		Select event.Type
-		Case EventType.MouseWheel
-			Local view:=_parent
-			While view
-				view.OnMouseEvent( event )
-				If event.Eaten Return
-				view=view._parent
-			Wend
-		End
+		If Not Active Return
 		
+		App.KeyView=Self
+		
+		If oldKeyView oldKeyView.OnKeyViewChanged( oldKeyView,Self )
+		
+		OnKeyViewChanged( oldKeyView,Self )
 	End
 	
-	#rem monkeydoc @hidden
+	#rem monkeydoc Sends a key event to the view.
 	#end
 	Method SendKeyEvent( event:KeyEvent )
 	
-		If Not ReallyEnabled Return
-	
-		OnKeyEvent( event )
-	End
-	
-	#rem monkeydoc @hidden
-	#end
-	Property Container:View() Virtual
-	
-		Return Self
+		If _acceptsKeyEvents
+		
+			OnKeyEvent( event )
+			
+			If event.Eaten Return
+		Endif
+		
+		If _parent _parent.SendKeyEvent( event )
 	End
 	
-	#rem monkeydoc @hidden
+	#rem monkeydoc Sends a mouse event to the view.
 	#end
-	Method FindWindow:Window() Virtual
+	Method SendMouseEvent( event:MouseEvent )
 	
-		If _parent Return _parent.FindWindow()
+		If _acceptsMouseEvents
+
+			OnMouseEvent( event.TransformToView( Self ) )
+			
+			If event.Eaten Return
+		Endif
 		
-		Return Null
+		If _parent _parent.SendMouseEvent( event )
 	End
 	
-	#rem monkeydoc @hidden
+	#rem monkeydoc Checks if the view is a child of another view.
 	#end
 	Method IsChildOf:Bool( view:View )
 		
@@ -474,46 +529,70 @@ Class View
 		Return False
 	End
 	
+	#rem monkeydoc @hidden
+	#end
+	Method RequestRender()
+	
+		App.RequestRender()
+	End
+
 	#rem monkeydoc @hidden
 	#end
 	Method InvalidateStyle()
 	
-		_dirty|=Dirty.Style
+		_styleSeq=-1
+		
+		App.RequestRender()
 	End
 	
 	#rem monkeydoc @hidden
 	#end
 	Method ValidateStyle()
 	
-		If Not (_dirty & Dirty.Style) Return
+		If _styleSeq=_themeSeq Return
+		
+		If _styleSeq<>-1
 		
+			Local name:=_style.Name
+			If name
+				Local style:=App.Theme.GetStyle( name )
+				If style _style=style
+			Endif
+
+		Endif
+	
 		_rstyle=_style
 		
-		If Not ReallyEnabled 
-			_rstyle=_style.GetState( "disabled" )
-		Else If _styleState
+		If Enabled
 			_rstyle=_style.GetState( _styleState )
+		Else
+			_rstyle=_style.GetState( "disabled" )
 		Endif
 		
 		_styleBounds=_rstyle.Bounds
-
-		_dirty&=~Dirty.Style
+		
+		_styleSeq=_themeSeq
 				
 		OnValidateStyle()
 	End
 	
+	Method MeasureLayoutSize:Vec2i()
+	
+		Measure()
+		
+		Return _layoutSize
+	End
+	
 	Protected
 	
 	#rem monkeydoc @hidden
 	#end
 	Method Measure()
-	
-		If Not _visible Return
+
+'		If Not _visible Return
 		
 		For Local view:=Eachin _children
-		
 			view.Measure()
-
 		Next
 		
 		ValidateStyle()
@@ -560,6 +639,15 @@ Class View
 			
 			_matrix=_matrix.Translate( 0,(_frame.Height-_bounds.Height)*_gravity.y )
 			
+		Case "fill-y"
+		
+			_rect.max.y=_frame.Height-_styleBounds.Height
+			
+			_bounds.min.y=_rect.min.y+_styleBounds.min.y
+			_bounds.max.y=_rect.max.y+_styleBounds.max.y
+			
+			_matrix=_matrix.Translate( (_frame.Width-_bounds.Width)*_gravity.x,0 )
+			
 		Case "float"
 		
 			_matrix=_matrix.Translate( (_frame.Width-_bounds.Width)*_gravity.x,(_frame.Height-_bounds.Height)*_gravity.y )
@@ -654,61 +742,46 @@ Class View
 		Next
 		
 		canvas.EndRender()
-		
 	End
-	
+
 	Protected
 	
-	#rem monkeydoc @hidden
+	#rem monkeydoc Called during layout if [[Style]] or [[StyleState]] have changed.
+
+	Views can use this method to cache [[RenderStyle]] properties if necessary.
+		
 	#end
 	Method OnValidateStyle() Virtual
-	
 	End
 	
-	#rem monkeydoc @hidden
+	#rem monkeydoc Called during layout to measure the view.
+	
+	Overriding methods should return their preferred content size.
+	
 	#end
 	Method OnMeasure:Vec2i() Virtual
-	
 		Return New Vec2i( 0,0 )
 	End
 	
-	#rem monkeydoc @hidden
-	#end
-	Method OnMeasure2:Vec2i( size:Vec2i ) Virtual
+	#rem monkeydoc Called during layout when the view needs to update its child views.
 	
-		Return New Vec2i( 0,0 )
-	End
+	Overriding methods should set the [[Frame]] property of any child views they are resposible for.
 	
-	#rem monkeydoc @hidden
 	#end
 	Method OnLayout() Virtual
-	
-		For Local view:=Eachin _children
-			view.Frame=Rect
-		Next
-
 	End
 	
-	#rem monkeydoc Render this view.
-	
-	Called when the view should render itself.
-	
+	#rem monkeydoc Called when the view needs to render itself.
 	#end
 	Method OnRender( canvas:Canvas ) Virtual
 	End
 	
-	#rem monkeydoc @hidden
-	#end
-	Method OnRenderBounds( canvas:Canvas ) Virtual
-	End
-	
-	#rem monkeydoc @hidden
-	#end
-	Method OnMakeKeyView() Virtual
+	#rem monkeydoc Called when the key view changes.
 	
-		Local window:=FindWindow()
-		If window window.KeyView=Self
+	This method is invoked on both the old key view and new key view when the key view changes.
 	
+	#end
+	Method OnKeyViewChanged( oldKeyView:View,newKeyView:View ) Virtual
 	End
 	
 	#rem monkeydoc Keyboard event handler.
@@ -727,53 +800,105 @@ Class View
 	Method OnMouseEvent( event:MouseEvent ) Virtual
 	End
 	
-	#rem monkeydoc @hidden
+	#rem monkeydoc The last size returned by OnMeasure.
 	#end
 	Property MeasuredSize:Vec2i()
 	
 		Return _measuredSize
 	End
 	
-	#rem monkeydoc @hidden
+	#rem monkeydoc MeasuredSize plus the current [[RenderStyle]] bounds size.
+	
+	Use this instead of MeasuredSize when calculating layout size for child views.
+	
 	#end
 	Property LayoutSize:Vec2i()
 	
 		Return _layoutSize
 	End
 	
-	#rem monkeydoc @hidden
+	#rem monkeydoc The current [[RenderStyle]] bounds rect.
 	#end
 	Property StyleBounds:Recti()
 	
 		Return _styleBounds
 	End
 	
+	'***** INTERNAL *****
+	
 	#rem monkeydoc @hidden
+	
+	For height-dependant-on-width views - clean me up!
+	
+	#end
+	Method OnMeasure2:Vec2i( size:Vec2i ) Virtual
+		Return New Vec2i( 0,0 )
+	End
+	
+	#rem monkeydoc @hidden
+	
+	For height-dependant-on-width views - clean me up!
+	
 	#end
 	Method Measure2:Vec2i( size:Vec2i )
 		size=OnMeasure2( size-_styleBounds.Size )
 		If size.x And size.y _layoutSize=size+_styleBounds.Size
 		Return _layoutSize
 	End
+
+	#rem monkeydoc @hidden
+	#end
+	Method SetWindow( window:Window )
 	
-	Private
+		_window=window
+		
+		For Local view:=Eachin _children
+			view.SetWindow( window )
+		Next
+	End
 	
-	Enum Dirty
-		Style=1
-		All=1
+	#rem monkeydoc @hidden
+	#end
+	Method UpdateActive()
+	
+		'Note: views are activated top-down, deactivated bottom-up.
+		'	
+		Local active:=_visible And _enabled And _window And (Not _parent Or _parent._active )
+		
+		Local changed:=active<>_active
+		
+		If changed
+			_active=active
+			If Not _active Deactivated()
+		Endif
+		
+		For Local child:=Eachin _children
+			child.UpdateActive()
+		Next
+		
+		If changed And _active Activated()
 	End
 	
-	Field _dirty:Dirty=Dirty.All
-
+	Private
+	
+	Global _themeSeq:Int
+	
+	Field _styleSeq:Int=-1
+	
 	Field _parent:View
+	Field _window:Window
 	Field _children:=New Stack<View>
 	
 	Field _visible:Bool=True
 	Field _enabled:Bool=True
+	Field _active:Bool=False
+	Field _acceptsKeyEvents:Bool=True
+	Field _acceptsMouseEvents:Bool=True
+	
 	Field _style:Style
 	Field _styleState:String
-
-	Field _layout:String
+	
+	Field _layout:String="fill"
 	Field _gravity:=New Vec2f( .5,.5 )
 	Field _offset:=New Vec2i( 0,0 )
 
@@ -782,13 +907,13 @@ Class View
 	
 	Field _frame:Recti
 	
-	'After Measuring...
+	'After measuring...
 	Field _rstyle:Style
 	Field _styleBounds:Recti
 	Field _measuredSize:Vec2i
 	Field _layoutSize:Vec2i
 	
-	'After layout
+	'After layout..
 	Field _rect:Recti
 	Field _bounds:Recti
 	Field _matrix:AffineMat3f
@@ -796,5 +921,4 @@ Class View
 	Field _rbounds:Recti
 	Field _rclip:Recti
 	Field _clip:Recti
-	
 End

+ 97 - 101
modules/mojo/app/window.monkey2

@@ -107,83 +107,29 @@ Class Window Extends View
 	
 	End
 	
-	#rem monkeydoc @hidden
-	#End
-	Method Update()
-
-#If __TARGET__="emscripten"
-
-		'ugly...fixme.
-		Local w:Int,h:Int,fs:Int
-		emscripten_get_canvas_size( Varptr w,Varptr h,Varptr fs )
-		If w<>Frame.Width Or h<>Frame.Height
-			Frame=New Recti( 0,0,w,h )
-		Endif
-	
-#Else
-		'ugly...fixme.
-		If MinSize<>_minSize
-			SDL_SetWindowMinimumSize( _sdlWindow,MinSize.x,MinSize.y )
-			_minSize=GetMinSize()
-			MinSize=_minSize
-		Endif
-		
-		If MaxSize<>_maxSize 
-			SDL_SetWindowMaximumSize( _sdlWindow,MaxSize.x,MaxSize.y )
-			_maxSize=GetMaxSize()
-			MaxSize=_maxSize
-		Endif
-		
-		If Frame<>_frame
-			SDL_SetWindowPosition( _sdlWindow,Frame.X,Frame.Y )
-			SDL_SetWindowSize( _sdlWindow,Frame.Width,Frame.Height )
-			_frame=GetFrame()
-			Frame=_frame
-			_weirdHack=True
-		Endif
-		
-#Endif
-		Measure()
-		
-		UpdateLayout()
-	End
+	Property ContentView:View()
 	
-	#rem monkeydoc @hidden
-	#end
-	Method Render()
+		Return _contentView
 	
-		SDL_GL_MakeCurrent( _sdlWindow,_sdlGLContext )	
-		SDL_GL_SetSwapInterval( _swapInterval )
+	Setter( contentView:View )
 	
-#If __TARGET__="desktop" And __HOSTOS__="windows"
-		If _weirdHack
-			_weirdHack=False
-			SDL_GL_SwapWindow( _sdlWindow )
-		Endif
-#Endif
+		If _contentView RemoveChildView( _contentView )
 		
-		Local bounds:=New Recti( 0,0,Frame.Size )
+		_contentView=contentView
 		
-		_canvas.Resize( bounds.Size )
+		If _contentView AddChildView( _contentView )
 		
-		_canvas.BeginRender( bounds,New AffineMat3f )
-		
-		If _clearEnabled _canvas.Clear( _clearColor )
-		
-		Render( _canvas )
-		
-		_canvas.EndRender()
-		
-		SDL_GL_SwapWindow( _sdlWindow )
 	End
 	
-	#rem monkeydoc @hidden
-	#end
-	Method FindWindow:Window() Override
+	Method UpdateWindow( render:Bool )
 	
-		Return Self
+		LayoutWindow()
+		
+		If render RenderWindow()
 	End
 	
+	'***** INTERNAL *****
+
 	#rem monkeydoc The internal SDL_Window used by this window.
 	#end
 	Property SDLWindow:SDL_Window Ptr()
@@ -218,8 +164,6 @@ Class Window Extends View
 		Return _windowsByID[id]
 	End
 
-	'***** INTERNAL *****
-	
 	#rem monkeydoc @hidden
 	#end
 	Method SendWindowEvent( event:WindowEvent )
@@ -234,32 +178,6 @@ Class Window Extends View
 		OnWindowEvent( event )
 	End
 	
-	#rem monkeydoc @hidden
-	#end
-	Property KeyView:View()
-	
-		Return _keyView
-		
-	Setter( keyView:View )
-	
-		_keyView=keyView
-	End
-	
-	#rem monkeydoc @hidden
-	
-	Dummy default window.
-	
-	#end
-	Method New( title:String,app:AppInstance )
-	
-		_sdlWindow=SDL_CreateWindow( title,0,0,0,0,SDL_WINDOW_HIDDEN|SDL_WINDOW_OPENGL )
-		Assert( _sdlWindow,"FATAL ERROR: SDL_CreateWindow failed" )
-
-		_sdlGLContext=SDL_GL_CreateContext( _sdlWindow )
-		Assert( _sdlGLContext,"FATAL ERROR: SDL_GL_CreateContext failed" )
-		SDL_GL_MakeCurrent( _sdlWindow,_sdlGLContext )	
-	End
-	
 	Protected
 	
 	#rem monkeydoc Window event handler.
@@ -288,14 +206,20 @@ Class Window Extends View
 		
 	End
 	
+	Protected
+	
+	Method OnLayout() Override
+	
+		If _contentView _contentView.Frame=Rect
+	End
+	
 	Private
 	
 	Field _sdlWindow:SDL_Window Ptr
 	Field _sdlGLContext:SDL_GLContext
-
+	
 	Field _flags:WindowFlags
 	Field _fullscreen:=False
-
 	Field _swapInterval:=1
 	
 	Field _canvas:Canvas
@@ -303,8 +227,8 @@ Class Window Extends View
 	Field _clearColor:=Color.Grey
 	Field _clearEnabled:=True
 	
-	Field _keyView:View
-	
+	Field _contentView:View
+
 	Field _minSize:Vec2i
 	Field _maxSize:Vec2i
 	Field _frame:Recti
@@ -335,9 +259,75 @@ Class Window Extends View
 		Return New Recti( x,y,x+w,y+h )
 	End
 	
-	Method Init( title:String,rect:Recti,flags:WindowFlags )
+	Method LayoutWindow()
+	
+#If __TARGET__="emscripten"
+
+		'ugly...fixme.
+		Local w:Int,h:Int,fs:Int
+		emscripten_get_canvas_size( Varptr w,Varptr h,Varptr fs )
+		If w<>Frame.Width Or h<>Frame.Height
+			Frame=New Recti( 0,0,w,h )
+		Endif
+	
+#Else
+		'ugly...fixme.
+		If MinSize<>_minSize
+			SDL_SetWindowMinimumSize( _sdlWindow,MinSize.x,MinSize.y )
+			_minSize=GetMinSize()
+			MinSize=_minSize
+		Endif
+		
+		If MaxSize<>_maxSize 
+			SDL_SetWindowMaximumSize( _sdlWindow,MaxSize.x,MaxSize.y )
+			_maxSize=GetMaxSize()
+			MaxSize=_maxSize
+		Endif
+		
+		If Frame<>_frame
+			SDL_SetWindowPosition( _sdlWindow,Frame.X,Frame.Y )
+			SDL_SetWindowSize( _sdlWindow,Frame.Width,Frame.Height )
+			_frame=GetFrame()
+			Frame=_frame
+			_weirdHack=True
+		Endif
+		
+#Endif
+		Measure()
+		
+		UpdateLayout()
+	End
 	
-		Layout="fill"
+	#rem monkeydoc @hidden
+	#end
+	Method RenderWindow()
+	
+		SDL_GL_MakeCurrent( _sdlWindow,_sdlGLContext )	
+		SDL_GL_SetSwapInterval( _swapInterval )
+	
+#If __TARGET__="desktop" And __HOSTOS__="windows"
+		If _weirdHack
+			_weirdHack=False
+			SDL_GL_SwapWindow( _sdlWindow )
+		Endif
+#Endif
+		
+		Local bounds:=New Recti( 0,0,Frame.Size )
+		
+		_canvas.Resize( bounds.Size )
+		
+		_canvas.BeginRender( bounds,New AffineMat3f )
+		
+		If _clearEnabled _canvas.Clear( _clearColor )
+		
+		Render( _canvas )
+		
+		_canvas.EndRender()
+		
+		SDL_GL_SwapWindow( _sdlWindow )
+	End
+	
+	Method Init( title:String,rect:Recti,flags:WindowFlags )
 	
 		Local x:=(flags & WindowFlags.CenterX) ? SDL_WINDOWPOS_CENTERED Else rect.X
 		Local y:=(flags & WindowFlags.CenterY) ? SDL_WINDOWPOS_CENTERED Else rect.Y
@@ -374,6 +364,12 @@ Class Window Extends View
 		
 		_canvas=New Canvas( _frame.Width,_frame.Height )
 		
-		Update()
+		_clearColor=App.Theme.GetColor( "windowClearColor" )
+		
+		SetWindow( Self )
+		
+		UpdateActive()
+
+		LayoutWindow()
 	End
 End

+ 2 - 9
modules/mojo/docs/module.md

@@ -1,5 +1,5 @@
 
-# The mojo module
+# The mojo module.
 
 The mojo module provides a simple cross platform application framework for writing games and other graphical apps.
 
@@ -9,25 +9,19 @@ The mojo module provides a simple cross platform application framework for writi
 To initialize mojo, you need to do at least 3 things:
 
 * Create an [[app.AppInstance]] using 'New AppInstance'. This will automatically initialize the [[app.App]] global variable.
-
-* Create a [[app.Window]]. You will generally create a subclass of window you have defined yourself, eg: 'New MyWindow'.
-
+* Create an [[app.Window]]. You will generally create a subclass of window you have defined yourself, eg: 'New MyWindow'.
 * Start the app using App.Run().
 
 This can all be done inside Main().
 
-
 Once the app is running, mojo runs a simple event loop that looks like this:
 
 * Get OS events and dispatch corresponding mojo events if necessary.
-
 * Once there are no OS events left to process, any custom [[app.AppInstance.Idle]] handlers are called.
-
 * If the application has called [[app.AppInstance.RequestRender]], then the app is rendered.
 
 The app will continue executing the event loop until [[app.AppInstance.Terminate]] is called.
 
-
 ## Subclassing Window
 
 Subclassing the app Window class is easy - just extend the [[app.Window]] class.
@@ -38,7 +32,6 @@ The OnRender method is called when the app needs to be rendered, and is passed a
 
 In addition, you may also want to override the [[app.Window.OnWindowEvent]] method to handle window close, resize etc events.
 
-
 ## Handling user input
 
 There are 2 main ways to handle user input, via the [[app.View.OnKeyEvent]] and [[app.View.OnMouseEvent]] event handlers or using the

+ 8 - 12
modules/mojo/graphics/canvas.monkey2

@@ -1,9 +1,6 @@
 
 Namespace mojo.graphics
 
-#Import "assets/shader_env.glsl@/mojo"
-#Import "assets/RobotoMono-Regular.ttf@/mojo"
-
 #rem monkeydoc @hidden
 #end	
 Class DrawOp
@@ -20,17 +17,11 @@ Canvas objects are used to perform rendering to either a mojo [[app.View]] or an
 To draw to a canvas, use one of the 'Draw' methods. Drawing is affected by a number of draw states, including:
 
 * [[Color]] - the current drawing color. This is combined with the current alpha to produce the final rendering color and alpha values.
-
 * [[Alpha]] - the current drawing alpha level.
-
 * [[Matrix]] - the current drawing matrix. All drawing coordinates are multiplied by this matrix before rendering.
-
 * [[BlendMode]] - the blending mode for drawing, eg: opaque, alpha, additive, multiply.
-
 * [[Viewport]] - the current viewport. All drawing coordinates are relative to the top-left of the viewport.
-
 * [[Scissor]] - the current scissor rect. All rendering is clipped to the union of the viewport and the scissor rect.
-
 * [[Font]] - The current font to use when drawing text with [[DrawText]].
 
 Drawing does not occur immediately. Drawing commands are 'buffered' to reduce the overhead of sending lots of draw calls to the lower level graphics API. You can force all drawing commands in the buffer to actually render using [[Flush]].
@@ -631,6 +622,11 @@ Class Canvas
 		AddVertex( rect.min.x,rect.max.y,s0,t1 )
 	End
 	
+	Method DrawRect( x:Float,y:Float,width:Float,height:Float,srcImage:Image,srcX:Int,srcY:Int )
+
+		DrawRect( New Rectf( x,y,x+width,y+height ),srcImage,New Recti( srcX,srcY,srcX+width,srcY+height ) )
+	End
+	
 	Method DrawRect( x:Float,y:Float,width:Float,height:Float,srcImage:Image,srcX:Int,srcY:Int,srcWidth:Int,srcHeight:Int )
 
 		DrawRect( New Rectf( x,y,x+width,y+height ),srcImage,New Recti( srcX,srcY,srcX+srcWidth,srcY+srcHeight ) )
@@ -843,8 +839,8 @@ Class Canvas
 		_matrix=matrix
 	End
 
-	Method DrawImage( image:Image,tv:Vec2f,rz:Float,scale:Vec2f )
-		DrawImage( image,tv.x,tv.y,rz,scale.x,scale.y )
+	Method DrawImage( image:Image,translation:Vec2f,rz:Float,scale:Vec2f )
+		DrawImage( image,translation.x,translation.y,rz,scale.x,scale.y )
 	End
 	
 	#rem monkeydoc Draws text.
@@ -975,7 +971,7 @@ Class Canvas
 			Local env:=stringio.LoadString( "asset::mojo/shader_env.glsl" )
 			_ambientEnv=New ShaderEnv( "#define RENDERPASS_AMBIENT~n"+env )
 '			_ambientEnv=New ShaderEnv( "#define RENDERPASS_NORMAL~n"+env )
-			_defaultFont=Font.Load( "asset::mojo/RobotoMono-Regular.ttf",16 )
+			_defaultFont=Font.Load( "font::DejaVuSans.ttf",16 )
 			_nullShader=Shader.GetShader( "null" )
 		Endif
 

+ 9 - 11
modules/mojo/graphics/shader.monkey2

@@ -1,11 +1,6 @@
 
 Namespace mojo.graphics
 
-#Import "assets/shader_sprite.glsl@/mojo"
-#Import "assets/shader_phong.glsl@/mojo"
-#Import "assets/shader_font.glsl@/mojo"
-#Import "assets/shader_null.glsl@/mojo"
-
 Private
 
 Function BindUniforms( uniforms:Uniform[],params:ParamBuffer,filter:Bool )
@@ -25,13 +20,16 @@ Function BindUniforms( uniforms:Uniform[],params:ParamBuffer,filter:Bool )
 			Local tex:=p.texture
 			DebugAssert( tex,"Can't bind shader texture uniform '"+u.name+"' - no texture!" )
 			glActiveTexture( GL_TEXTURE0+u.texunit )
-			glBindTexture( GL_TEXTURE_2D,tex.GLTexture )
-			If (tex.Flags & TextureFlags.Filter) And filter
-				glTexParameteri( GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR )
-			Else
-				glTexParameteri( GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST )
+			Local gltex:=tex.GLTexture
+			If gltex
+				glBindTexture( GL_TEXTURE_2D,tex.GLTexture )
+				If (tex.Flags & TextureFlags.Filter) And filter
+					glTexParameteri( GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR )
+				Else
+					glTexParameteri( GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST )
+				Endif
+				glUniform1i( u.location,u.texunit )
 			Endif
-			glUniform1i( u.location,u.texunit )
 		Default
 			Assert( False,"Unsupported uniform type for param:"+u.name )
 		End

+ 2 - 1
modules/mojo/graphics/texture.monkey2

@@ -153,7 +153,8 @@ Class Texture
 	#rem monkeydoc @hidden
 	#end	
 	Property GLTexture:GLuint()
-		DebugAssert( Not _discarded,"texture has been discarded" )
+		If _discarded Return 0
+'		DebugAssert( Not _discarded,"texture has been discarded" )
 	
 		If _texSeq=glGraphicsSeq And Not _texDirty And Not _mipsDirty Return _glTexture
 		

+ 3 - 0
modules/mojo/module.json

@@ -1,5 +1,8 @@
 {
 	"module":"mojo",
+	"about":"Monkey2 app framework",
+	"author":"Mark Sibly",
 	"version":"1.0.0",
+	"support":"http://monkey2.monkey-x.com",
 	"depends":["freetype","emscripten","std","sdl2","gles20","openal"]
 }

+ 23 - 0
modules/mojo/mojo.monkey2

@@ -1,6 +1,8 @@
 
 Namespace mojo
 
+#Import "assets/"
+
 #Import "<emscripten>"
 #Import "<std>"
 #Import "<sdl2>"
@@ -11,6 +13,9 @@ Namespace mojo
 #Import "app/event"
 #Import "app/skin"
 #Import "app/style"
+#Import "app/theme"
+#import "app/jsonify"
+
 #Import "app/view"
 #Import "app/window"
 #Import "app/glwindow"
@@ -47,4 +52,22 @@ Using mojo..
 Private
 
 Function Main()
+
+	Stream.OpenFuncs["font"]=Lambda:Stream( proto:String,path:String,mode:String )
+	
+		Return Stream.Open( "asset::fonts/"+path,mode )
+	End
+		
+	Stream.OpenFuncs["image"]=Lambda:Stream( proto:String,path:String,mode:String )
+	
+		Return Stream.Open( "asset::images/"+path,mode )
+	End
+
+	Stream.OpenFuncs["theme"]=Lambda:Stream( proto:String,path:String,mode:String )
+	
+		If Not App Or Not App.Theme Or Not App.Theme.Path Return Null
+
+		Return Stream.Open( ExtractDir( App.Theme.Path )+path,mode )
+	End
+	
 End

+ 6 - 0
modules/mojo/process/native/process.cpp

@@ -34,6 +34,8 @@ struct semaphore{
 #include <windows.h>
 #include <tlhelp32.h>
 
+extern "C" WINBASEAPI WINBOOL WINAPI CancelIoEx( HANDLE hFile,LPOVERLAPPED lpOverlapped );
+
 #else
 
 #include <sys/ioctl.h>
@@ -451,7 +453,11 @@ void bbProcess::terminate(){
 	if( !_rep ) return;
 
 #if _WIN32
+
 	TerminateProcessGroup( _rep->proc,-1 );
+	
+	CancelIoEx( _rep->out,0 );
+
 #else
 	killpg( _rep->proc,SIGTERM );
 #endif

+ 2 - 2
modules/mojo/process/process.monkey2

@@ -1,9 +1,9 @@
 
-Namespace mojo.process
-
 #Import "native/process.h"
 #Import "native/process.cpp"
 
+Namespace mojo.process
+
 Extern
 
 Class Process="bbProcess"

+ 4 - 1
modules/mojo/requesters/native/requesters.cpp

@@ -192,7 +192,10 @@ bbString bbRequesters::RequestDir( bbString title,bbString dir ){
 
 void bbRequesters::OpenUrl( bbString url ){
 
-	ShellExecute( HWND_DESKTOP,"open",url.c_str(),0,0,SW_SHOWNORMAL );
+	CoInitializeEx( NULL,COINIT_APARTMENTTHREADED|COINIT_DISABLE_OLE1DDE );
+	
+	ShellExecute( HWND_DESKTOP,0,url.c_str(),0,0,SW_SHOWNORMAL );
+//	ShellExecute( HWND_DESKTOP,"open",url.c_str(),0,0,SW_SHOWNORMAL );
 }
 
 #elif __linux