Sign in to follow this  
Followers 0

Multiple PNG images as GUI elements

31 posts in this topic

Posted (edited)

I discovered that standard controls couldn't achieve some of the visual effects I wanted. using @SW_SHOW and HIDE repeatedly caused flickers, transparent gifs have jaggy artifacts, and other methods had other problems. I discovered lod3n's PNG as GUI example, and proceeded to discover that it was a pain in the arse to create multi-image GUIs. I finally discovered that by using both Kip's _GuiCreate_Alpha and SetBitMap together, I could create child windows that could be used as the foundation for more advanced widgets (and can themselves have child windows.)

Nothing compiled, all source and images included.

The Heart image can be dragged by clicking on the top 50 pixels or so (windows treats it like a child window, so thinks it has a titlebar.) The Windows icon is fixed in place and cannot be dragged, or used to drag. The YinYang can be dragged and pulls the other two along with it. It's relatively simple to capture clicks, butyou'll need to get the title of the currently hovered png by using:

$pos = _WinAPI_GetMousePos()
    $hwnd = _WinAPI_WindowFromPoint($pos)
    $title = _WinAPI_GetWindowText($hwnd)

And then use $title to filter click (or other) events.

Can be used for multiple png, image buttons, custom controls, drop shadows, holes in GUIs, fade animations, and much more. :D

This is raw, I'm planning on making it much more sleek and usable, codewise.

Edited by JRowe

Share this post


Link to post
Share on other sites



Posted

Very nice work JRowe!

This can lead to some slick looking GUIs.

Thanks much.

Share this post


Link to post
Share on other sites

Posted

Cool work, i will try it in a project

Share this post


Link to post
Share on other sites

Posted

I have a question, that's probably very basic. Why doesn't the program continuously execute? If you look at the animated fading, it stops cycling when the cursor isn't in motion, or when there's no input activity.

Is this something to do with the GUIRegisterMsg blocking execution, or what?

Share this post


Link to post
Share on other sites

Posted (edited)

I haven't looked at the code, but...

Are you using OnEventMode or GuiGetMsg()? GuiGetMsg() will throttle how often it returns if no messages are in the queue and slow down your main loop...I'm assuming you're running the animation in the main loop. Switch to OnEventMode with Sleep() statements to control your main loop better.

Edited by wraithdu

Share this post


Link to post
Share on other sites

Posted (edited)

Looks really nice!

If you replace the While - Wend with this and add Opt("GUIOnEventMode", 1) / GUISetOnEvent($GUI_EVENT_CLOSE, "_Exit")

#include <GDIPlus.au3>
#include <WindowsConstants.au3>
#include <GuiConstantsEx.au3>
#include <StaticConstants.au3>
Global Const $AC_SRC_ALPHA = 1
Opt("GUIOnEventMode", 1)
_GDIPlus_Startup()

$pngSrc = @ScriptDir & "\GUIBK.png"
;This allows the png to be dragged by clicking anywhere on the main image.
GUIRegisterMsg($WM_NCHITTEST, "WM_NCHITTEST")
$GUI = _GUICreate_Alpha("Look at the shiny", $pngSrc)
$myGuiHandle = WinGetHandle("Look at the shiny")
GUISetState()

;This is an invisible control container. You can create regular windows controls on this panel.
$controlGui = GUICreate("ControlGUI", 400, 400, 0, 0, $WS_POPUP, BitOR($WS_EX_LAYERED, $WS_EX_MDICHILD), $myGuiHandle)
GUICtrlSetBkColor($controlGui, 0xFF00FF)
GUICtrlSetState(-1, $GUI_DISABLE)


;This png is draggable as if it were a child window. It has about 50 pixels at the top that allow it to be dragged
$TransparentButtonTest = GUICreate("Test321", 100, 100, -1, -1, $WS_EX_MDICHILD, $WS_EX_LAYERED, $myGuiHandle)
$hImageButton = _GDIPlus_ImageLoadFromFile(@ScriptDir & "\Fav.png")
SetBitMap($TransparentButtonTest, $hImageButton, 255)

GUISetState()

;This png is static. It is in a fixed position relative to the main window png, and is dragged along with it.
;For fun, it fades out in cycles.
$TransparentButtonTest2 = GUICreate("Test123", 250, 250, 400, 200,$WS_POPUP, BitOR($WS_EX_LAYERED, $WS_EX_MDICHILD), $myGuiHandle)
$hImageButton2 = _GDIPlus_ImageLoadFromFile(@ScriptDir & "\Windows.png")
SetBitMap($TransparentButtonTest2, $hImageButton2, 255)

