I have been experimenting to find a good way to present a 'Dynamic Text Box' that the User can freely re-size, edit and export as a 32bit PNG.
A short movie showing an example of one: http://www.professorcad.co.uk/pbforumquestion.html
In the movie, graphical 'handles' are displayed which the User can use to drag the Object (box graphic + multi-line text) to the desired size - that feature is not necessarily required, slider bars would be adequate, but I do need to be able to change the colour of the box and change the text font attributes.
Methods
1) 2D Sprite, using predefined image for the box
Advantages - Draw the text on the sprite, easy to output the finished result.
Disadvantages - Difficult to re-size/fit the text; Difficult to re-size the Object.
2) 2D Sprite, box graphic drawn on-the-fly
Advantages - Draw the text on the sprite; Easy to resize the sprite (replace with a re-draw); Easy to output the finished result.
Disadvantages - Difficult to re-size/fit the text; Potential for screen flicker during re-size.
3) Separate, Overlaid Windows - (Both border-less, top most has transparent background) 1 being the output for 2D drawing, the other, top window consisting of an Editor Gadget.
Advantages - Easy to resize the Object; Easy to handle box and font attribute changes.
Disadvantages - Don't know how best to handle the Z-level of the Windows so that they re-size together with a continuous display; Output the result as a screen region capture - not tested.
So, are there better methods? Would the new canvas gadget be among the contenders?
Thoughts on the best way to define a 'dynamic text box'
-
IdeasVacuum
- Always Here

