ProGUI V3 Alpha 3 Ready for testing!

Developed or developing a new product in PureBasic? Tell the world about it.
PrincieD
Addict
Addict
Posts: 858
Joined: Wed Aug 10, 2005 2:08 pm
Location: Yorkshire, England
Contact:

Re: ProGUI V3 Alpha 3 Ready for testing!

Post by PrincieD »

If for example you had a widget that renders multiple parts of the widget (inside the DrawEvent handler) and not sub widgets (nested layouts). The skin defination could look something like this:

Code: Select all

SkinSetValue("testWidget", "header.background-color", "green")
SkinSetValue("testWidget", "hover.header.background-color", "#FF0000")
SkinSetValue("testWidget", "active.header.background-color", "rgba(0, 0, 255, 150)")
and inside the DrawEvent handler:

Code: Select all

Procedure drawTestWidget(Widget, EventType, *eventData.PG_EventDraw)
    
    WidgetGetSkinColor(Widget, "header.background-color", @color.l, @opacity.f)
    
    DrawBox(0, 0, *eventData\width, *eventData\height, color, opacity)
    
EndProcedure
So a lot of versatility :)

Or it could be:

Code: Select all

SkinSetValue("testWidget", "bigFishLittleFish.some-color", "green")

Code: Select all

Procedure drawTestWidget(Widget, EventType, *eventData.PG_EventDraw)
    
    WidgetGetSkinColor(Widget, "bigFishLittleFish.some-color", @color.l, @opacity.f)
    
    DrawBox(0, 0, *eventData\width, *eventData\height, color, opacity)
    
EndProcedure
The idea is that custom widgets and custom skin properties are dynamic (you only need to have a correlation between the skin and what you have in the DrawEvent handler for the widget)
Last edited by PrincieD on Sat Jan 11, 2025 1:58 am, edited 2 times in total.
ProGUI - Professional Graphical User Interface Library - http://www.progui.co.uk
PrincieD
Addict
Addict
Posts: 858
Joined: Wed Aug 10, 2005 2:08 pm
Location: Yorkshire, England
Contact:

Re: ProGUI V3 Alpha 3 Ready for testing!

Post by PrincieD »

This system also lends itself nicely for transition animations between states, the Widget DrawEvent handler doesn't have to worry about any state transition animations because the WidgetGetSkinColor() will return the current interpolatated values, so the DrawEvent handler is unchanged:

Code: Select all

Procedure drawTestWidget(Widget, EventType, *eventData.PG_EventDraw)
    
    WidgetGetSkinColor(Widget, "background-color", @color.l, @opacity.f)
    
    DrawBox(0, 0, *eventData\width, *eventData\height, color, opacity)
    
EndProcedure
Keeps things really simple for creating your own widgets (and for me creating the 'Pro' widgets :lol:)

For handling Img cross fade between states I'll probably introduce some new gfx API commands such as DrawWidgetSkinBackground(), DrawWidgetSkinImg()

Widget borders will be handled automatically so you don't have to worry about that.
ProGUI - Professional Graphical User Interface Library - http://www.progui.co.uk
PrincieD
Addict
Addict
Posts: 858
Joined: Wed Aug 10, 2005 2:08 pm
Location: Yorkshire, England
Contact:

Re: ProGUI V3 Alpha 3 Ready for testing!

Post by PrincieD »

The skin engine is working really well so far! there's now a skin cache per widget too so a single map lookup for the stateProperty if the skin hasn't changed or the widget's class path. The following is a basic example with a mouse hover event:

Code: Select all

Global NewMap layoutmap.s()

#ProGUI_UseDll = 0;#True