GUISetState()

GUISetOnEvent($GUI_EVENT_CLOSE, "_Exit")

$i = 0

Do
    $i += 2
    If $i >= 255 Then $i = 0
    $advMsg = GUIGetMsg(1)
    SetBitMap($TransparentButtonTest2, $hImageButton2, $i)
Until Not Sleep(10)

Func _Exit()
    _GDIPlus_Shutdown()
    Exit
EndFunc

Func WM_NCHITTEST($hWnd, $iMsg, $iwParam, $ilParam)
    If ($hwnd = WinGetHandle("Look at the shiny")) And ($iMsg = $WM_NCHITTEST) Then
    Return $HTCAPTION
    EndIf
EndFunc

Func _GUICreate_Alpha($sTitle, $sPath, $iX=-1, $iY=-1, $iOpacity=255)
    Local $hGUI, $hImage, $hScrDC, $hMemDC, $hBitmap, $hOld, $pSize, $tSize, $pSource, $tSource, $pBlend, $tBlend
    $hImage = _GDIPlus_ImageLoadFromFile($sPath)
    $width = _GDIPlus_ImageGetWidth($hImage)
    $height = _GDIPlus_ImageGetHeight($hImage)
    $hGUI = GUICreate($sTitle, $width, $height, $iX, $iY, $WS_POPUP, $WS_EX_LAYERED)
    $hScrDC = _WinAPI_GetDC(0)
    $hMemDC = _WinAPI_CreateCompatibleDC($hScrDC)
    $hBitmap = _GDIPlus_BitmapCreateHBITMAPFromBitmap($hImage)
    $hOld = _WinAPI_SelectObject($hMemDC, $hBitmap)
    $tSize = DllStructCreate($tagSIZE)
    $pSize = DllStructGetPtr($tSize)
    DllStructSetData($tSize, "X", $width)
    DllStructSetData($tSize, "Y", $height)
    $tSource = DllStructCreate($tagPOINT)
    $pSource = DllStructGetPtr($tSource)
    $tBlend = DllStructCreate($tagBLENDFUNCTION)
    $pBlend = DllStructGetPtr($tBlend)
    DllStructSetData($tBlend, "Alpha", $iOpacity)
    DllStructSetData($tBlend, "Format", 1)
    _WinAPI_UpdateLayeredWindow($hGUI, $hScrDC, 0, $pSize, $hMemDC, $pSource, 0, $pBlend, 2)
    _WinAPI_ReleaseDC(0, $hScrDC)
    _WinAPI_SelectObject($hMemDC, $hOld)
    _WinAPI_DeleteObject($hBitmap)
    _WinAPI_DeleteObject($hImage)
    _WinAPI_DeleteDC($hMemDC)
EndFunc ;==>_GUICreate_Alpha

Func SetBitmap($hGUI, $hImage, $iOpacity)
    Local $hScrDC, $hMemDC, $hBitmap, $hOld, $pSize, $tSize, $pSource, $tSource, $pBlend, $tBlend
    $hScrDC = _WinAPI_GetDC(0)
    $hMemDC = _WinAPI_CreateCompatibleDC($hScrDC)
    $hBitmap = _GDIPlus_BitmapCreateHBITMAPFromBitmap($hImage)
    $hOld = _WinAPI_SelectObject($hMemDC, $hBitmap)
    $tSize = DllStructCreate($tagSIZE)
    $pSize = DllStructGetPtr($tSize)
    DllStructSetData($tSize, "X", _GDIPlus_ImageGetWidth($hImage))
    DllStructSetData($tSize, "Y", _GDIPlus_ImageGetHeight($hImage))
    $tSource = DllStructCreate($tagPOINT)
    $pSource = DllStructGetPtr($tSource)
    $tBlend = DllStructCreate($tagBLENDFUNCTION)
    $pBlend = DllStructGetPtr($tBlend)
    DllStructSetData($tBlend, "Alpha", $iOpacity)
    DllStructSetData($tBlend, "Format", $AC_SRC_ALPHA)
    _WinAPI_UpdateLayeredWindow($hGUI, $hScrDC, 0, $pSize, $hMemDC, $pSource, 0, $pBlend, $ULW_ALPHA)
    _WinAPI_ReleaseDC(0, $hScrDC)
    _WinAPI_SelectObject($hMemDC, $hOld)
    _WinAPI_DeleteObject($hBitmap)
    _WinAPI_DeleteDC($hMemDC)