- Posts: 6426
- Joined: Fri Oct 23, 2009 2:33 am
- Location: Wales, UK
- Contact:
Thoughts on the best way to define a 'dynamic text box'
IdeasVacuum
If it sounds simple, you have not grasped the complexity.
If it sounds simple, you have not grasped the complexity.
Re: Thoughts on the best way to define a 'dynamic text box'
Hi IdeasVacuum,
A fast try:Maybe it makes your decision easier.
Bernd
A fast try:
Code: Select all
#PickUpPixels = 10
#None = $00
#Move = $01
#ResizeLeft = $02
#ResizeRight = $04
#ResizeUp = $08
#ResizeDown = $10
OpenWindow(0, 0, 0, 640, 480, "Test", #PB_Window_SystemMenu|#PB_Window_ScreenCentered)
CanvasXPos = 10
CanvasYPos = 10
CanvasWidth = 200
CanvasHeight = 100
CanvasGadget(0, CanvasXPos, CanvasYPos, CanvasWidth, CanvasHeight)
GadgetMod = #None
Exit =#False
Repeat
Event = WaitWindowEvent()
Select Event
Case #PB_Event_Gadget
If EventGadget() = 0
Select EventType()
Case #PB_EventType_LeftButtonDown
StartXPos = WindowMouseX(0)
StartYPos = WindowMouseY(0)
If GetGadgetAttribute(0, #PB_Canvas_MouseX) > #PickUpPixels And GetGadgetAttribute(0, #PB_Canvas_MouseX) < GadgetWidth(0) - #PickUpPixels And GetGadgetAttribute(0, #PB_Canvas_MouseY) > #PickUpPixels And GetGadgetAttribute(0, #PB_Canvas_MouseY) < GadgetHeight(0) - #PickUpPixels
GadgetMod | #Move
Else
If GetGadgetAttribute(0, #PB_Canvas_MouseX) < #PickUpPixels
GadgetMod | #ResizeLeft
ElseIf GetGadgetAttribute(0, #PB_Canvas_MouseX) > GadgetWidth(0) - #PickUpPixels
GadgetMod | #ResizeRight
EndIf
If GetGadgetAttribute(0, #PB_Canvas_MouseY) < #PickUpPixels
GadgetMod | #ResizeUp
ElseIf GetGadgetAttribute(0, #PB_Canvas_MouseY) > GadgetHeight(0) - #PickUpPixels
GadgetMod | #ResizeDown
EndIf
EndIf
Case #PB_EventType_LeftButtonUp
If GadgetMod <> #None
CurrentXPos = WindowMouseX(0)
CurrentYPos = WindowMouseY(0)
If GadgetMod & #Move
CanvasXPos = CanvasXPos + (CurrentXPos - StartXPos)
CanvasYPos = CanvasYPos + (CurrentYPos - StartYPos)
Else
If GadgetMod & #ResizeLeft
CanvasXPos = CanvasXPos - (StartXPos - CurrentXPos)
CanvasWidth = CanvasWidth + (StartXPos - CurrentXPos)
ElseIf GadgetMod & #ResizeRight
CanvasWidth + CurrentXPos - StartXPos
EndIf
If GadgetMod & #ResizeUp
CanvasYPos = CanvasYPos - (StartYPos - CurrentYPos)
CanvasHeight = CanvasHeight + (StartYPos - CurrentYPos)
ElseIf GadgetMod & #ResizeDown
CanvasHeight + CurrentYPos - StartYPos
EndIf
EndIf
CanvasGadget(0, CanvasXPos, CanvasYPos, CanvasWidth, CanvasHeight)
GadgetMod = #None
EndIf
EndSelect
EndIf
Case #PB_Event_CloseWindow
Exit = #True
EndSelect
Until ExitBernd
Re: Thoughts on the best way to define a 'dynamic text box'
CanvasGadget was my first instinct too... it's great for drawing interactive objects. I think it's quite flicker-free as well?
Re: Thoughts on the best way to define a 'dynamic text box'
But I found a problem:If you move them that they are overlapping, you can not select the right Gadget for resize
Bernd
Code: Select all
#PickUpPixels = 10
#None = $00
#Move = $01
#ResizeLeft = $02
#ResizeRight = $04
#ResizeUp = $08
#ResizeDown = $10
Structure GadgetStr
No.i
Mod.i
XPos.i
YPos.i
Width.i
Height.i
EndStructure
NewMap GadgetMap.GadgetStr()
OpenWindow(0, 0, 0, 640, 480, "Test", #PB_Window_SystemMenu|#PB_Window_ScreenCentered)
AddMapElement(GadgetMap(), "0")
GadgetMap()\No = 0
GadgetMap()\XPos = 10
GadgetMap()\YPos = 10
GadgetMap()\Width = 200
GadgetMap()\Height = 100
CanvasGadget(0, GadgetMap()\XPos, GadgetMap()\YPos, GadgetMap()\Width, GadgetMap()\Height, #PB_Canvas_Border)
AddMapElement(GadgetMap(), "1")
GadgetMap()\No = 1
GadgetMap()\XPos = 10
GadgetMap()\YPos = 200
GadgetMap()\Width = 200
GadgetMap()\Height = 100
CanvasGadget(1, GadgetMap()\XPos, GadgetMap()\YPos, GadgetMap()\Width, GadgetMap()\Height, #PB_Canvas_Border)
GadgetMod = #None
Exit =#False
Repeat
Event = WaitWindowEvent()
Select Event
Case #PB_Event_Gadget
EventGadget = EventGadget()
If FindMapElement(GadgetMap(), Str(EventGadget))
Select EventType()
Case #PB_EventType_LeftButtonDown
StartXPos = WindowMouseX(0)
StartYPos = WindowMouseY(0)
If GetGadgetAttribute(GadgetMap()\No, #PB_Canvas_MouseX) > #PickUpPixels And GetGadgetAttribute(GadgetMap()\No, #PB_Canvas_MouseX) < GadgetWidth(GadgetMap()\No) - #PickUpPixels And GetGadgetAttribute(GadgetMap()\No, #PB_Canvas_MouseY) > #PickUpPixels And GetGadgetAttribute(GadgetMap()\No, #PB_Canvas_MouseY) < GadgetHeight(GadgetMap()\No) - #PickUpPixels
GadgetMap()\Mod | #Move
Else
If GetGadgetAttribute(GadgetMap()\No, #PB_Canvas_MouseX) < #PickUpPixels
GadgetMap()\Mod | #ResizeLeft
ElseIf GetGadgetAttribute(GadgetMap()\No, #PB_Canvas_MouseX) > GadgetWidth(GadgetMap()\No) - #PickUpPixels
GadgetMap()\Mod | #ResizeRight
EndIf
If GetGadgetAttribute(GadgetMap()\No, #PB_Canvas_MouseY) < #PickUpPixels
GadgetMap()\Mod | #ResizeUp
ElseIf GetGadgetAttribute(GadgetMap()\No, #PB_Canvas_MouseY) > GadgetHeight(GadgetMap()\No) - #PickUpPixels
GadgetMap()\Mod | #ResizeDown
EndIf
EndIf
Case #PB_EventType_LeftButtonUp
If GadgetMap()\Mod <> #None
CurrentXPos = WindowMouseX(0)
CurrentYPos = WindowMouseY(0)
If GadgetMap()\Mod & #Move
GadgetMap()\XPos = GadgetMap()\XPos + (CurrentXPos - StartXPos)
GadgetMap()\YPos = GadgetMap()\YPos + (CurrentYPos - StartYPos)
Else
If GadgetMap()\Mod & #ResizeLeft
GadgetMap()\XPos = GadgetMap()\XPos - (StartXPos - CurrentXPos)
GadgetMap()\Width = GadgetMap()\Width + (StartXPos - CurrentXPos)
ElseIf GadgetMap()\Mod & #ResizeRight
GadgetMap()\Width + CurrentXPos - StartXPos
EndIf
If GadgetMap()\Mod & #ResizeUp
GadgetMap()\YPos = GadgetMap()\YPos - (StartYPos - CurrentYPos)
GadgetMap()\Height = GadgetMap()\Height + (StartYPos - CurrentYPos)
ElseIf GadgetMap()\Mod & #ResizeDown
GadgetMap()\Height + CurrentYPos - StartYPos
EndIf
EndIf
CanvasGadget(GadgetMap()\No, GadgetMap()\XPos, GadgetMap()\YPos, GadgetMap()\Width, GadgetMap()\Height, #PB_Canvas_Border)
GadgetMap()\Mod = #None
EndIf
EndSelect
EndIf
Case #PB_Event_CloseWindow
Exit = #True
EndSelect
Until ExitBernd
-
IdeasVacuum
- Always Here

- Posts: 6426
- Joined: Fri Oct 23, 2009 2:33 am
- Location: Wales, UK
- Contact:
Re: Thoughts on the best way to define a 'dynamic text box'
Interesting infratec, I didn't think about using the canvas itself as part of the Object. Learnt a lot from your code. Overlapping would never be a problem because there would only ever be one Object at a time. However, I think the canvas can only be rectangular/square, so as-is it will not meet the requirements.
Another possibility would be to have an Editor gadget on top of an image gadget - officially, that is not recommended, but I have 'got away with it' in the past with the image gadget disabled once loaded.
Another possibility would be to have an Editor gadget on top of an image gadget - officially, that is not recommended, but I have 'got away with it' in the past with the image gadget disabled once loaded.
IdeasVacuum
If it sounds simple, you have not grasped the complexity.
If it sounds simple, you have not grasped the complexity.
-
IdeasVacuum
- Always Here

- Posts: 6426
- Joined: Fri Oct 23, 2009 2:33 am
- Location: Wales, UK
- Contact:
Re: Thoughts on the best way to define a 'dynamic text box'
This is (less) rubbish (now), but I think it shows that the Canvas Gadget could be the solution. The code is stripped-out of the PB help example:
Snag with the editor gadget is the scrollbars showing-up. Is there a way to suppress them?
Edit: Slightly refined to be less rubbish
Edit2: Further refinement, plus extra issues!
Edit3: Changed to Kiffy's text alignment method.
New question: I'm saving the Object out as a PNG. As it is an irregular shape, the image has to include some of the background. The apps receiving the image need the background to be transparent. I can set 32bits during image create, but since the save is done via screen capture, the image is actually 24bit. Is there a way to identify the (white) background as being the transparency region so that it is saved as such in a PNG? This is something that image viewers such as IrfanView can accomplish with 24bit images (but with User input)
Edit: Answer: Create a 32bit Image for the screen capture. Before saving, set DrawingMode(#PB_2DDrawing_AlphaChannel), then Fill the background with the colour representing transparency: FillArea(0, 0, igBorderColour, igTransparent) [igTransparent = RGBA(0,0,0,0)]
Code: Select all
Enumeration
#IMAGE_Content ; stores the previous CanvasGadget content while the mouse is down
#IMAGE_Color
#IMAGE_LoadSave
#ImageSave
#DynText
#Editor
#Canvas
#Color
#Refresh
#Shadow
#Ctr
#Lft
#Rgt
#Clear
#Load
#Save
#TextEditor
EndEnumeration
Global igCurrentColour.i, igStartX.i, igStartY.i, igEndX.i, igEndY.i
Global igLastStartX.i, igLastStartY.i, igLastEndX.i, igLastEndY.i
Global gsrcDC.l, igDeskX.i, igDeskY.i, igLastDeskX.i, igLastDeskY.i
Global sgImageOutFile.s = ""
Global igObjectExists.i = #False
;Editor Font
Global sgFontName.s = "Arial"
Global igFontSize.i = 16
Global igFontColour.i = RGB(0,0,0)
Global igFontStyle.i = (#PB_Font_Bold | #PB_Font_HighQuality + 2)
Global igFontID1.i = LoadFont(1, sgFontName, igFontSize, igFontStyle)
Global igShadow.i = #False
Global sgText.s = "Mary had a little lamb, it's fleece was white as snow. Everywhere that Mary went, the lamb was sure to go."
UsePNGImageDecoder()
UsePNGImageEncoder()
;CreateImage(#IMAGE_Content, 380, 380,32|#PB_Image_Transparent)
CreateImage(#IMAGE_Content, 380, 380, 24)
;Colour Selection Button
igCurrentColour = RGB(191,223,255)
CreateImage(#IMAGE_Color, 40, 15, 24)
If StartDrawing(ImageOutput(#IMAGE_Color))
Box(0, 0, 40, 15, igCurrentColour)
StopDrawing()
EndIf
Procedure SetAlignment(iJustify, iX, iY)
;--------------------------------------
Protected sText.s
sText = GetGadgetText(#TextEditor)
FreeGadget(#TextEditor)
EditorGadget(#TextEditor, igStartX + 25, igStartY + 25, (iX-igStartX) - 30, (iY-igStartY) - 30, iJustify)
SendMessage_(GadgetID(#TextEditor),#EM_SETTARGETDEVICE,#Null,0) ;WordWrap on
SetGadgetColor(#TextEditor,#PB_Gadget_BackColor,igCurrentColour)
SetGadgetFont(#TextEditor,igFontID1)
SetGadgetText(#TextEditor, sText)
EndProcedure
Procedure.l CaptureRegion(iX.i, iY.i, iW.i, iH.i)
;------------------------------------------------
Shared gsrcDC
dm.DEVMODE
gsrcDC = CreateDC_("DISPLAY","","",dm)
trgDC.l = CreateCompatibleDC_(gsrcDC)
BMPHandle.l = CreateCompatibleBitmap_(gsrcDC,iW,iH)
SelectObject_(trgDC,BMPHandle)
BitBlt_(trgDC,0,0,iW,iH,gsrcDC,iX,iY,#SRCCOPY)
DeleteDC_(trgDC)
ReleaseDC_(BMPHandle,srcDC)
ProcedureReturn BMPHandle
EndProcedure
Procedure ImageSave()
;--------------------
Protected iX.i = (igLastDeskX - 5)
Protected iY.i = (igLastDeskY - 5)
Protected iW.i = (igLastEndX - igLastStartX) + 10
Protected iH.i = (igLastEndY - igLastStartY) + 10
If(igShadow = #True) : iW + 4 : iH + 4 : EndIf
ScreenCaptureAddress = CaptureRegion(iX,iY,iW,iH)
If ScreenCaptureAddress <> 0
;CreateImage(#ImageSave,iW,iH,32|#PB_Image_Transparent)
CreateImage(#ImageSave,iW,iH,24)
StartDrawing(ImageOutput(#ImageSave))
DrawImage(ScreenCaptureAddress,0,0)
StopDrawing()
SaveImage(#ImageSave,sgImageOutFile,#PB_ImagePlugin_PNG)
FreeImage(#ImageSave)
DeleteDC_(gsrcDC)
DeleteObject_(ScreenCaptureAddress)
Else
MessageRequester("Image Save Failed", "Cannot save file: " + sgImageOutFile)
EndIf
EndProcedure
Procedure DrawObject(x,y)
;------------------------
Shared igObjectExists, igCurrentColour, igStartX, igStartY, igLastStartX, igLastStartY, igLastEndX, igLastEndY, igDeskX, igDeskY
If StartDrawing(CanvasOutput(#Canvas))
DrawImage(ImageID(#IMAGE_Content), 0, 0)
;Shadow
If(igShadow = #True)
RoundBox(igStartX + 4, igStartY + 4, (x-igStartX), (y-igStartY), 12, 12, RGB(128,128,128))
EndIf
;Outline
RoundBox(igStartX, igStartY, x-igStartX, y-igStartY, 14, 14, RGB(0,0,0))
;Fill
RoundBox(igStartX + 2, igStartY + 2, (x-igStartX) - 4, (y-igStartY) - 4, 12, 12, igCurrentColour)
SetGadgetColor(#TextEditor,#PB_Gadget_BackColor,igCurrentColour)
SetGadgetFont(#TextEditor,igFontID1)
ResizeGadget(#TextEditor,igStartX + 25, igStartY + 25, (x-igStartX) - 30, (y-igStartY) - 30)
SetGadgetText(#TextEditor,sgText)
DrawingMode(#PB_2DDrawing_Default)
StopDrawing()
igLastStartX = igStartX
igLastStartY = igStartY
igLastDeskX = igDeskX
igLastDeskY = igDeskY
igLastEndX = x
igLastEndY = y
igObjectExists = #True
EndIf
EndProcedure
Procedure OpenWin()
;------------------
If OpenWindow(#DynText,0,0,460,400,"Dynamic Caption Box",#PB_Window_Invisible|#PB_Window_SystemMenu|#PB_Window_ScreenCentered)
CanvasGadget(#Canvas, 10, 10, 380, 380, #PB_Canvas_ClipMouse)
ButtonImageGadget(#Color, 400, 10, 50, 25, ImageID(#IMAGE_Color))
ButtonGadget(#Refresh,400, 40, 50, 25, "Refresh")
ButtonGadget(#Shadow, 400, 80, 50, 25, "Shadow")
ButtonGadget(#Ctr, 400, 125, 50, 25, "Ctr Just")
ButtonGadget(#Lft, 400, 150, 50, 25, "Lft Just")
ButtonGadget(#Rgt, 400, 175, 50, 25, "Rgt Just")
ButtonGadget(#Clear, 400, 280, 50, 25, "Clear")
ButtonGadget(#Load, 400, 335, 50, 25, "Load")
ButtonGadget(#Save, 400, 365, 50, 25, "Save")
EditorGadget(#TextEditor,10,10,380,380,#ES_CENTER)
SendMessage_(GadgetID(#TextEditor),#EM_SETTARGETDEVICE,#Null,0) ;WordWrap on
SetGadgetAttribute(#Canvas,#PB_Canvas_Cursor,#PB_Cursor_Default)
HideWindow(#DynText,#False)
EndIf
EndProcedure
Procedure WaitForUser()
;----------------------
Shared igObjectExists
Repeat
iEvent = WaitWindowEvent()
If iEvent = #PB_Event_Gadget
iEventGadget = EventGadget()
Select iEventGadget
Case #Canvas
igEndX = GetGadgetAttribute(#Canvas, #PB_Canvas_MouseX)
igEndY = GetGadgetAttribute(#Canvas, #PB_Canvas_MouseY)
Select EventType()
Case #PB_EventType_LeftButtonDown
;
; This stores the current content of the CanvasGadget in #IMAGE_Content,
; so it can be re-drawn while the mouse moves
;
If StartDrawing(ImageOutput(#IMAGE_Content))
DrawImage(GetGadgetAttribute(#Canvas, #PB_Canvas_Image), 0, 0)
StopDrawing()
EndIf
igDeskX = DesktopMouseX()
igDeskY = DesktopMouseY()
igStartX = igEndX
igStartY = igEndY
DrawObject(igEndX, igEndY)
Case #PB_EventType_LeftButtonUp
DrawObject(igEndX, igEndY)
Case #PB_EventType_MouseMove
If GetGadgetAttribute(#Canvas, #PB_Canvas_Buttons) & #PB_Canvas_LeftButton
DrawObject(igEndX, igEndY)
EndIf
EndSelect
Case #Color
igCurrentColour = ColorRequester(igCurrentColour)
If StartDrawing(ImageOutput(#IMAGE_Color))
Box(0, 0, 40, 15, igCurrentColour)
StopDrawing()
SetGadgetAttribute(#Color, #PB_Button_Image, ImageID(#IMAGE_Color))
EndIf
Case #Shadow
If(igShadow = #False)
igShadow = #True
Else
igShadow = #False
EndIf
If(igObjectExists = #True)
igStartX = igLastStartX
igStartY = igLastStartY
DrawObject(igLastEndX,igLastEndY)
EndIf
Case #Refresh
igStartX = igLastStartX
igStartY = igLastStartY
DrawObject(igLastEndX,igLastEndY)
Case #Ctr
igStartX = igLastStartX
igStartY = igLastStartY
SetAlignment(#ES_CENTER, igLastEndX, igLastEndY)
Case #Lft
igStartX = igLastStartX
igStartY = igLastStartY
SetAlignment(#ES_LEFT, igLastEndX, igLastEndY)
Case #Rgt
igStartX = igLastStartX
igStartY = igLastStartY
SetAlignment(#ES_RIGHT, igLastEndX, igLastEndY)
Case #Clear
If StartDrawing(CanvasOutput(#Canvas))
Box(0, 0, 380, 380, $FFFFFF)
StopDrawing()
EndIf
igObjectExists = #False
Case #Load
File$ = OpenFileRequester("Load Image...", "", "PNG Images|*.png|All Files|*.*", 0)
If File$
If LoadImage(#IMAGE_LoadSave, File$)
If StartDrawing(CanvasOutput(#Canvas))
Box(0, 0, 380, 380, $FFFFFF)
DrawImage(ImageID(#IMAGE_LoadSave), 0, 0)
StopDrawing()
EndIf
FreeImage(#IMAGE_LoadSave)
Else
MessageRequester("Image File", "Cannot load image: " + File$)
EndIf
EndIf
Case #Save
sgImageOutFile = SaveFileRequester("Save Image", sgImageOutFile, "PNG Images|*.png|All Files|*.*", 0)
If sgImageOutFile And (FileSize(sgImageOutFile) = -1 Or MessageRequester("File Exists", "Overwrite this file? " + sgImageOutFile, #PB_MessageRequester_YesNo) = #PB_MessageRequester_Yes)
ImageSave()
EndIf
EndSelect
EndIf
Until iEvent = #PB_Event_CloseWindow
EndProcedure
OpenWin()
WaitForUser()
End
Edit: Slightly refined to be less rubbish
Edit2: Further refinement, plus extra issues!
Edit3: Changed to Kiffy's text alignment method.
New question: I'm saving the Object out as a PNG. As it is an irregular shape, the image has to include some of the background. The apps receiving the image need the background to be transparent. I can set 32bits during image create, but since the save is done via screen capture, the image is actually 24bit. Is there a way to identify the (white) background as being the transparency region so that it is saved as such in a PNG? This is something that image viewers such as IrfanView can accomplish with 24bit images (but with User input)
Edit: Answer: Create a 32bit Image for the screen capture. Before saving, set DrawingMode(#PB_2DDrawing_AlphaChannel), then Fill the background with the colour representing transparency: FillArea(0, 0, igBorderColour, igTransparent) [igTransparent = RGBA(0,0,0,0)]
Last edited by IdeasVacuum on Tue Nov 22, 2011 1:20 am, edited 5 times in total.
IdeasVacuum
If it sounds simple, you have not grasped the complexity.
If it sounds simple, you have not grasped the complexity.
-
IdeasVacuum
- Always Here

- Posts: 6426
- Joined: Fri Oct 23, 2009 2:33 am
- Location: Wales, UK
- Contact:
Re: Thoughts on the best way to define a 'dynamic text box'
Oh-oh, when I do a screen capture (BitBlt), the capture is perfect......... but only the graphic is there, the text is missing
I'm thinking that might be a z-level issue, but how to promote an Editor Gadget to topmost Z?
Edit: Kiffy's text justification enhancement seems to have fixed this problem. Code in 1st post updated again.
I'm thinking that might be a z-level issue, but how to promote an Editor Gadget to topmost Z?
Edit: Kiffy's text justification enhancement seems to have fixed this problem. Code in 1st post updated again.
IdeasVacuum
If it sounds simple, you have not grasped the complexity.
If it sounds simple, you have not grasped the complexity.
