but I think I will need a TCanvas. The component need to have some text display methods.
I was able to fix several problems with the class, e.g. there was a problem setting the font, when using the
TCanvas:SelectFont
help topic. That topic example is
Código: Seleccionar todo
WITH OBJECT oFont := TFont():New()
:Name := "Arial"
:oDevice := Printer
:Create()
END WITH
WITH OBJECT oDevice:oCanvas
:SelectFont( oFont )
.....
END WITH
That problem is fixed, but I have a feeling there will be many more. Would it be possible for you to look this over, and offer some suggestions on how better to structure this? I'm trying to do a Text Mode emulation. I'll share the code when it's done.
Código: Seleccionar todo
/*
* Project: TextModeEmulation
* File: TextModeEmulator.prg
* Description:
* Author:
* Date: 02-18-2025
*/
#include "Xailer.ch"
#define SCREEN_ROWS 25
#define SCREEN_COLS 80
#define CHAR_WIDTH 8 // pixels
#define CHAR_HEIGHT 16 // pixels
CLASS TScreenBuffer
VAR aBuffer // 2D array to hold characters
VAR aAttributes // 2D array to hold color attributes
VAR nCursorRow
VAR nCursorCol
VAR oFont
VAR oWindow // GUI window reference
METHOD New()
METHOD t_DispOut(cText)
METHOD t_QOut(cText)
METHOD t_SetPos(nRow, nCol)
METHOD t_Row()
METHOD t_Col()
METHOD t_SetColor(cColor)
METHOD Refresh() // Updates the GUI display
METHOD ColorToAttribute(cColor)
METHOD AttributeToRGB(nAttr)
METHOD MakeAttribute(nFore, nBack)
METHOD CurrentBackColor()
ENDCLASS
METHOD New() CLASS TScreenBuffer
Local oFont
::aBuffer := Array(SCREEN_ROWS, SCREEN_COLS)
::aAttributes := Array(SCREEN_ROWS, SCREEN_COLS)
::nCursorRow := 0
::nCursorCol := 0
::oFont := TFont():Create( "Courier New", 10 )
// Initialize buffer with spaces
AEval(::aBuffer, {|a| AFill(a, " ")})
/*
WITH OBJECT oFont := TFont():New()
:Name := "Courier New"
:Create()
END WITH
::oFont := oFont
*/
// Create the GUI window with fixed-width font
// ::oWindow := TFORM():New() // Doesn't have TextOut or anything similar.
::oWindow := TCANVAS():New()
// ::oWindow := TWindow():New() // No oWindow
// ::oWindow:Create()
::oWindow:SelectFont( ::oFont )
// ::oWindow:SetFont("Courier New", 10) // Doesn't work.
RETURN Self
METHOD t_DispOut(cText) CLASS TScreenBuffer
LOCAL nLen := Len(cText)
LOCAL i
FOR i := 1 TO nLen
IF ::nCursorCol >= SCREEN_COLS
::nCursorRow++
::nCursorCol := 0
IF ::nCursorRow >= SCREEN_ROWS
// Scroll buffer up
::ScrollUp()
ENDIF
ENDIF
::aBuffer[::nCursorRow + 1, ::nCursorCol + 1] := SubStr(cText, i, 1)
::nCursorCol++
NEXT
::Refresh()
RETURN NIL
METHOD Refresh() CLASS TScreenBuffer
LOCAL nRow, nCol
LOCAL cLine := ""
// Convert buffer to GUI display
// This is where we'd draw to the GUI window
FOR nRow := 1 TO SCREEN_ROWS
cLine := ""
FOR nCol := 1 TO SCREEN_COLS
cLine += ::aBuffer[nRow, nCol]
NEXT
// Draw line to GUI at proper position
::oWindow:TextOut(cLine, ;
nCol * CHAR_WIDTH, ;
nRow * CHAR_HEIGHT)
NEXT
// ::oWindow:Refresh() // There's no Refresh Method in TCanvas
RETURN NIL
METHOD t_QOut(cText) CLASS TScreenBuffer
::t_DispOut(cText + HB_EOL())
RETURN NIL
METHOD t_SetPos(nRow, nCol) CLASS TScreenBuffer
// Ensure coordinates are within bounds
::nCursorRow := Min(Max(nRow, 0), SCREEN_ROWS - 1)
::nCursorCol := Min(Max(nCol, 0), SCREEN_COLS - 1)
// Update cursor position in GUI
// There's No SetCaretxxx method
::oWindow:SetCaretPos(::nCursorCol * CHAR_WIDTH, ;
::nCursorRow * CHAR_HEIGHT)
RETURN NIL
METHOD t_Row() CLASS TScreenBuffer
RETURN ::nCursorRow
METHOD t_Col() CLASS TScreenBuffer
RETURN ::nCursorCol
METHOD t_SetColor(cColor) CLASS TScreenBuffer
LOCAL nFore, nBack
LOCAL cChar
// Parse standard color string (e.g., "W/N" for White on Black)
IF "/"$ cColor
cChar := SubStr(cColor, 1, 1)
nFore := ::ColorToAttribute(cChar)
cChar := SubStr(cColor, At("/", cColor) + 1, 1)
nBack := ::ColorToAttribute(cChar)
ELSE
// If no background specified, use current background
nFore := ::ColorToAttribute(SubStr(cColor, 1, 1))
nBack := ::CurrentBackColor()
ENDIF
// Store current position's attribute
::aAttributes[::nCursorRow + 1, ::nCursorCol + 1] := ;
::MakeAttribute(nFore, nBack)
// Update GUI colors
::oWindow:SetTextColor(::AttributeToRGB(nFore))
::oWindow:SetBkColor(::AttributeToRGB(nBack))
RETURN NIL
// Helper methods for color handling
METHOD ColorToAttribute(cColor) CLASS TScreenBuffer
LOCAL nAttr
SWITCH Upper(cColor)
CASE "N"; nAttr := 0; EXIT // Black
CASE "B"; nAttr := 1; EXIT // Blue
CASE "G"; nAttr := 2; EXIT // Green
CASE "C"; nAttr := 3; EXIT // Cyan
CASE "R"; nAttr := 4; EXIT // Red
CASE "M"; nAttr := 5; EXIT // Magenta
CASE "Y"; nAttr := 6; EXIT // Yellow
CASE "W"; nAttr := 7; EXIT // White
OTHERWISE
nAttr := 7 // Default to white
END
RETURN nAttr
METHOD AttributeToRGB(nAttr) CLASS TScreenBuffer
LOCAL aColors := { ;
{ 0, 0, 0 }, ; // Black
{ 0, 0, 255 }, ; // Blue
{ 0, 255, 0 }, ; // Green
{ 0, 255, 255 }, ; // Cyan
{ 255, 0, 0 }, ; // Red
{ 255, 0, 255 }, ; // Magenta
{ 255, 255, 0 }, ; // Yellow
{ 255, 255, 255 } } // White
RETURN RGB(aColors[nAttr + 1, 1], ;
aColors[nAttr + 1, 2], ;
aColors[nAttr + 1, 3])
METHOD MakeAttribute(nFore, nBack) CLASS TScreenBuffer
// Combine foreground and background into single attribute
RETURN nFore + (nBack * 16)
METHOD CurrentBackColor() CLASS TScreenBuffer
// Extract background color from current position's attribute
RETURN Int(::aAttributes[::nCursorRow + 1, ::nCursorCol + 1] / 16)
FUNCTION TextModeEmulator()
LOCAL oScreen := TScreenBuffer():New()
// These would work similar to their console counterparts
oScreen:t_DispOut("Hello from text-mode emulation!")
oScreen:t_SetPos(5, 10)
oScreen:t_QOut("Positioned text")
RETURN NIL