EndFunc   ;==>SetBitmap

it will work as designed :D

UEZ

Edited by UEZ

Share this post


Link to post
Share on other sites

Posted

Hi

I appreciate your effort here.

I wanted to say, if you have gone this far with developing a GUI with png's I see no reason to use guictrls at all. You make reference in your code to doing this.

;This is an invisible control container. You can create regular windows controls on this panel.

You can draw whatever you need right on the png.

My below modifications adds text to one of the pngs.

As an aside, you have included grey.gif in your zip file, I don't think it is needed.

Again, thanks for your example

Picea

#include <GDIPlus.au3>
#include <WindowsConstants.au3>
#include <GuiConstantsEx.au3>
#include <StaticConstants.au3>
Global Const $AC_SRC_ALPHA = 1

_GDIPlus_Startup()

$pngSrc = @ScriptDir & "\GUIBK.png"
;This allows the png to be dragged by clicking anywhere on the main image.
GUIRegisterMsg($WM_NCHITTEST, "WM_NCHITTEST")
$GUI = _GUICreate_Alpha("Look at the shiny", $pngSrc)
$myGuiHandle = WinGetHandle("Look at the shiny")
GUISetState()

;This is an invisible control container. You can create regular windows controls on this panel.
$controlGui = GUICreate("ControlGUI", 400, 400, 0, 0, $WS_POPUP, BitOR($WS_EX_LAYERED, $WS_EX_MDICHILD), $myGuiHandle)
GUICtrlSetBkColor($controlGui, 0xFF00FF)
GUICtrlSetState(-1, $GUI_DISABLE)


;This png is draggable as if it were a child window. It has about 50 pixels at the top that allow it to be dragged
$TransparentButtonTest = GUICreate("Test321", 100, 100, -1, -1, $WS_EX_MDICHILD, $WS_EX_LAYERED, $myGuiHandle)
$hImageButton = _GDIPlus_ImageLoadFromFile(@ScriptDir & "\Fav.png")
$hBmp = _ImageDrawText($hImageButton, "Imbedded Text", 50, 75, 0x111111, 14, 0, "Arial")
SetBitMap($TransparentButtonTest, $hBmp, 255)

GUISetState()

;This png is static. It is in a fixed position relative to the main window png, and is dragged along with it.
;For fun, it fades out in cycles.
$TransparentButtonTest2 = GUICreate("Test123", 250, 250, 400, 200,$WS_POPUP, BitOR($WS_EX_LAYERED, $WS_EX_MDICHILD), $myGuiHandle)
$hImageButton2 = _GDIPlus_ImageLoadFromFile(@ScriptDir & "\Windows.png")
SetBitMap($TransparentButtonTest2, $hImageButton2, 255)

GUISetState()
$i = 0

While 1
$i = $i + 1
If $i = 255 Then $i = 0
    $msg = GUIGetMsg()
    $advMsg = GUIGetMsg(1)
    Select
        Case $msg = $GUI_EVENT_CLOSE
            ExitLoop

    EndSelect
SetBitMap($TransparentButtonTest2, $hImageButton2, $i)
WEnd


_GDIPlus_Shutdown()

Func WM_NCHITTEST($hWnd, $iMsg, $iwParam, $ilParam)
    If ($hwnd = WinGetHandle("Look at the shiny")) And ($iMsg = $WM_NCHITTEST) Then
    Return $HTCAPTION
    EndIf
EndFunc

