Updated Version: I added a bit of documentation, fixed a few bugs, added Cut/Copy/Paste-functions and drawing of line numbers. Automatic as well as manual scrolling will be added next.
Afterwards syntax highlighting and rightclick-menu, and then the most important requirements for a code editor are met, I think.
Updated Code:
remstart
Misc:
te_init() - Has to be called once to initialize the required arrays and globals
te_Update(x1,y1) - Draws Text Editor Widget to given screen coordinates and handles any related input
te_ForceUpdate() - Usually not required - forces the editor widget to redraw its content
Editor Window Appearance:
te_SetSize(w,h) - Sets the size of the editor widget (in pixels) (default: 640x320)
te_SetFirstLine(num) - Scrolls the text up/down so that the given line is at the top of the widget (default: 1)
te_SetFirstChar(char) - Scrolls the text right/left so that the given char is at the left side of the widget (default: 0)
te_SetLinenumbers(bool) - if this is set to 1/true, line numbers will be drawn at the left side of the widget (default: true)
te_SetLineNumberWidth(int) - defines the width of the line number bar (default 36px)
Content:
te_ClearText() - Removes any text from the editor widget
te_SetText(string) - Applies a given text string
te_SetTextLine(num, string) - sets a predefined text line
te_InsertLine(num, string) - Inserts a new line of text at the given position
te_AppendLine(string) - adds a line of text to the end of the content
te_DeleteLine(num) - removes a line of text
te_InsertAtCursor(string) - inserts a string at the cursor position and moves the cursor forward
te_InsertAtCursorMultiple(string) - same as te_InsertAtCursor, but is able to insert multiple lines of text (separated by te_CR)
te_RemoveSel() - Removes the selected block of text from the editor widget's content
string = te_GetText() - Returns the editor widget's content
string = te_GetTextLine(num) - returns a predefined text line
string = te_GetSelectedText() - returns the text that is currently selected inside the editor widget (or empty string if nothing is selected)
Cursor/Selection:
te_SetCursor(line, char) - Sets the cursor to a given position (line between 1 and max line, char between 0 and max char in that line)
te_SetSelection(startline, startchar, endline, endchar) - selects an area of text
te_Deselect() - Removes the selection
te_MoveCursor(chars) - Moves the cursor a given number of chars forward or backward (if number is negative)
te_MoveCursorUp() - Moves the cursor one line up
te_MoveCursorDown() - Moves the cursor one line down
remend
rem start
rem Showcase
rem Init Screen
set display mode 1024,768,32
set window on
sync on
sync rate 0
sync
rem Init Libraries
gl_inp_init()
te_init()
te_SetSize(800,512)
t$ = "set display mode 800,600,32" + te_CR + "sync on" + te_CR + "sync rate 120" + te_CR + te_CR + "REM Example Code"
t$ = t$ + te_CR + " make object cube 1, 100" + te_CR + te_CR + "REM Main Loop" + te_CR + "do" + te_CR
t$ = t$ + " rotate object 1, 200*sin(timer()*0.1), object angle y(1)+0.5, 30*sin(timer()*0.06)" + te_CR
t$ = t$ + " sync" + te_CR + "loop"
te_SetText(t$)
te_SetCursor(2,1)
rem Main Loop
do
cls
te_Update(112,20)
ink 0xFFFFFFFF,0
print "FPS: ", screen fps()
sync
loop
end
rem Implementation required due to gl_input-Library:
function gl_user_KeyHandler(code as integer)
rem ...
endfunction
rem End of Showcase
rem end
rem Configuration
rem Useful Stuff
#constant te_CR = chr$(13)+chr$(10)
rem Configuration
#constant te_TAB_SPACES = 3
rem DBP-Internal
#constant te_MIN_BITMAP_ID = 20
rem Widget Appearance
#constant te_DEFAULT_WIDTH = 640
#constant te_DEFAULT_HEIGHT = 320
rem Text Appearance
#constant te_LINE_HEIGHT = 16
#constant te_TEXT_SIZE = 16
#constant te_TEXT_FONT = "Courier New"
rem Mouse Behaviour
#constant te_DOUBLE_CLICK_DELAY = 500
#constant te_MS_DEFAULT = 0
#constant te_MS_SELECTION = 1
#constant te_MS_DRAG = 2
rem Colors
#constant te_COLOR_FRAME = 0xFFFFFFFF
#constant te_COLOR_BACK = 0xFFC0C0C0
#constant te_COLOR_SELECTION = 0xFF808080
#constant te_COLOR_TEXT = 0xFF000000
#constant te_COLOR_CURSOR_DARK = 0xFF000000
#constant te_COLOR_CURSOR_LIGHT = 0
#constant te_COLOR_LINEBACK = 0xFF303030
#constant te_COLOR_LINEFRAME = 0xFF000000
#constant te_COLOR_LINETEXT = 0xD0FFFFFF
function te_init()
rem Configuration
global te_TabSpaces as integer = te_TAB_SPACES
global te_DrawLineNumbers as boolean = 1
global te_LineNumberWidth as integer = 36
rem Kind of Constant Attributes
global te_Width as integer = 0
global te_Height as integer = 0
global te_Bitmap as integer = 0
rem Dynamically changing Attributes
global te_FirstLine as integer = 0 `Scroll Y
global te_FirstChar as integer = 0 `Scroll X
rem Regarding Text
global te_LineCount as integer = 0
dim te_Text(1) as te_TextType
rem Drawing
global te_SX1 as integer `Screen-space Coordinates
global te_SY1 as integer
global te_SX2 as integer
global te_SY2 as integer
rem Cursor
global te_CurrentLine as integer = 0
global te_CurrentChar as integer = 0
global te_SelStartLine as integer = 0
global te_SelStartChar as integer = 0
global te_SelEndLine as integer = 0
global te_SelEndChar as integer = 0
global te_MouseState as byte = te_MS_DEFAULT
global te_CursorTimeOffset as integer = 0
rem Misc
global te_forceDrawUpdate as boolean = 1
global te_StoreX as integer = 0
global te_StoreY as integer = 0
rem Initialize
te_Bitmap = te__GetFreeBitmap()
te_SetSize(te_DEFAULT_WIDTH, te_DEFAULT_HEIGHT)
te_SetText("")
te_SetFirstLine(1)
te_SetFirstChar(1)
te_InsertLine(1,"")
te__SetFont(te_TEXT_SIZE, te_TEXT_FONT)
endfunction
type te_TextType
s as string
l as integer `length
w as integer `width
endtype
rem This function automatically draws and updates the editor widget
rem Call it once from within your main loop to draw and update the text editor at the given position
rem sx1, sy1 define the top left corner of the editor window
function te_Update(sx1,sy1)
sx2 = sx1 + te_Width : sy2 = sy + te_Height
te_SX1 = sx1 + te_DrawLineNumbers*te_LineNumberWidth
te_SY1 = sy1 : te_SX2 = sx2 : te_SY2 = sy2
rem Update Input
gl_inp_Update(0)
c1 = te__HandleMouse()
c2 = te__HandleKeyboard()
rem Redraw?
if c1 or c2 or te_forceDrawUpdate
te_forceDrawUpdate = 0
old_bitmap = current bitmap()
set current bitmap te_Bitmap
rem Local Bitmap-Space coordinates
x1 = 0 : y1 = 0 : x2 = te_Width : y2 = te_Height
rem Line Numbers
if te_DrawLineNumbers
tx1 = x1 + te_LineNumberWidth
else
tx1 = x1
endif
rem Draw Editor Elements
te__DrawBack(x1,y1,x2,y2)
te__DrawSelection(tx1,y1,x2,y2)
te__DrawText(tx1,y1,x2,y2)
te__DrawScrollbars(x1,y1,x2,y2)
if te_DrawLineNumbers then te__DrawLineNumbers(x1,y1,x1+te_LineNumberWidth,y2)
te__DrawFrame(x1,y1,x2,y2)
rem Reset Bitmap Focus
set current bitmap old_bitmap
endif
rem Draw to Screen
copy bitmap te_Bitmap, 0,0,te_Width,te_Height, old_bitmap, sx1,sy1,sx2,sy2
rem Cursor
te__DrawCursor(sx1,sy1,sx2,sy2)
endfunction
function te_SetLineNumbers(drawthem as boolean)
te_DrawLineNumbers = drawthem
endfunction
function te_SetLineNumberWidth(width)
te_LineNumberWidth = width
endfunction
function te_ClearText()
undim te_Text()
dim te_Text(1) as te_TextType
te_LineCount = 0
te_ForceUpdate()
endfunction
function te_SetText(s$)
rem Clear
te_ClearText()
rem Parse String for line breaks
s$ = s$ + chr$(13)+chr$(10)
L = len(s$)
cur$ = ""
for i = 1 to L
m$ = mid$(s$,i) : a = asc(m$)
if a = 13 or a = 10
te_AppendLine(cur$)
cur$ = ""
inc i
else
cur$ = cur$ + m$
endif
next i
te_ForceUpdate()
endfunction
function te_GetText()
s$ = ""
for i = 1 to te_LineCount
s$ = s$ + te_Text(i).s
next i
endfunction s$
function te_SetTextLine(L,s$)
te_Text(L).s = s$
te_Text(L).l = len(s$)
te_Text(L).w = te__GetTextWidth(s$)
te_ForceUpdate()
endfunction
function te_GetTextLine(L)
endfunction
function te_GetSelectedText()
if te_SelStartLine = te_SelEndLine
s$ = right$(left$(te_Text(te_SelStartLine).s, te_SelEndChar), te_SelEndChar-te_SelStartChar)
else
s$ = right$(te_Text(te_SelStartLine).s, te_Text(te_SelStartLine).L - te_SelStartChar)
for L = te_SelStartLine+1 to te_SelEndLine-1
s$ = s$ + te_CR + te_Text(L).s
next L
s$ = s$ + te_CR + left$(te_Text(te_SelEndLine).s, te_SelEndChar)
endif
endfunction s$
function te_InsertLine(l,s$)
inc te_LineCount
array insert at element te_Text(), l
te_SetTextLine(l, s$)
te_ForceUpdate()
endfunction
function te_AppendLine(s$)
te_InsertLine(te_LineCount+1, s$)
te_ForceUpdate()
endfunction
function te_DeleteLine(l)
if l > 0 and l <= te_LineCount
dec te_LineCount
array delete element te_Text(), l
endif
te_ForceUpdate()
endfunction
function te_SetCursor(y,x)
te_CurrentLine = y
te_CurrentChar = x
te_Deselect()
endfunction
function te_SetSelection(y1,x1, y2,x2)
te_SelStartLine = y1
te_SelStartChar = x1
te_SelEndLine = y2
te_SelEndChar = x2
te_ForceUpdate()
endfunction
function te_Deselect()
te_SetSelection(te_CurrentLine,te_CurrentChar, te_CurrentLine,te_CurrentChar)
te_ForceUpdate()
endfunction
function te_SetFirstLine(l)
te_FirstLine = l
te_ForceUpdate()
endfunction
function te_SetFirstChar(c)
te_FirstChar = c
te_ForceUpdate()
endfunction
function te_SetSize(w,h)
te_Width = w
te_Height = h
old_bitmap = current bitmap()
if bitmap exist(te_Bitmap) then delete bitmap te_Bitmap
create bitmap te_Bitmap, w, h
set current bitmap old_bitmap
te_ForceUpdate()
endfunction
function te_ForceUpdate()
te_ForceDrawUpdate = 1
endfunction
function te_MoveCursor(d)
if d > 0
for i = 1 to d : te__MoveCursorForward() : next d
else
for i = d to -1 : te__MoveCursorBackward() : next d
endif
endfunction
function te__MoveCursorForward()
inc te_CurrentChar
if te_CurrentChar > te_Text(te_CurrentLine).L
if te_CurrentLine < te_LineCount
te_SetCursor(te_CurrentLine+1, 0)
else
dec te_CurrentChar
endif
endif
endfunction
function te__MoveCursorBackward()
dec te_CurrentChar
if te_CurrentChar < 0
if te_CurrentLine > 1
te_SetCursor(te_CurrentLine-1, te_Text(te_CurrentLine-1).L)
else
inc te_CurrentChar
endif
endif
endfunction
function te_MoveCursorUp()
if te_CurrentLine > 1
dec te_CurrentLine
if te_CurrentChar > te_Text(te_CurrentLine).l then te_SetCursor(te_CurrentLine, te_Text(te_CurrentLine).l)
else
te_SetCursor(te_CurrentLine, 0)
endif
endfunction
function te_MoveCursorDown()
if te_CurrentLine < te_LineCount
inc te_CurrentLine
if te_CurrentChar > te_Text(te_CurrentLine).l then te_SetCursor(te_CurrentLine, te_Text(te_CurrentLine).l)
else
te_SetCursor(te_CurrentLine, te_Text(te_CurrentLine).l)
endif
endfunction
function te_RemoveAtCursor(offset)
if te_CurrentChar = 0 and offset = 0
cur = te_Text(te_CurrentLine-1).L
te_SetTextLine(te_CurrentLine-1, te_Text(te_CurrentLine-1).s + te_Text(te_CurrentLine).s)
te_SetCursor(te_CurrentLine-1, cur+1)
te_DeleteLine(te_CurrentLine+1)
else
if te_CurrentChar >= te_Text(te_CurrentLine).L and offset = 1
te_SetTextLine(te_CurrentLine, te_Text(te_CurrentLine).s + te_Text(te_CurrentLine+1).s)
te_DeleteLine(te_CurrentLine+1)
else
te_SetTextLine(te_CurrentLine, left$(te_Text(te_CurrentLine).s, te_CurrentChar - 1 + offset) + right$(te_Text(te_CurrentLine).s, te_Text(te_CurrentLine).l - te_CurrentChar - offset) )
endif
endif
endfunction
function te_InsertAtCursor(s$)
te_SetTextLine(te_CurrentLine, left$(te_Text(te_CurrentLine).s, te_CurrentChar) + s$ + right$(te_Text(te_CurrentLine).s, te_Text(te_CurrentLine).l - te_CurrentChar) )
te_MoveCursor(len(s$))
endfunction
function te_InsertAtCursorMultiple(s$)
s$ = s$ + te_CR
L = len(s$)
for i = 1 to L
m$ = mid$(s$,i)
a = asc(m$)
if a = 13
te_InsertAtCursor(left$(s$,i-1))
if i < L-1 then te_InsertLine(te_CurrentLine+1,"") : te_SetCursor(te_CurrentLine+1,0)
s$ = right$(s$,L-i-1)
dec L, i+1
i = 0
endif
next i
endfunction
function te_RemoveSel()
rem Remove lines in between
l1 = te_SelStartLine : l2 = te_SelEndLine
c1 = te_SelStartChar : c2 = te_SelEndChar
if l1 = l2
if c1 = c2 then exitfunction
if c1 > c2 then ct = c1 : c1 = c2 : c2 = ct
te_SetTextLine(l1, left$(te_Text(l1).s,c1) + right$(te_Text(l1).s, te_Text(l1).l-c2))
else
if l1 > l2 then lt = l1 : l1 = l2 : l2 = lt : ct = c1 : c1 = c2 : c2 = ct
for l = l2-1 to l1+1 step -1
te_DeleteLine(l)
next l
rem Remove selected parts of border lines
te_SetTextLine(l1, left$(te_Text(l1).s,c1) + right$(te_Text(l1+1).s, te_Text(l1+1).l-c2))
rem te_SetTextLine(l1+1, )
te_DeleteLine(l1+1)
endif
te_SetCursor(l1, c1)
te_SetSelection(l1,c1,l1,c1)
endfunction
function te__HandleMouse()
select te_MouseState
rem **No mousekey pressed**
case te_MS_DEFAULT
rem Leftclick
if gl_inp_Mouseclick(1) = gl_inp__KEYHIT
rem Single or Double Leftclick?
if gl_inp_time > te_LastClickTime + te_DOUBLE_CLICK_DELAY
rem Single Leftclick
te_LastClickTime = gl_inp_time
te__ScreenToEditor(gl_inp_MX-te_SX1, gl_inp_MY-te_SY1)
te_SetCursor(te_StoreY, te_StoreX)
te_SetSelection(te_StoreY, te_StoreX, te_StoreY, te_StoreX)
te_MouseState = te_MS_SELECTION
else
rem Double Leftclick
te_LastClickTime = 0
rem Find Left and Right anchor point for selection
rem ...
te_SetSelection(L,x1,L,x2)
te_SetCursor(L,x2)
endif
else
rem Mouse Moved -> No double click possible
if gl_inp_MMX <> 0 or gl_inp_MMY <> 0 then te_LastClickTime = 0
endif
rem Rightclick
if gl_inp_Mouseclick(2) = gl_inp__KEYHIT
rem ...
endif
endcase
rem Leftkey is hold
case te_MS_SELECTION
rem Update
te__ScreenToEditor(gl_inp_MX-te_SX1, gl_inp_MY-te_SY1)
s1 = te_SelStartLine : s2 = te_SelStartChar
te_SetCursor(te_StoreY, te_StoreX)
te_SetSelection(s1, s2, te_StoreY, te_StoreX)
rem Key Release
if gl_inp_Mouseclick(1) = gl_inp__KEYRELEASE
te_MouseState = te_MS_DEFAULT
endif
endcase
case te_MS_DRAG
endcase
endselect
rem Mouse Visibility
changed = 0
if mouseclick()
te__MakeCursorVisible()
changed = 1
endif
endfunction changed
function te__HandleKeyboard()
rem gl_inp_SetCursor(te_CurrentChar, te_CurrentChar)
t$ = te_Text(te_CurrentLine).s
te_Input()
rem te_SetCursor(te_CurrentLine, gl_inp_CursorStart)
rem String Changed?
changed = 0
if t$ <> te_Text(te_CurrentLine).s
te__MakeCursorVisible()
changed = 1
else
if scancode() then te__MakeCursorVisible() : changed = 1
endif
endfunction changed
function te__DrawBack(x1,y1,x2,y2)
ink te_COLOR_BACK, 0
box x1+1, y1+1, x2-1, y2-1
endfunction
function te__DrawSelection(x1,y1,x2,y2)
sx1 = te_SelStartChar : sy1 = te_SelStartLine
sx2 = te_SelEndChar : sy2 = te_SelEndLine
rem Is something selected?
if sx1 <> sx2 or sy1 <> sy2
rem Bring selection-boundaries in right order
if sy2 < sy1 or sy2 = sy1 and sx2 < sx1
tx = sx1 : sx1 = sx2 : sx2 = tx
ty = sy1 : sy1 = sy2 : sy2 = ty
endif
rem Multiple Lines?
ink te_COLOR_SELECTION, 0
if sy2 > sy1
te__DrawSelectionBox(x1,y1,x2,y2, sy1, sx1, te_Text(sy1).l)
for l = sy1+1 to sy2-1
te__DrawSelectionBox(x1,y1,x2,y2, l,0,te_Text(l).l)
next l
te__DrawSelectionBox(x1,y1,x2,y2, sy2, 0, sx2)
else
te__DrawSelectionBox(x1,y1,x2,y2, sy1, sx1,sx2)
endif
endif
endfunction
function te__DrawSelectionBox(x1,y1,x2,y2, L,c1,c2)
y = y1 + 3 + te_LINE_HEIGHT * (L - te_FirstLine)
xL = x1 + 3 + te__GetTextWidth( right$(left$(te_Text(L).s, c1), c1-te_FirstChar+1) )
xR = xL + te__GetTextWidth( right$(left$(te_Text(L).s, c2), c2-c1) )
box xL, y, xR, y+te_LINE_HEIGHT
endfunction
function te__DrawText(x1,y1,x2,y2)
tx = x1+3 : ty = y1 + 3
te__ApplyTextColor(te_COLOR_TEXT)
for l = te_FirstLine to te_LineCount
rem Print
te__Text( right$(te_Text(l).s, te_Text(l).l - te_FirstChar + 1), tx, ty )
rem Leave?
if ty >= y2 then exit
rem Next Line
inc ty, te_LINE_HEIGHT
next l
endfunction
function te__DrawScrollbars(x1,y1,x2,y2)
rem ...
endfunction
function te__DrawLineNumbers(x1,y1,x2,y2)
ink te_COLOR_LINEBACK, 0
box x1,y1,x2,y2
ink te_COLOR_LINEFRAME, 0
box x2-1,y1,x2,y2
ink te_COLOR_LINETEXT, 0
cury = y1+3
L = te_FirstLine
repeat
t$ = str$(L)
tx = x2-5-te__GetTextWidth(t$)
te__Text(t$, tx, cury)
rem Next Line
inc L
inc cury, te_LINE_HEIGHT
until cury >= y2 or L > te_LineCount
endfunction
function te__DrawFrame(x1,y1,x2,y2)
ink te_COLOR_FRAME, 0
box x1,y1,x1+1,y2
box x1,y1,x2,y1+1
box x1,y2-1,x2,y2
box x2-1,y1,x2,y2
endfunction
function te__DrawCursor(x1,y1,x2,y2)
te__EditorToScreen(te_CurrentChar, te_CurrentLine)
if ((gl_inp_time - te_CursorTimeOffset) mod 800) < 400
ink te_COLOR_CURSOR_DARK, 0
else
if te_COLOR_CURSOR_LIGHT = 0 then exitfunction else ink te_COLOR_CURSOR_LIGHT, 0
endif
ink rgb(c,c,c),0
x = te_StoreX + te_SX1
y = te_StoreY + te_SY1 + 1
box x,y, x+2, y + te_LINE_HEIGHT-2
endfunction
rem This function makes sure that the cursor will not be hidden
rem e.g. used after the cursor is moved
function te__MakeCursorVisible()
te_CursorTimeOffset = gl_inp_time
endfunction
function te__EditorToScreen(C, L)
te_StoreY = 3 + te_LINE_HEIGHT*(L-te_FirstLine)
te_StoreX = 3 + te__GetTextWidth( right$(left$(te_Text(L).s,C), C-te_FirstChar+1) )
endfunction
function te__ScreenToEditor(xrel, yrel)
te_StoreY = te_FirstLine + (yrel-3)/te_LINE_HEIGHT
if te_StoreY > te_LineCount then te_StoreY = te_LineCount else if te_StoreY < 1 then te_StoreY = 1
rem Get Char of that Line
`dec xrel, 3
if te_Text(te_StoreY).W > xrel
for i = 1 to te_Text(te_StoreY).L
w = te__GetTextWidth( left$(te_Text(te_StoreY).s, i) )
if w >= xrel then exit
next i
te_StoreX = i-1
else
te_StoreX = te_Text(te_StoreY).L
endif
endfunction
function te_Input()
rem Timer
if gl_inp_scancode = 0
gl_inp_KeyTimer = gl_inp_Time
gl_inp_KeyTimerTemp = 0
endif
rem Input
e$ = entry$()
clear entry buffer
L = len(e$)
for i = 1 to L
m$ = mid$(e$,i)
a = asc(m$)
rem Usual Characters
if a >= 32 then te_RemoveSel() : te_InsertAtCursor(m$) : te_Deselect()
`s$ = L$ + m$ + R$ : gl_inp_SetCursor(gl_inp_CursorStart+1, 0)
rem Backspace
if a = 8
if te_SelStartChar <> te_SelEndChar or te_SelStartLine <> te_SelEndLine
te_RemoveSel()
else
te_RemoveAtCursor(0)
te_MoveCursor(-1)
endif
te_Deselect()
endif
rem CTRL-Backspace
if a = 127
te_RemoveAtCursor(0)
rem Find Previous whitespace or linebreak
endif
rem Return
if a = 13
te_RemoveSel()
l$ = left$(te_Text(te_CurrentLine).s, te_CurrentChar)
r$ = right$(te_Text(te_CurrentLine).s, te_Text(te_CurrentLine).L-te_CurrentChar)
te_SetTextLine(te_CurrentLine,l$)
te_InsertLine(te_CurrentLine+1,r$)
te_SetCursor(te_CurrentLine+1,0)
te_Deselect()
endif
rem TAB
if a = 9
nexttab = ((te_CurrentChar/te_TabSpaces)+1)*te_TabSpaces
spaces = nexttab - te_CurrentChar
for i = 1 to spaces
te_InsertAtCursor(" ")
next i
rem te_MoveCursor(spaces)
endif
rem Select All
if a = 1 then te_SetCursor(te_LineCount, te_Text(te_LineCount).L) : te_SetSelection(1,0, te_LineCount, te_Text(te_LineCount).L)
rem Cut/CopyPaste
if a = 22
rem Paste
te_RemoveSel()
te_InsertAtCursorMultiple(get clipboard$())
endif
if a = 3
rem Copy
write to clipboard te_GetSelectedText()
endif
if a = 24
rem Cut
write to clipboard te_GetSelectedText()
te_RemoveSel()
endif
next i
rem Function Keys with manual time coding
if L = 0 and gl_inp_Time >= gl_inp_KeyTimer
rem Prepare
tmp = 0
rem Delete
if gl_inp_Scancode = 211
if te_SelStartChar <> te_SelEndChar or te_SelStartLine <> te_SelEndLine
te_RemoveSel()
else
te_RemoveAtCursor(1)
endif
te_Deselect()
`s$ = L$ + right$(R$, len(R$)-1)
`gl_inp_SetCursor(gl_inp_CursorStart, gl_inp_CursorStart)
tmp = 1
endif
rem Right/Left
rl = rightkey() - leftkey()
if rl
if shiftkey()
s1 = te_SelStartLine : s2 = te_SelStartChar
te_MoveCursor(rl)
te_SetSelection(s1,s2, te_CurrentLine, te_CurrentChar)
`gl_inp_SetCursorPos(gl_inp_Cursor + rl)
else
te_MoveCursor(rl)
te_Deselect()
`gl_inp_SetCursor(gl_inp_Cursor + rl, 0)
endif
tmp = 2
endif
rem Up/Down
ud = upkey() - downkey()
if ud
s1 = te_SelStartLine : s2 = te_SelStartChar
if ud > 0 then te_MoveCursorUp() else te_MoveCursorDown()
if shiftkey()
te_SetSelection(s1,s2, te_CurrentLine, te_CurrentChar)
else
te_Deselect()
endif
tmp = 3
endif
rem Apply Delay
if tmp > 0
if gl_inp_KeyTimerTemp = 0
gl_inp_KeyTimer = gl_inp_Time + gl_inp_INITIAL_KEY_DELAY
else
gl_inp_KeyTimer = gl_inp_Time + gl_inp_ONGOING_KEY_DELAY
endif
gl_inp_KeyTimerTemp = tmp
else
gl_inp_KeyTimer = gl_inp_Time
gl_inp_KeyTimerTemp = 0
endif
else
if gl_inp_Scancode <> 211 and rightkey()=0 and leftkey()=0 and upkey()=0 and downkey()=0
gl_inp_KeyTimer = gl_inp_Time
gl_inp_KeyTimerTemp = 0
endif
endif
endfunction
function te__GetFreeBitmap()
ID = te_MIN_BITMAP_ID
while bitmap exist(ID) : inc ID : endwhile
endfunction ID
function te__SetFont(size,f$)
set text size size
set text font f$
endfunction
function te__GetTextWidth(s$)
w = text width(s$)
endfunction w
function te__Text(s$,x,y)
text x,y,s$
endfunction
function te__ApplyTextColor(color as dword)
ink color, 0
endfunction
#constant gl_inp__KEYUP 0
#constant gl_inp__KEYDOWN 1
#constant gl_inp__KEYHIT 2
#constant gl_inp__KEYRELEASE -1
#constant gl_inp__NONE 42
#constant gl_inp_MOUSEKEYS 5
rem **Scancodes**
#constant gl_key_1 2
#constant gl_key_Q 16
#constant gl_key_A 30
#constant gl_key_Y 44
#constant gl_key_SPACE 57
#constant gl_key_ESC 1
#constant gl_key_ESCAPE 1
#constant gl_key_RETURN 28
#constant gl_key_TAB 15
#constant gl_key_CAPSLOCK 58
#constant gl_key_LSHIFT 42
#constant gl_key_SHIFT 42
#constant gl_key_LCTRL 29
#constant gl_key_LCONTROL 29
#constant gl_key_CTRL 29
#constant gl_key_CONTROL 29
#constant gl_key_LALT 56
#constant gl_key_ALT 56
#constant gl_key_WINDOWS 219
#constant gl_key_RALT 184
#constant gl_key_RCTRL 157
#constant gl_key_RCONTROL 157
#constant gl_key_RSHIFT 54
#constant gl_key_MENU 221
#constant gl_key_BACKSPACE 14
#constant gl_key_LEFT 203
#constant gl_key_RIGHT 205
#constant gl_key_UP 200
#constant gl_key_DOWN 208
#constant gl_key_PAGEUP 201
#constant gl_key_PAGEDOWN 209
#constant gl_key_HOME 199
#constant gl_key_END 207
#constant gl_key_F1 59
#constant gl_key_F2 60
#constant gl_key_F3 61
#constant gl_key_F4 62
#constant gl_key_F5 63
#constant gl_key_F6 64
#constant gl_key_F7 65
#constant gl_key_F8 66
#constant gl_key_F9 67
#constant gl_key_F10 68
#constant gl_key_F11 87
#constant gl_key_F12 88
#constant gl_key_NUM 69
#constant gl_key_ROLL 70
#constant gl_key_PRINT 183
#constant gl_key_INSERT 210
#constant gl_key_PAUSE 197
#constant gl_key_BREAK 198
#constant gl_key_DELETE 211
#constant gl_inp_INITIAL_KEY_DELAY = 320
#constant gl_inp_ONGOING_KEY_DELAY = 24
function gl_inp_init()
global gl_inp_initialized as boolean = 1
dim gl_inp_MouseClick(gl_inp_MOUSEKEYS) as integer
global gl_inp_Time as integer = 0 : gl_inp_Time = timer()
global gl_inp_MX as integer = 0
global gl_inp_MY as integer = 0
global gl_inp_MMX as integer = 0
global gl_inp_MMY as integer = 0
global gl_inp_MMZ as integer = 0
global gl_inp_Keys as integer = 0
dim gl_inp_Key(0) as gl_inp_KeyType
dim gl_inp_KeyList(255) as gl_inp_KeyListType //Used for Direct Access via gl_inp_GetKey(code)
global gl_inp_Scancode as integer = 0 `Stores scancode()-value
global gl_inp_Cursor as integer = 0 `absolute position for fixed cursor (either start of end pos)
global gl_inp_CursorStart as integer = 0 `number defines how many characters are in front of it: "012_" would be pos 3
global gl_inp_CursorEnd as integer = 0 `start = end means that selection is empty, position only
global gl_inp_KeyTimer as integer = 0
global gl_inp_KeyTimerTemp as integer = 0
global gl_inp_FunctionKeys as byte = 0
for i = 0 to 255 : gl_inp_KeyList(i).prevState = 0 : gl_inp_KeyList(i).nextTime = 0 : next i
endfunction
type gl_inp_KeyType
code as byte
state as integer
event as integer
condition as byte
endtype
type gl_inp_KeyListType
prevState as integer
nextTime as integer
endtype
function gl_inp_Update(updateKeyHandlers as boolean)
gl_inp_Time = timer()
rem **Mouse-Keys**
mc = mouseclick()
for c = 1 to gl_inp_MOUSEKEYS
gl_inp_MouseClick(c) = gl_inp_Transition( gl_inp_MouseClick(c), mc mod 2 )
rem next Dimension
mc = mc/2
next c
rem **Position**
gl_inp_MX = mousex()
gl_inp_MY = mousey()
rem **Mouse-Movement**
gl_inp_MMX = mousemovex()
gl_inp_MMY = mousemovey()
gl_inp_MMZ = mousemovez()
rem **Keyboard**
gl_inp_FunctionKeys = controlkey() + 2*(keystate(gl_key_LALT) or keystate(gl_key_RALT)) + 4*shiftkey()
gl_inp_Scancode = scancode()
if updateKeyHandlers
for k = 1 to gl_inp_Keys
if (gl_inp_FunctionKeys && gl_inp_Key(k).condition) = gl_inp_Key(k).condition
gl_inp_Key(k).state = gl_inp_Transition( gl_inp_Key(k).state, keystate(gl_inp_Key(k).code) )
if gl_inp_Key(k).state = gl_inp_Key(k).event then gl_user_keyHandler(k)
else
gl_inp_Key(k).state = 0
endif
next k
endif
endfunction
function gl_inp_RegisterKey(code,event)
inc gl_inp_Keys
array insert at bottom gl_inp_Key()
ID = gl_inp_Keys
gl_inp_Key(ID).code = code
gl_inp_Key(ID).state = 0
gl_inp_Key(ID).event = event
gl_inp_Key(ID).condition = 0
endfunction ID
function gl_inp_SetKeyCondition(ID, cond)
gl_inp_Key(ID).condition = cond
endfunction
function gl_inp_GetCondition(ctrl as boolean, alt as boolean, shift as boolean)
cond = ctrl + 2*alt + 4*shift
endfunction cond
function gl_inp_Transition(prev,new)
if new
if prev<=0
new=2
else
new=1
endif
else
if prev > 0
new = -1
else
new = 0
endif
endif
endfunction new
function gl_inp_GetKey(code as integer)
r = gl_inp_Transition( gl_inp_KeyList(code).prevState, keystate(code) )
gl_inp_KeyList(code).prevState = r
endfunction r
function gl_inp_GetKeyRegular(code as integer)
r = gl_inp_GetKey(code)
ret = 0
if r = gl_inp__KEYDOWN
if gl_inp_time >= gl_inp_KeyList(code).nextTime
inc gl_inp_KeyList(code).nextTime, gl_inp_ONGOING_KEY_DELAY
if gl_inp_KeyList(code).nextTime < gl_inp_time
gl_inp_KeyList(code).nextTime = gl_inp_time
endif
ret = 1
endif
else
if r = gl_inp__KEYHIT
gl_inp_KeyList(code).nextTime = gl_inp_time + gl_inp_INITIAL_KEY_DELAY
ret = 1
endif
endif
endfunction ret
function gl_inp_SpecialKeysPressed(ctrl as boolean, alt as boolean, shift as boolean)
tmp = gl_inp_GetCondition(ctrl,alt,shift)
r = ((gl_inp_FunctionKeys && tmp) = tmp)
endfunction r
function gl_inp_NumInput(s$)
e$ = entry$()
clear entry buffer
L = len(e$)
for i = 1 to L
m$ = mid$(e$,i)
a = asc(m$)
if a >= 48 and a <= 57 then s$ = s$ + m$
if a = 8 then s$ = left$(s$,len(s$)-1)
next i
endfunction s$
function gl_inp_Input(s$)
e$ = entry$()
clear entry buffer
L = len(e$)
rem For all new characters (usually 0, sometimes 1, rarely more when program laggs)
for i = 1 to L
m$ = mid$(e$,i)
a = asc(m$)
rem Usual Characters
if a >= 32 then s$ = s$ + m$
rem Backspace
if a = 8 then s$ = left$(s$,len(s$)-1)
next i
endfunction s$
function gl_inp_CursorInput(s$)
rem Timer
if gl_inp_scancode = 0
gl_inp_KeyTimer = gl_inp_Time
gl_inp_KeyTimerTemp = 0
endif
rem Input
e$ = entry$()
clear entry buffer
L = len(e$)
for i = 1 to L
m$ = mid$(e$,i)
a = asc(m$)
L$ = left$(s$,gl_inp_CursorStart)
R$ = right$(s$, len(s$)-gl_inp_CursorEnd)
rem Usual Characters
if a >= 32 then s$ = L$ + m$ + R$ : gl_inp_SetCursor(gl_inp_CursorStart+1, 0)
rem Backspace
if a = 8 then s$ = left$(L$, len(L$)-1) + R$ : gl_inp_SetCursor(gl_inp_CursorStart-1, 0)
rem Select All
if a = 1 then gl_inp_SetCursor(0,len(s$))
next i
rem Function Keys with manual time coding
if L = 0 and gl_inp_Time >= gl_inp_KeyTimer
rem Prepare
tmp = 0
L$ = left$(s$,gl_inp_CursorStart)
R$ = right$(s$, len(s$)-gl_inp_CursorEnd)
rem Delete
if gl_inp_Scancode = 211
s$ = L$ + right$(R$, len(R$)-1)
gl_inp_SetCursor(gl_inp_CursorStart, gl_inp_CursorStart)
tmp = 1
endif
rem Right/Left
rl = rightkey() - leftkey()
if rl
if shiftkey()
gl_inp_SetCursorPos(gl_inp_Cursor + rl)
else
gl_inp_SetCursor(gl_inp_Cursor + rl, 0)
endif
tmp = 2
endif
rem Apply Delay
if tmp > 0
if gl_inp_KeyTimerTemp = 0
gl_inp_KeyTimer = gl_inp_Time + gl_inp_INITIAL_KEY_DELAY
else
gl_inp_KeyTimer = gl_inp_Time + gl_inp_ONGOING_KEY_DELAY
endif
gl_inp_KeyTimerTemp = tmp
else
gl_inp_KeyTimer = gl_inp_Time
gl_inp_KeyTimerTemp = 0
endif
else
if gl_inp_Scancode <> 211 and rightkey()=0 and leftkey()=0
gl_inp_KeyTimer = gl_inp_Time
gl_inp_KeyTimerTemp = 0
endif
endif
rem Limit Cursor
L = len(s$)
if gl_inp_Cursor > L then gl_inp_Cursor = L
if gl_inp_cursorEnd > L then gl_inp_CursorEnd = L
if gl_inp_cursorStart > L then gl_inp_cursorStart = L
endfunction s$
function gl_inp_SetCursor(curStart,curEnd)
if curStart < 0 then curStart = 0
if curEnd < curStart then curEnd = curStart
gl_inp_CursorStart = curStart
gl_inp_CursorEnd = curEnd
rem make sure cursor stays inside start-end window
if gl_inp_Cursor < gl_inp_CursorStart then gl_inp_Cursor = gl_inp_CursorStart
if gl_inp_Cursor > gl_inp_CursorEnd then gl_inp_Cursor = gl_inp_CursorEnd
endfunction
function gl_inp_SetCursorPos(pos)
if pos < 0 then pos = 0
gl_inp_Cursor = pos
if pos < gl_inp_CursorStart then gl_inp_CursorStart = pos
if pos > gl_inp_CursorEnd then gl_inp_CursorEnd = pos
endfunction
Thank you Diggsey, and about the Syntax highlighting: My plan is to store information about the highlighting in an array for each line, so the whole text will not be stored as single string per line, but as a bunch of small strings, each with individual highlight-state and information about where it begins. Everytime a line is changed, the syntax information is recalculated, so it should be pretty efficient and relatively easy to implement. Does that sound like a good idea to you, or is there an easier way to tackle the problem?