About Monkey 2 › Forums › Monkey 2 Programming Help › MojoX GUI questions
This topic contains 10 replies, has 3 voices, and was last updated by
Ethernaut
1 year, 7 months ago.
-
AuthorPosts
-
September 2, 2017 at 6:42 am #10203
I’ve just started getting my feet wet in MojoX so I can create some content editors, and have some questions. There aren’t a lot of learning resources other than the examples and something like the source for Ted2, so I still can’t figure out:
- In the code below, how would you make edge between the two views “draggable”, so that the relative size of each one changes? The “resizable” argument on AddView only seems to work if I specify the size in pixels, but then each view has its own independent size, instead of being a percentage of the parent view. It works in Ted2Go – the “_browsersTabView” has the behavior I want – but I can’t figure out how
- In my GraphView class, the “OnMouseEvent” method should scroll based on the mouse wheel X and Y axis (two fingers drag on trackpad). But setting Scroll returns without doing anything, even though I added some content to the view. What am I missing?
- Again on GraphView, how would I add more than one visible item to it, if ContentView only supports one view object? I want to add several “Nodes” I can drag around and connect, and I imagine I’ll extend the View class (or maybe Button) to create the draggable/clickable nodes.
Monkey123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596#Import "<mojo>"#Import "<mojox>"Using std..Using mojo..Using mojox..Function Main()New AppInstanceNew TestGuiApp.Run()EndClass TestGui Extends WindowField mainView:DockingViewField gameView:GameViewField graphView:GraphViewConst smallFont:Font = Font.Load( "font::DejaVuSans.ttf", 10 )Method New()Super.New( "Test", 1024, 640, WindowFlags.Resizable )'Child viewsgameView=New GameView()graphView = New GraphView()'Main ViewmainView = New DockingViewmainView.AddView( gameView, "left", "60%", True )mainView.AddView( graphView, "right", "40%", True )ContentView=mainViewEndEndClass GraphView Extends ScrollViewField panSpeed := 2.5Method New()Layout="fill"ScrollBarsVisible = TrueLocal newStyle := Style.Copy()newStyle.BackgroundColor = Color.GreynewStyle.BorderColor = Color.BlacknewStyle.Font = TestGui.smallFontnewStyle.TextColor = Color.DarkGreyStyle = newStyleLocal button:=New PushButton( "Test" )button.Layout="float"button.Gravity=New Vec2f( 0.5 )button.MaxSize=New Vec2i( 100,0 )ContentView=buttonEndMethod OnRender( canvas:Canvas ) Overridecanvas.DrawText( "graph:", 5, 5 )Super.OnRender( canvas )canvas.DrawCircle( 100, 200, 25 )EndMethod OnMouseEvent( event:MouseEvent ) OverrideSelect event.TypeCase EventType.MouseWheelScroll.X += ( event.Wheel.X * panSpeed )Scroll.Y -= ( event.Wheel.Y * panSpeed )App.RequestRender()EndEndEndClass GameView Extends ViewField text:StringMethod New()Layout="fill"Local newStyle := Style.Copy()newStyle.BackgroundColor = Color.DarkGreynewStyle.BorderColor = Color.BlacknewStyle.TextColor = Color.BlacknewStyle.Font = TestGui.smallFontStyle = newStyleEndMethod OnRender( canvas:Canvas ) OverrideSuper.OnRender( canvas )RequestRender()canvas.DrawText( "gameview:" + App.FPS + " fps", 5, 5 )canvas.DrawCircle( 200, 200, 50 )EndEndThanks!
September 2, 2017 at 9:33 am #10205- Resizable doesn’t work with persentage widths. There is pixel-sized _browsersTabView in ted2go
- To make scrolling :
- set button size more than view to enable scrolling: button.MinSize=New Vec2i( 0,1000 )
- change scroll like this: Scroll = Scroll + New Vec2i( event.Wheel.X * panSpeed, -event.Wheel.Y * panSpeed )
- assigning a value to Scroll.X is a mistake, because when you get access to Scroll variable you get copy of vec2 – say “hello” to struct type!
- in my case you assign whole vec2 and get right result.
- You can extends GraphView with DockingView. Or use internal docking view ‘wrapped’ with scrollview to manage views and have scrolling stuf.
- I am thinking about writing own gui system – I like the QT widgets layout, maybe I can implement some similar stuff.
What I like in QT and missed in monkey – SizePolicy basis, so you can just set sizePolicy=SizePolicy.Expanding and view will expand to all empty space, etc.
But it requires to write more complex and difficult layout system.
Also I tried to use operator += to add views, looks good.
Monkey12345678910111213141516171819Class VerticalLayout Extends DockingViewMethod New( topToBottom:Bool=True )_topToBottom=topToBottomLayout="fill-y"EndOperator += :Void( view:View )Self.AddView( view,_topToBottom ? "top" Else "bottom" )EndPrivateField _topToBottom:BoolEndand usage is
Monkey1234Local lay:=New VerticalLayoutlay += New Label( "Line 1" )lay += New Label( "Line 2" )lay += New Label( "Line 3" )September 2, 2017 at 9:14 pm #10217The use of percentages in docking view means the pane is always that percentage of docking view size, so absolute size will only change if docking view size changes. So a pane that is 25% is always 25%. If you change sizes to pixel size (eg: “60”) it works the way you want, although DockingView is really designed to have a ContentView so may have a ‘hole’ in the middle. I agree percentage sizes should probably have a resize knob too if ‘resizable=true’ and the percentage should mean ‘initial’ size. Will have a look at fixing this.
Another thing you can do is to just keep adding stuff to the “left” of a DockingView so it’s ‘stacks up’ left to right. Then, if you make the last view the ‘ContentView’ you can completely fill up the DockingView as the content view fills up the space left over after all ‘docks’ are added. Just noticed nerobot suggested something similar with VerticalLayout…
Your scrollview isn’t scrolling at least because it doesn’t have any content – you need to set the ContentView property. The content view of a scroll view should also have an OnMeasure method as ScrollView uses content view’s MeasuredSize to determine how large scrollable content is, how large to make scroll bars etc.
Here’s a little scroll view demo showing the basics. Note this doesn’t extend ScrollView as there’s little point here, although if you want to customize scroll wheel handling etc you can do so. Scroll wheel handling in ScrollView uses font height for scrolling I think.
Monkey1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950#Import "<mojo>"#Import "<mojox>"Using std..Using mojo..Using mojox..Function Main()New AppInstanceNew TestGuiApp.Run()EndClass TestGui Extends WindowField gameView:GameViewField scrollView:ScrollViewMethod New()Super.New( "Test", 1024, 640, WindowFlags.Resizable )gameView=New GameViewscrollView=New ScrollViewscrollView.ContentView=gameViewContentView=scrollViewEndEndClass GameView Extends ViewField text:StringMethod New()EndMethod OnMeasure:Vec2i() OverrideReturn New Vec2i( 1920,1080 )EndMethod OnRender( canvas:Canvas ) OverrideRequestRender()canvas.DrawText( "gameview:" + App.FPS + " fps, Width="+Width+", Height="+Height, 5, 5 )canvas.DrawCircle( 200, 200, 50 )EndEndSeptember 3, 2017 at 2:17 am #10221I got the scrolling and the resizing working perfectly, thanks for the help!
I agree percentage sizes should probably have a resize knob too if ‘resizable=true’ and the percentage should mean ‘initial’ size. Will have a look at fixing this.
That seems like it would be really helpful, thanks Mark!
Here’s the revised example. Only one thing I’m not being able to get right: I’d like the initial Scroll coordinates to be centered in the frame when the App starts, but setting Scroll in New() seems to have no effect. I could make a ‘_firstFrame:Bool” field and check for it in OnRender, but it feels like it would be unnecessary clutter if there’s a place where Scroll can be initialized. Where is it?
Monkey123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111#Import "<mojo>"#Import "<mojox>"Using std..Using mojo..Using mojox..Function Main()New AppInstanceNew TestGuiApp.Run()EndClass TestGui Extends WindowField mainDock:DockingViewField rgtDock:ScrollViewField graphView:GraphViewConst smallFont:Font = Font.Load( "font::DejaVuSans.ttf", 10 )Method New()Super.New( "Test", 1024, 640, WindowFlags.Resizable )mainDock = New MainDock()rgtDock = New RightDock()mainDock.AddView( rgtDock, "right", "400", True )ContentView = mainDockEndEndClass MainDock Extends DockingViewMethod New()Layout="fill"Local newStyle := Style.Copy()newStyle.BackgroundColor = Color.DarkGreynewStyle.BorderColor = Color.BlacknewStyle.Font = TestGui.smallFontStyle = newStyleEndMethod OnRender( canvas:Canvas ) OverrideSuper.OnRender( canvas )canvas.Color = New Color( Rnd(), Rnd(), Rnd() )canvas.DrawCircle( 200, 200, 50 )canvas.Color = Color.Aluminumcanvas.DrawText( "gameview:" + App.FPS + " fps", 5, 5 )EndEndClass RightDock Extends ScrollViewPrivateField _panSpeed := 5.0PublicMethod New()Layout="fill"ScrollBarsVisible = TrueLocal newStyle := Style.Copy()newStyle.BackgroundColor = Color.GreynewStyle.BorderColor = Color.BlacknewStyle.Font = TestGui.smallFontStyle = newStyleLocal graph:=New GraphViewContentView = graphScroll = New Vec2i( graph.Frame.Width/2, graph.Frame.Height/2 ) 'Doesn't work!EndMethod OnRender( canvas:Canvas ) OverrideSuper.OnRender( canvas )canvas.Color = Color.Aluminumcanvas.DrawText( "size:" + Frame + " ,scroll:" + Scroll , 5, 5 )EndMethod OnMouseEvent( event:MouseEvent ) OverrideSelect event.TypeCase EventType.MouseWheelScroll = New Vec2i( Scroll.X+(event.Wheel.X*_panSpeed), Scroll.Y-(event.Wheel.Y*_panSpeed) )App.RequestRender()EndEndEndClass GraphView Extends ViewPrivateField _panSpeed := 5.0Field _size := New Vec2i( 1024, 1024 )PublicMethod New()MinSize=New Vec2i( _size.X, _size.Y )EndMethod OnRender( canvas:Canvas ) OverrideLocal r:= 20.0For Local x := 1 Until 10For Local y := 1 Until 10Local v := (x/10.0) -0.05canvas.Color = New Color( v, v, v )canvas.DrawCircle( (x*100)+r, (y*100)+r, r )NextNextEndEndSeptember 3, 2017 at 4:31 am #10225There is my ‘improved’ version
Monkey1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071Class RightDock Extends ScrollViewPrivateField _panSpeed := 5.0Field _prevSize:Vec2iMethod FirstGrabSizes( newSize:Vec2i,prevSize:Vec2i )Print "sizes: "+newSize+", "+prevSize'Scroll = newSize * 0.5Scroll = AvailableScroll * 0.5SizeChanged-=FirstGrabSizes ' unsubscribesEnd' missed this method in mojoxProperty AvailableScroll:Vec2i()' also need to calc paddings, etcLocal xx:=Max( 0,MeasuredSize.x-VisibleRect.Width )Local yy:=Max( 0,MeasuredSize.y-VisibleRect.Height )Return New Vec2i( xx,yy )EndPublicField SizeChanged:Void( newSize:Vec2i,prevSize:Vec2i )Method New()Layout="fill"ScrollBarsVisible = TrueLocal newStyle := Style.Copy()newStyle.BackgroundColor = Color.GreynewStyle.BorderColor = Color.BlacknewStyle.Font = TestGui.smallFontStyle = newStyleLocal graph:=New GraphViewContentView = graph' subscribes to changesSizeChanged+=FirstGrabSizesEndMethod OnRender( canvas:Canvas ) OverrideSuper.OnRender( canvas )canvas.Color = Color.Aluminumcanvas.DrawText( "size:" + Frame + " ,scroll:" + Scroll , 5, 5 )EndProtectedMethod OnMouseEvent( event:MouseEvent ) OverrideSelect event.TypeCase EventType.MouseWheelScroll = New Vec2i( Scroll.X+(event.Wheel.X*_panSpeed), Scroll.Y-(event.Wheel.Y*_panSpeed) )App.RequestRender()EndEnd' store prev sizeMethod OnMeasure:Vec2i() Override_prevSize=MeasuredSizeReturn Super.OnMeasure()End' check new size and fire SizeChanged event if necessaryMethod OnLayout() OverrideSuper.OnLayout()Local cur:=MeasuredSizeIf cur<>_prevSize Then SizeChanged( cur,_prevSize )EndEndAs for me, SizeChanged event should be a mojox core part.
Also I missed AvailableScroll:Vec2i property in ScrollView – to let us know is there scroll available and how much.
Above is a way how my BlaBlaViewExt were borned in ted2go.
September 3, 2017 at 4:35 am #10226Here is splitted-by-classes version:
Monkey1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980Class RightDock Extends ScrollViewExtPrivateMethod FirstGrabSizes( newSize:Vec2i,prevSize:Vec2i )Print "sizes: "+newSize+", "+prevSize'Scroll = newSize * 0.5Scroll = AvailableScroll * 0.5SizeChanged-=FirstGrabSizes ' unsubscribesEndPublicMethod New()Layout="fill"ScrollBarsVisible = TrueLocal newStyle := Style.Copy()newStyle.BackgroundColor = Color.GreynewStyle.BorderColor = Color.BlacknewStyle.Font = TestGui.smallFontStyle = newStyleLocal graph:=New GraphViewContentView = graph' subscribes to changesSizeChanged+=FirstGrabSizesEndMethod OnRender( canvas:Canvas ) OverrideSuper.OnRender( canvas )canvas.Color = Color.Aluminumcanvas.DrawText( "size:" + Frame + " ,scroll:" + Scroll , 5, 5 )EndEndClass ScrollViewExt Extends ScrollViewPrivateField _prevSize:Vec2iField _panSpeed := 5.0' missed this method in mojoxProperty AvailableScroll:Vec2i()' also need to calc paddings, etcLocal xx:=Max( 0,MeasuredSize.x-VisibleRect.Width )Local yy:=Max( 0,MeasuredSize.y-VisibleRect.Height )Return New Vec2i( xx,yy )EndPublicField SizeChanged:Void( newSize:Vec2i,prevSize:Vec2i )ProtectedMethod OnMouseEvent( event:MouseEvent ) OverrideSelect event.TypeCase EventType.MouseWheelScroll = New Vec2i( Scroll.X+(event.Wheel.X*_panSpeed), Scroll.Y-(event.Wheel.Y*_panSpeed) )App.RequestRender()EndEnd' store prev sizeMethod OnMeasure:Vec2i() Override_prevSize=MeasuredSizeReturn Super.OnMeasure()End' check new size and fire SizeChanged event if necessaryMethod OnLayout() OverrideSuper.OnLayout()Local cur:=MeasuredSizeIf cur<>_prevSize Then SizeChanged( cur,_prevSize )EndEndSeptember 3, 2017 at 5:24 am #10228Hi,
Here’s my little hack for initial auto-centering. Just replace RightDock.OnRender wth this:
Monkey1234567891011Method OnRender( canvas:Canvas ) OverrideSuper.OnRender( canvas )canvas.Color = Color.Aluminumcanvas.DrawText( "size:" + Frame + " ,scroll:" + Scroll , 5, 5 )Global _centered:BoolIf _centered ReturnScroll=(ContentView.MeasuredSize-VisibleRect.Size)/2_centered=TrueEndOK, it’s still not all that pretty, and also relies on a ‘done’ style flag. This is alas not easy/possible to do in the ctor because the gui has not been laid out yet – layout is done just before render so while ctor is executing, nothing has been measured/positioned yet.
I chose this design to minimize layout overhead and to simplify things. Another approach is to, as nerobot says, make views update layout in response to resize events, but in my experience this can lead to considerable layout overhead, and even layout ‘lockups’, esp. in the case of complex hierarchies as one resize can trigger lots of layout code. I’m not adverse to adding a SizeChanged event, but I am against the idea of it being used to trigger layout.
Mojox uses a pretty simple layout system (largely pinched form Android). Layout is a 2 pass process and is always performed just before rendering (could be optimized/improved here). In the first pass, views are measured ‘bottom up’ using OnMeasure. This initializes their MeasuredSize property. Since measuring is bottom up, parent views can therefore depend on children having already been measured when measuring themselves. In the second pass, views are positioned/laid out ‘top down’. This way, everything is measured *exactly* once, and positioned *exactly* once. Also, properties like ‘Rect’ and ‘Frame’ etc are updated once and always in sync with each other and their true ‘render position’.
There are of course some limitations and issues with the mojox approach. Layout of things like wordwrapped text or html views is tricky, because view height is dependant on view width, and there are some hacks in there for dealing with that . And having layout occur ‘at some point in the future’ can sometimes be inconvenient, but in my experience the benefits are worth it. YMMV.
September 3, 2017 at 5:34 am #10229Thanks for explanation, and for ‘global’ inside of method body.
September 3, 2017 at 5:45 am #10230Actually, ‘field’ inside of method would be cool!
September 3, 2017 at 6:16 am #10231Also, forgot about UpdateWindow(). Add this to the end of TestGui ctor:
Monkey123UpdateWindow( False )rgtDock.Scroll=(rgtDock.ContentView.MeasuredSize-rgtDock.VisibleRect.Size)/2UpdateWindow causes a full window layout update so should be used sparingly, but I think its the right solution in this case.
September 3, 2017 at 8:02 am #10234You guys are amazing!
I’m enjoying MojoX now that I understand it better, and diving into it led me to rework my “Game” class so it extends View instead of Window. That way I can more easily stick it into a GUI with all kinds of docks and toolbars, and even have two or more different instances of that class, each one showing something different, in the same interface.
Thanks!
-
AuthorPosts
You must be logged in to reply to this topic.