Func _GUICreate_Alpha($sTitle, $sPath, $iX=-1, $iY=-1, $iOpacity=255)
    Local $hGUI, $hImage, $hScrDC, $hMemDC, $hBitmap, $hOld, $pSize, $tSize, $pSource, $tSource, $pBlend, $tBlend
    $hImage = _GDIPlus_ImageLoadFromFile($sPath)
    $width = _GDIPlus_ImageGetWidth($hImage)
    $height = _GDIPlus_ImageGetHeight($hImage)
    $hGUI = GUICreate($sTitle, $width, $height, $iX, $iY, $WS_POPUP, $WS_EX_LAYERED)
    $hScrDC = _WinAPI_GetDC(0)
    $hMemDC = _WinAPI_CreateCompatibleDC($hScrDC)
    $hBitmap = _GDIPlus_BitmapCreateHBITMAPFromBitmap($hImage)
    $hOld = _WinAPI_SelectObject($hMemDC, $hBitmap)
    $tSize = DllStructCreate($tagSIZE)
    $pSize = DllStructGetPtr($tSize)
    DllStructSetData($tSize, "X", $width)
    DllStructSetData($tSize, "Y", $height)
    $tSource = DllStructCreate($tagPOINT)
    $pSource = DllStructGetPtr($tSource)
    $tBlend = DllStructCreate($tagBLENDFUNCTION)
    $pBlend = DllStructGetPtr($tBlend)
    DllStructSetData($tBlend, "Alpha", $iOpacity)
    DllStructSetData($tBlend, "Format", 1)
    _WinAPI_UpdateLayeredWindow($hGUI, $hScrDC, 0, $pSize, $hMemDC, $pSource, 0, $pBlend, 2)
    _WinAPI_ReleaseDC(0, $hScrDC)
    _WinAPI_SelectObject($hMemDC, $hOld)
    _WinAPI_DeleteObject($hBitmap)
    _WinAPI_DeleteObject($hImage)
    _WinAPI_DeleteDC($hMemDC)
EndFunc ;==>_GUICreate_Alpha

Func SetBitmap($hGUI, $hImage, $iOpacity)
    Local $hScrDC, $hMemDC, $hBitmap, $hOld, $pSize, $tSize, $pSource, $tSource, $pBlend, $tBlend
    $hScrDC = _WinAPI_GetDC(0)
    $hMemDC = _WinAPI_CreateCompatibleDC($hScrDC)
    $hBitmap = _GDIPlus_BitmapCreateHBITMAPFromBitmap($hImage)
    $hOld = _WinAPI_SelectObject($hMemDC, $hBitmap)
    $tSize = DllStructCreate($tagSIZE)
    $pSize = DllStructGetPtr($tSize)
    DllStructSetData($tSize, "X", _GDIPlus_ImageGetWidth($hImage))
    DllStructSetData($tSize, "Y", _GDIPlus_ImageGetHeight($hImage))
    $tSource = DllStructCreate($tagPOINT)
    $pSource = DllStructGetPtr($tSource)
    $tBlend = DllStructCreate($tagBLENDFUNCTION)
    $pBlend = DllStructGetPtr($tBlend)
    DllStructSetData($tBlend, "Alpha", $iOpacity)
    DllStructSetData($tBlend, "Format", $AC_SRC_ALPHA)
    _WinAPI_UpdateLayeredWindow($hGUI, $hScrDC, 0, $pSize, $hMemDC, $pSource, 0, $pBlend, $ULW_ALPHA)
    _WinAPI_ReleaseDC(0, $hScrDC)
    _WinAPI_SelectObject($hMemDC, $hOld)
    _WinAPI_DeleteObject($hBitmap)
    _WinAPI_DeleteDC($hMemDC)
EndFunc   ;==>SetBitmap