CompilerIf Not #PB_Compiler_Thread
    MessageRequester("ProGUI", "Please enable threadsafe executable", #PB_MessageRequester_Ok)
    End
CompilerEndIf

CompilerIf #PB_Compiler_DPIAware
    MessageRequester("ProGUI", "Please disable DPI aware executable, ProGUI handles per-monitor DPI", #PB_MessageRequester_Ok)
    End
CompilerEndIf

CompilerIf #ProGUI_UseDll
    IncludeFile "ProGUI_PB.pbi"
CompilerElse
    IncludeFile "ProGUI.pbi"
CompilerEndIf

driver = #PG_RenderDirect2D
;driver = #PG_RenderVector
;driver = #PG_RenderCairo

StartProGUI(driver)

Procedure draw(window, EventType, *eventData.PG_EventDraw)
    
    ;DrawClear(0, 0)
    DrawClear(RGB(240, 240, 240), 1)
    
EndProcedure

Procedure drawTestWidget(Widget, EventType, *eventData.PG_EventDraw)
    
    WidgetGetSkinColor(Widget, "background-color", @color.l, @opacity.f)
    
    DrawBox(0, 0, *eventData\width, *eventData\height, color, opacity)
    
EndProcedure

Procedure testWidgetMouse(Widget, EventType, *eventData.PG_EventMouse)
    
    Select EventType
            
        Case #PG_Event_MouseEnter
            
            WidgetSetSkinState(Widget, "hover")
            
        Case #PG_Event_MouseLeave
            
            WidgetSetSkinState(Widget)
            
    EndSelect
    
EndProcedure

Procedure CreateTestWidget()
    
    *widget = CreateWidget(0, 0, 350, 0, 0, #PG_Widget_LayoutFlex)
    WidgetSetClass(*widget, "testContainer")
    
    AddEventHandler(*widget, #PG_Event_Draw, @drawTestWidget())
    AddEventHandler(*widget, #PG_Event_MouseEnter, @testWidgetMouse())
    AddEventHandler(*widget, #PG_Event_MouseLeave, @testWidgetMouse())
    
    LayoutFlexSetDirection(0, #PG_Flex_Direction_Row)
    LayoutFlexSetWrap(0, #PG_Flex_Wrap_Enabled)
    
    ProcedureReturn *widget
    
EndProcedure

Procedure CreateTestWidget2()
    
    LayoutPush()
    
    *widget = CreateWidget(0, 0, 200, 200, 0, #PG_Widget_LayoutFlex)
    WidgetSetClass(*widget, "testContainer2")
    
    AddEventHandler(*widget, #PG_Event_Draw, @drawTestWidget())
    
    LayoutFlexSetDirection(0, #PG_Flex_Direction_Row)
    LayoutFlexSetWrap(0, #PG_Flex_Wrap_Enabled)
    
    *widget = CreateWidget(0, 0, 100, 100, 0)
    WidgetSetClass(*widget, "testWidget2")
    
    AddEventHandler(*widget, #PG_Event_Draw, @drawTestWidget())
    AddEventHandler(*widget, #PG_Event_MouseEnter, @testWidgetMouse())
    AddEventHandler(*widget, #PG_Event_MouseLeave, @testWidgetMouse())
    
    LayoutPop()
    
    ProcedureReturn *widget
    
EndProcedure

Global SignalQuit
Procedure WindowCloseEventHandler(Window, EventType, *eventData, *userData)
    
    SignalQuit = #True
    
EndProcedure

OpenConsole()

window = CreateWindow(0, 0, 800, 600, "Test", #PG_Window_MinimizeWidget | #PG_Window_MaximizeWidget | #PG_Window_Sizeable)
;window = CreateWindow(0, 0, 800, 600, "Test", #PG_Window_BorderLess | #PG_Window_Sizeable, 0)

SkinSetValue("testContainer", "background-color", "orange")
SkinSetValue("testContainer", "hover.background-color", "darkorange")
SkinSetValue("dark:testContainer", "background-color", "gray")
SkinSetValue("testContainer.testWidget", "background-color", "red")
SkinSetValue("testContainer.testWidget", "hover.background-color", "purple")
SkinSetValue("testWidget", "background-color", "green")

SkinSetValue("testContainer2", "background-color", "blue")
SkinSetValue("testContainer2.testWidget2", "background-color", "yellow")
SkinSetValue("testContainer2.testWidget2", "hover.background-color", "purple")

;SkinSetValue("testContainer.testContainer2", "background-color", "grey")
;SkinSetValue("testContainer.testContainer2.testWidget2", "background-color", "brown")


layout = LayoutGetCurrent()
LayoutSetType(layout, #PG_Layout_Type_Flex)
LayoutFlexSetDirection(layout, #PG_Flex_Direction_Row)
LayoutSetPadding(layout, 20)
;LayoutSetPaddingLeft(layout, 0)
;LayoutSetPaddingRight(layout, 0)
;LayoutSetPaddingTop(layout, 0)
;LayoutSetPaddingBottom(layout, 0)
;LayoutFlexSetJustify(layout, #PG_Flex_Justify_Center)
;LayoutFlexSetAlignItems(layout, #PG_Flex_AlignItems_End)
;LayoutFlexSetWrap(layout, #PG_Flex_Wrap_Enabled)
;LayoutFlexSetAlignContent(layout, #PG_Flex_AlignContent_SpaceBetween)

itemMargin = 0

widget = CreateWidget(50, 50, 150, 150)
WidgetSetMinWidth(widget, 100)
WidgetSetMinHeight(widget, 50)
WidgetSetMargin(widget, itemMargin)

CreateTestWidget2()

widget2 = CreateWidget(160, 50, 150, 150)
WidgetSetClass(widget2, "testWidget")
AddEventHandler(widget2, #PG_Event_Draw, @drawTestWidget())
WidgetSetMargin(widget2, itemMargin)

widget3 = CreateWidget(270, 50, 150, 150)
WidgetSetMargin(widget3, itemMargin)

widget5 = CreateWidget(380, 50, 150, 150)
WidgetSetMargin(widget5, itemMargin)

testWidget = CreateTestWidget()
WidgetSetSkin(testWidget, testSkin)
WidgetSetMinWidth(testWidget, 100)
WidgetSetHeight(testWidget, #PG_Widget_FitContent)
WidgetSetMargin(testWidget, itemMargin)
layout2 = LayoutGetCurrent()

LayoutSetCurrent(layout)

widget6 = CreateWidget(50, 50, 150, 100)
WidgetSetMinWidth(widget6, 80)
WidgetSetMargin(widget6, itemMargin)

widget7 = CreateWidget(160, 50, 0, 0)
WidgetSetMinWidth(widget7, 200)
WidgetSetMargin(widget7, itemMargin)
WidgetSetMinHeight(widget7, 50)

widget8 = CreateWidget(380, 50, 150, 100)
WidgetSetMinWidth(widget8, 50)
WidgetSetMargin(widget8, itemMargin)

LayoutSetCurrent(layout2)
For n = 1 To 10;000
    
    widget = CreateWidget(380, 50, Random(250), 0)
    WidgetSetClass(widget, "testWidget")
    AddEventHandler(widget, #PG_Event_Draw, @drawTestWidget())
    WidgetSetMinWidth(widget, 50)
    WidgetSetMinHeight(widget, 50)
    WidgetSetMargin(widget, 5)
    
Next

CreateTestWidget2()

AddEventHandler(window, #PG_Event_Draw, @draw())
AddEventHandler(window, #PG_Event_WindowClose, @WindowCloseEventHandler())

WindowShow(window)

Repeat
    
    Event = WaitWindowEvent()
    
Until Event = #PB_Event_CloseWindow Or Event = #PB_Event_RightClick Or SignalQuit

StopProGUI()
ProGUI - Professional Graphical User Interface Library - http://www.progui.co.uk
User avatar
blueb
Addict
Addict
Posts: 1111
Joined: Sat Apr 26, 2003 2:15 pm
Location: Cuernavaca, Mexico

Re: ProGUI V3 Alpha 3 Ready for testing!

Post by blueb »

IDE Log...
[COMPILER] Line 38: WidgetGetSkinColor() is not a function, array, list, map or macro.

Checked both the DLL and the .PBI file and the above doesn't exist. Any clues?

I downloaded the latest zip from the first page, and the results were the same.
- It was too lonely at the top.

System : PB 6.21(x64) and Win 11 Pro (x64)
Hardware: AMD Ryzen 9 5900X w/64 gigs Ram, AMD RX 6950 XT Graphics w/16gigs Mem
PrincieD
Addict
Addict
Posts: 858
Joined: Wed Aug 10, 2005 2:08 pm
Location: Yorkshire, England
Contact:

Re: ProGUI V3 Alpha 3 Ready for testing!

Post by PrincieD »

blueb wrote: Sun Jan 12, 2025 2:19 pm IDE Log...
[COMPILER] Line 38: WidgetGetSkinColor() is not a function, array, list, map or macro.

Checked both the DLL and the .PBI file and the above doesn't exist. Any clues?

I downloaded the latest zip from the first page, and the results were the same.
Hi blueb, this is work in progress at the moment so these new commands aren't part of the alpha 3 download yet
ProGUI - Professional Graphical User Interface Library - http://www.progui.co.uk
PrincieD
Addict
Addict
Posts: 858
Joined: Wed Aug 10, 2005 2:08 pm
Location: Yorkshire, England
Contact:

Re: ProGUI V3 Alpha 3 Ready for testing!

Post by PrincieD »

Early version demo of skin state transition animations :)

https://www.youtube.com/shorts/5ZeZrRmwW_Y
ProGUI - Professional Graphical User Interface Library - http://www.progui.co.uk
PrincieD
Addict
Addict
Posts: 858
Joined: Wed Aug 10, 2005 2:08 pm
Location: Yorkshire, England
Contact:

Re: ProGUI V3 Alpha 3 Ready for testing!

Post by PrincieD »

https://www.youtube.com/shorts/CjbLz0j0hFs

So this is a slighty more interesting example. This time the test widget has a transition on hover too:

Code: Select all

SkinSetValue("testContainer.testWidget", "background-color", "red")
SkinSetValue("testContainer.testWidget", "hover.background-color", "rgb(0, 255, 0)")
SkinSetValue("testContainer.testWidget", "transition", "background-color 2s")
SkinSetValue("testContainer.testWidget", "hover.transition", "background-color 0s")
And the test container widget now has hover state and transition:

Code: Select all

SkinSetValue("testContainer", "background-color", "orange")
SkinSetValue("testContainer", "hover.background-color", "gray")
SkinSetValue("testContainer", "transition", "background-color .5s")
I've also refactored the code slightly, previously WidgetSetSkinState() handled the swapping of widget states and starting of transition animations however I noticed occasionally there was some glitching due to thread race conditions (and a rare dead lock). WidgetSetSkinState() now simply adds the new state to a queue in the animation thread (keeping the UI thread nice and quick) and the processing of widget skin states is now handled in the animation thread before the rendering thread is signaled.
ProGUI - Professional Graphical User Interface Library - http://www.progui.co.uk
User avatar
idle
Always Here
Always Here
Posts: 5834
Joined: Fri Sep 21, 2007 5:52 am
Location: New Zealand

Re: ProGUI V3 Alpha 3 Ready for testing!

Post by idle »

looks great :D
PrincieD
Addict
Addict
Posts: 858
Joined: Wed Aug 10, 2005 2:08 pm
Location: Yorkshire, England
Contact:

Re: ProGUI V3 Alpha 3 Ready for testing!

Post by PrincieD »

idle wrote: Thu Jan 16, 2025 1:42 amlooks great :D
Thanks mate! :D
ProGUI - Professional Graphical User Interface Library - http://www.progui.co.uk
PrincieD
Addict
Addict
Posts: 858
Joined: Wed Aug 10, 2005 2:08 pm
Location: Yorkshire, England
Contact:

Re: ProGUI V3 Alpha 3 Ready for testing!

Post by PrincieD »

Hi guys quick update, I've got proper transition interruption working now so if a property is currently animating and theres a new state and the new state is the same as the animation's start or end state then the distance is calculated and the new duration calculated correctly. For example if you hover over a widget and then quickly un-hover and then hover again instead of it doing a full transition duration from the last current transitioned values it picks up where it left off - so nice responsive animations that behave the same as CSS transitions.

Currently the transition animation event handler timing function is hard coded to "ease" so tomorrow I'll be adding support for (to start off with):

Code: Select all

ease
linear
ease-in
ease-out
ease-in-out
There are already about 45 Transition_x() animation functions in ProGUI V3 - so it should just be a case of creating a prototype pointer and then performing dynamic dispatch on the pointer based on a name map.

Code: Select all

Transition_CubicBezier.d(t.d, b.d, c.d, d.d, P1x.d, P1y.d, P2x.d, P2y.d)
Transition_Linear.d(t.d, b.d, c.d, d.d)
Transition_Ease.d(t.d, b.d, c.d, d.d)
Transition_EaseInQuad.d(t.d, b.d, c.d, d.d)
Transition_EaseOutQuad.d(t.d, b.d, c.d, d.d)
Transition_EaseInOutQuad.d(t.d, b.d, c.d, d.d)
Transition_EaseOutInQuad.d(t.d, b.d, c.d, d.d)
Transition_EaseInCubic.d(t.d, b.d, c.d, d.d)
Transition_EaseOutCubic.d(t.d, b.d, c.d, d.d)
Transition_EaseInOutCubic.d(t.d, b.d, c.d, d.d)
Transition_EaseOutInCubic.d(t.d, b.d, c.d, d.d)
Transition_EaseInQuart.d(t.d, b.d, c.d, d.d)
Transition_EaseOutQuart.d(t.d, b.d, c.d, d.d)
Transition_EaseInOutQuart.d(t.d, b.d, c.d, d.d)
Transition_EaseOutInQuart.d(t.d, b.d, c.d, d.d)
Transition_EaseInQuint.d(t.d, b.d, c.d, d.d)
Transition_EaseOutQuint.d(t.d, b.d, c.d, d.d)
Transition_EaseInOutQuint.d(t.d, b.d, c.d, d.d)
Transition_EaseOutInQuint.d(t.d, b.d, c.d, d.d)
Transition_EaseInSine.d(t.d, b.d, c.d, d.d)
Transition_EaseOutSine.d(t.d, b.d, c.d, d.d)
Transition_EaseInOutSine.d(t.d, b.d, c.d, d.d)
Transition_EaseOutInSine.d(t.d, b.d, c.d, d.d)
Transition_EaseInExpo.d(t.d, b.d, c.d, d.d)
Transition_EaseOutExpo.d(t.d, b.d, c.d, d.d)
Transition_EaseInOutExpo.d(t.d, b.d, c.d, d.d)
Transition_EaseOutInExpo.d(t.d, b.d, c.d, d.d)
Transition_EaseInCirc.d(t.d, b.d, c.d, d.d)
Transition_EaseOutCirc.d(t.d, b.d, c.d, d.d)
Transition_EaseInOutCirc.d(t.d, b.d, c.d, d.d)
Transition_EaseOutInCirc.d(t.d, b.d, c.d, d.d)
Transition_EaseInElastic.d(t.d, b.d, c.d, d.d, amplitude.d = 1, period.d = 0.3)
Transition_EaseOutElastic.d(t.d, b.d, c.d, d.d, amplitude.d = 1, period.d = 0.3)
Transition_EaseInOutElastic.d(t.d, b.d, c.d, d.d, amplitude.d = 1, period.d = 0.3)
Transition_EaseOutInElastic.d(t.d, b.d, c.d, d.d)
Transition_EaseInElastic2.d(t.d, b.d, c.d, d.d, easeInRatio.d = 0.2, amplitude.d = 10, wobbleAmount.d = 3)
Transition_EaseOutElastic2.d(t.d, b.d, c.d, d.d, easeInRatio.d = 0.2, amplitude.d = 10, wobbleAmount.d = 3)
Transition_EaseInBack.d(t.d, b.d, c.d, d.d, backAmount.d = 1.70158)
Transition_EaseOutBack.d(t.d, b.d, c.d, d.d, backAmount.d = 1.70158)
Transition_EaseInOutBack.d(t.d, b.d, c.d, d.d, backAmount.d = 1.70158)
Transition_EaseOutInBack.d(t.d, b.d, c.d, d.d, backAmount.d = 1.70158)
Transition_EaseOutBounce.d(t.d, b.d, c.d, d.d)
Transition_EaseInBounce.d(t.d, b.d, c.d, d.d)
Transition_EaseInOutBounce.d(t.d, b.d, c.d, d.d)
Transition_EaseOutInBounce.d(t.d, b.d, c.d, d.d)
ProGUI - Professional Graphical User Interface Library - http://www.progui.co.uk
PrincieD
Addict
Addict
Posts: 858
Joined: Wed Aug 10, 2005 2:08 pm
Location: Yorkshire, England
Contact:

Re: ProGUI V3 Alpha 3 Ready for testing!

Post by PrincieD »

The following transition timing functions are now working by name:

Code: Select all

ease
linear
ease-in
ease-out
ease-in-out
cubic-bezier
ease-in-quad
ease-out-quad
ease-in-out-quad
ease-out-in-quad
ease-in-cubic
ease-out-cubic
ease-in-out-cubic
ease-out-in-cubic
ease-in-quart
ease-out-quart
ease-in-out-quart
ease-out-in-quart
ease-in-quint
ease-out-quint
ease-in-out-quint
ease-out-in-quint
ease-in-sine
ease-out-sine
ease-in-out-sine
ease-out-in-sine
ease-in-expo
ease-out-expo
ease-in-out-expo
ease-out-in-expo
ease-in-circ
ease-out-circ
ease-in-out-circ
ease-out-in-circ
ease-in-elastic
ease-out-elastic
ease-in-out-elastic
ease-out-in-elastic
ease-in-elastic2
ease-out-elastic2
ease-in-back
ease-out-back
ease-in-out-back
ease-out-in-back
ease-out-bounce
ease-in-bounce
ease-in-out-bounce
ease-out-in-bounce
For example:

Code: Select all

SkinSetValue("testContainer.testWidget", "background-color", "red")
SkinSetValue("testContainer.testWidget", "hover.background-color", "rgb(0, 255, 0)")
SkinSetValue("testContainer.testWidget", "transition", "background-color ease-out 2s")
SkinSetValue("testContainer.testWidget", "hover.transition", "background-color linear .5s")
If no timing function is specified then the default is 'ease' i.e

Code: Select all

SkinSetValue("testContainer.testWidget", "background-color", "red")
SkinSetValue("testContainer.testWidget", "hover.background-color", "rgb(0, 255, 0)")
SkinSetValue("testContainer.testWidget", "transition", "background-color 2s")
SkinSetValue("testContainer.testWidget", "hover.transition", "background-color .5s")
ProGUI - Professional Graphical User Interface Library - http://www.progui.co.uk
PrincieD
Addict
Addict
Posts: 858
Joined: Wed Aug 10, 2005 2:08 pm
Location: Yorkshire, England
Contact:

Re: ProGUI V3 Alpha 3 Ready for testing!

Post by PrincieD »

Hi guys, just taking a moment to pause (and reflect). I'm really chuffed with the development after a year and a half from resurrecting the V2 experimental code - now finally at a stage where I'm seeing skin state animation transitions (what I imagined!) and now seeing it, really pleased :) I hope ProGUI V3 will become something quite special and cheers for all the encouragement my friends I really appreciate it!

Next will be widget border state transition and animations (now the core is working well with colors), then gradients and images, seeing borders animate should be really cool.

Cheers!

Chris.
ProGUI - Professional Graphical User Interface Library - http://www.progui.co.uk
PrincieD
Addict
Addict
Posts: 858
Joined: Wed Aug 10, 2005 2:08 pm
Location: Yorkshire, England
Contact:

Re: ProGUI V3 Alpha 3 Ready for testing!

Post by PrincieD »

Dll currently compiles to 827KB boo! now 7K more than an Amiga floppy disk, memory usage is 30 - 40 meg. The dll will probably grow to a meg with the 'Pro' widgets.

p.s. tell a lie it's 880KB on an Amiga Floppy, getting confused with 720K PC (high density 1.44 meg) ... it's been about 20 years since I last checked, forgive me lol
ProGUI - Professional Graphical User Interface Library - http://www.progui.co.uk
PrincieD
Addict
Addict
Posts: 858
Joined: Wed Aug 10, 2005 2:08 pm
Location: Yorkshire, England
Contact:

Re: ProGUI V3 Alpha 3 Ready for testing!

Post by PrincieD »

Just over halfway through writing the new skin engine for ProGUI V3 🙂 I'm currently refactoring the code to handle sub properties more efficiently for example: border, border-radius, border-top-left-radius with 'border' being the base property.

Previously all of these would have their own allocated memory structures and animate independently however it's better to have the sub-properties share the base property memory so that there isn't multiple property lookups when drawing, just one - the base property.

Specifying transition animations in the skin doesn't need to be explicit too for example "transition: border-radius 2s, border-top-left-radius 2s" should be able to do just a "transition: border-radius 2s" if you don't want to animate the top left border radius independently .. sounds complicated I know lol, should be really versatile when I've got it working though and mimick how web browsers handle CSS

Sneek peek WIP video: https://www.youtube.com/shorts/oDEVxtpJPgA

Code: Select all

SkinSetValue("testContainer.testWidget", "hover.transition", "background-color .5s, border 1s ease-out-bounce")
ProGUI - Professional Graphical User Interface Library - http://www.progui.co.uk
User avatar
idle
Always Here
Always Here
Posts: 5834
Joined: Fri Sep 21, 2007 5:52 am
Location: New Zealand

Re: ProGUI V3 Alpha 3 Ready for testing!

Post by idle »

looking good. 8)
Post Reply