Func _ImageDrawText($hImage, $sText, $iX = 0, $iY = 0, $iRGB = 0x000000, $iSize = 9, $iStyle = 0, $sFont = "Arial") 
    Local $w, $h, $hGraphic1, $hBitmap, $hGraphic2, $hBrush, $hFormat, $hFamily, $hFont, $tLayout, $aInfo
    $w = _GDIPlus_ImageGetWidth($hImage)
    $h = _GDIPlus_ImageGetHeight($hImage)
    
    ;Create a new bitmap, this way the original opened png is left unchanged
    $hGraphic1 = _GDIPlus_GraphicsCreateFromHWND(_WinAPI_GetDesktopWindow())
    $hBitmap = _GDIPlus_BitmapCreateFromGraphics($w, $h, $hGraphic1)
    $hGraphic2 = _GDIPlus_ImageGetGraphicsContext($hBitmap) 
    
    ; Draw the original opened png into my newly created bitmap
    _GDIPlus_GraphicsDrawImageRect($hGraphic2, $hImage, 0, 0, $w, $h)

    ;Create the font
    $hBrush = _GDIPlus_BrushCreateSolid ("0xFF" & Hex($iRGB, 6))
    $hFormat = _GDIPlus_StringFormatCreate()
    $hFamily = _GDIPlus_FontFamilyCreate ($sFont)
    $hFont = _GDIPlus_FontCreate ($hFamily, $iSize, $iStyle)
    $tLayout = _GDIPlus_RectFCreate ($iX, $iY, 0, 0)
    $aInfo = _GDIPlus_GraphicsMeasureString ($hGraphic2, $sText, $hFont, $tLayout, $hFormat)
    
    ;Draw the font onto the new bitmap
    _GDIPlus_GraphicsDrawStringEx ($hGraphic2, $sText, $hFont, $aInfo[0], $hFormat, $hBrush)
    
    ;Cleanup the no longer needed resources
    _GDIPlus_FontDispose ($hFont)
    _GDIPlus_FontFamilyDispose ($hFamily)
    _GDIPlus_StringFormatDispose ($hFormat)
    _GDIPlus_BrushDispose ($hBrush)
    _GDIPlus_GraphicsDispose ($hGraphic2)
    _GDIPlus_GraphicsDispose ($hGraphic1)

    ;Return the new bitmap
    Return $hBitmap
EndFunc

Share this post


Link to post
Share on other sites

Posted

I use the SetBitMap() function in one of my programs, and I also recently added a fading in/out animation. I found (at least on Vista/Win7) this to be very CPU intensive. If I remember correctly from an old discussion it takes longer/more work to _WinAPI_GetDC() in Vista/7. I moved this initialization code and cleanup code into their own functions that are only executed once on start/exit. Now I can animate all day long at 0% CPU...BIG difference from the ~10% previously. If you could find a way to move the _GDIPlus_ImageLoadFromFile() and associated image loading functions out of the loop it would further increase optimization. I got it down to the point where this was all my SetBitMap() function contains:

DllStructSetData($tBlend, "Alpha", $iOpacity)
_WinAPI_UpdateLayeredWindow($GUI, $hScrDC, 0, $pSize, $hMemDC, $pSource, 0, $pBlend, $ULW_ALPHA)

Share this post


Link to post
Share on other sites

Posted

Thanks for the comments, guys, keep 'em coming. I'm working on a Regions system, to keep track of mouse position, and which part of a GUI it's currently over, and an Interaction system to keep track of interaction events. I'd figured out yesterday how to draw borders using pngs, so I can now draw a complete frame around an empty window. I'll start optimizing the initialization and cleanup when I start turning these into functions, much thanks Wraithdu.

I have a plan that goes like this:

Regions are passed as parameters of Controls. Controls are rectangular primitives that have graphics and activation events assigned to them. Controls can be combined into Collections, which are used to construct Widgets. Widgets are the pinnacle of the GUI chain. Every activation event of the control primitives is passed on to the Widget, and in addition, the Widget can be animated.

So I can create a set of 4 controls (Top border, right border, left border, bottom border.) These will have activation events that trigger resizing functions. A Caption region can be set directly below the Top border, allowing you to drag.

I'll have to set up Control chaining so that you can have child windows, and a layer system to keep track of z-order.

It starts with using the low level mouse hook, then keeping track of regions, then tracking advanced input (clicks, right clicks, wheels up/down, x buttons.)

Here's how I'm drawing a frame. I have 4 corner images and 4 border images. As soon as I have a frame widget constructed, it will use this to resize a drawn window on the fly, just like a normal windows GUI, but sexier. There's some overlap in the transparent corners, so it looks a little funky, but overall, I like the effect.

I take the loaded image, get it's graphic context, paint to the graphic, and then create a handle to the result.

$gContext = _GDIPlus_ImageGetGraphicsContext($hImage)
    $drawing = _GDIPlus_GraphicsDrawImage($gContext, $hImage2, 50, 50)

    _GDIPlus_GraphicsDrawImage($gContext, $TopLeftCorner, 0, 0)
    _GDIPlus_GraphicsDrawImage($gContext, $TopRightCorner, $width-43, 0)
    _GDIPlus_GraphicsDrawImage($gContext, $BottomLeftCorner, 0, $height -43)
    _GDIPlus_GraphicsDrawImage($gContext, $BottomRightCorner, $width-43, $height -43)
    ;Draw Top and Bottom borders
    For $i = 43 to $width - 43 Step 1
        _GDIPlus_GraphicsDrawImage($gContext, $TopBorder, $i, 0)
        _GDIPlus_GraphicsDrawImage($gContext, $BottomBorder, $i, $height - 43)
    Next
    ;Draw Side Borders
    For $i = 43 to $height - 43 Step 1
    _GDIPlus_GraphicsDrawImage($gContext, $RightBorder, $width-43, $i)
    _GDIPlus_GraphicsDrawImage($gContext, $LeftBorder, 0, $i)
    Next


    ;Image finalized right here
    $hBitmap = _GDIPlus_BitmapCreateHBITMAPFromBitmap($hImage)

Share this post


Link to post
Share on other sites

Posted

Ok, so I have a proposal for the GUI framework, I'd like people to look at it, and discuss any ideas they may have.

Please check out this thread: http://www.autoitscript.com/forum/index.php?showtopic=102920

Tentative project name:

PNGWin, definitively usurping the animal for something AutoIt related. :D

Share this post


Link to post
Share on other sites

Posted

Hello, everyone!

I have a question regarding "SetBitmap" function. I've created a launcher gui that uses only png images and it has three-state buttons (normal, over, press). So my question is: Is it bad for the computer (in any possible way: ram usage, processor usage...) to continuously set images (or same image) with "SetBitmap". Like:

while
If ...mouse is over button... then setbitmap
sleep(...)
wend
. I've also created one that checks if image is set or not but it's a bit slower. So which one would be better?

Share this post


Link to post
Share on other sites

Posted

It will increase the cpu usage a bit, but that should be it. There should not be any problem with the rest of the computer unless you are constantly writing to a file. That can reduce the life of your computer.

Share this post


Link to post
Share on other sites

Posted (edited)

Well, this is interesting!

But I can't seem to find a way to add a label to the main GUI, I tried:

;This is an invisible control container. You can create regular windows controls on this panel.
$controlGui = GUICreate("ControlGUI", 400, 400, 0, 0, $WS_POPUP, BitOR($WS_EX_LAYERED, $WS_EX_MDICHILD), $myGuiHandle)
GuictrlCreateButton ( "lossssssssssssssssssssssssssssssssssssssssdfklhalskjdjklasdfjklasjkdfjkasjkdfjklasdljkjklasdjfjasdfl", 20, 20, 200, 200 )
GUICtrlSetBkColor($controlGui, 0xFF00FF)
GUICtrlSetState(-1, $GUI_DISABLE)

But it doesn't seem to work, how can I make text display on the PNG?

Edited by Rawox

Share this post


Link to post
Share on other sites

Posted

Ok, quick question. You think there is anyway to include support for animated .pngs?

Share this post


Link to post
Share on other sites

Posted

Uhm, animated pngs don't exist :mellow:

Share this post


Link to post
Share on other sites

Posted

Right, because you've never heard of them, they don't exist. But don't listen to me, I must just be a figment of your imagination. :mellow:

I think the whole concept needs some major work, and to integrate animated pngs might be relatively easy to include on a ground up rebuild.

There should be a gui library out there that comes packaged as a dll that can be used to set animated pngs, and all the rest. It's too commonly sought after not to have some open source package out there.

Share this post


Link to post
Share on other sites

Posted

Right, because you've never heard of them, they don't exist. But don't listen to me, I must just be a figment of your imagination. :mellow:

I think the whole concept needs some major work, and to integrate animated pngs might be relatively easy to include on a ground up rebuild.

There should be a gui library out there that comes packaged as a dll that can be used to set animated pngs, and all the rest. It's too commonly sought after not to have some open source package out there.

True, I like pure AutoIt solutions though. Why? I'm not to sure. Probly cause I can't change the code in a dll.

Share this post


Link to post
Share on other sites

Posted

Cool! Thanks for the information...

But can you also answer my question?

Share this post


Link to post
Share on other sites

Posted

I'll take a look and play with it later today, and let you know. I'd start by setting a better button name, since 200 characters of garbage isn't good for anything. I wouldn't be terribly surprised if that fixed your problem immediately, but I'll be checking later anyway.

Share this post


Link to post
Share on other sites

Posted

Good work JRowe.

Keep on that...

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!


Register a new account

Sign in

Already have an account? Sign in here.


Sign In Now
Sign in to follow this  
Followers 0

  • Recently Browsing   0 members

    No registered users viewing this page.