Scintilla using SublimeText-like code-scroller (not Unicode)
Posted: Mon Nov 17, 2014 6:53 am
EDIT: Developed on Linux, other operating systems not fully tested. Not Unicode friendly.
After seing Eddy's scintilla code and the resemblance with SublimeText I wanted to implement SublimeText's MiniMap (code scroller instead of a vertical scrollbar).
Not beeing a 2D coder myself I knew I need help and after some search found Stargate's canvas scrollbar code.
The code below seems to work (based on Eddy's and Stargate's code) but I believe there are some conceptual mistakes based on my inability to wrap my head around it.
There are some strange effects that maybe someone else has a solution for. (like when the window is maximized, etc.)
Also didn't find a working solution on changing the width of the code-scroller while changing the size of the main window.
(scintilla gadget width and code-scroller gadget width depend on each other - my code always had strange artifacts)
In any case, the code below definately shows how not to do it.
Anyway, have fun coding.
After seing Eddy's scintilla code and the resemblance with SublimeText I wanted to implement SublimeText's MiniMap (code scroller instead of a vertical scrollbar).
Not beeing a 2D coder myself I knew I need help and after some search found Stargate's canvas scrollbar code.
The code below seems to work (based on Eddy's and Stargate's code) but I believe there are some conceptual mistakes based on my inability to wrap my head around it.
There are some strange effects that maybe someone else has a solution for. (like when the window is maximized, etc.)
Also didn't find a working solution on changing the width of the code-scroller while changing the size of the main window.
(scintilla gadget width and code-scroller gadget width depend on each other - my code always had strange artifacts)
In any case, the code below definately shows how not to do it.
Anyway, have fun coding.
Code: Select all
;
; Proof of concept:
; Scintilla with code-scroller
; like Sublime Text (MiniMap)
;
; Scintilla documentation:
; http://www.scintilla.org/ScintillaDoc.html
;
EnableExplicit
Enumeration
#Window
#Gadget
#Font
#SciMain
EndEnumeration
#SideViewWidth = 120
#MENU_EXTEND_SELECTION = 10
Global EnableScroll.i, maxPositionsOnMap.i
Global totalEditorLinesOfCode.i, firstVisibleEditorLine.i, visibleEditorLines.i
Global scrollerPosition.i, scrollerTextFrontColor.i, scrollerTextBackColor.i, scrollerBarActiveColor.i, scrollerBarDeactiveColor.i
Global NewList Item.s()
Global txt$, txtLen
; load the font
LoadFont(#Font, "Monospace", 2)
; load the test text
Declare.s LoadText()
txt$ = LoadText()
;-start
txtLen=StringByteLength(txt$, #PB_UTF8)
;- default settings
scrollerTextFrontColor = RGB (195, 213, 255)
scrollerTextBackColor = RGB ( 70, 78, 85)
scrollerBarActiveColor = RGBA(100, 252, 195, 50)
scrollerBarDeactiveColor = RGBA(200, 200, 200, 50)
Procedure MakeUTF8Text(text.s)
Static buffer.s
buffer = Space(StringByteLength(text, #PB_UTF8))
PokeS(@buffer, text, -1, #PB_UTF8)
ProcedureReturn @buffer
EndProcedure
Procedure CodeScrollerGadgetUpdate(Gadget)
Protected scrollerFontHeight.i, Y.i, scrollerBarHeight.i
Protected TextLength.i, Text$, Index.i
;- get needed editor data
; This returns the number of lines in the document.
; An empty document contains 1 line.
; A document holding only an end of line sequence has 2 lines.
totalEditorLinesOfCode = ScintillaSendMessage(#SciMain, #SCI_GETLINECOUNT, 0)
Debug "totalEditorLinesOfCode: " + totalEditorLinesOfCode
; there's a strange (but logical) thing:
; when pulling down the bar the scroller background moves up
; this means the scroller "step" is never one line but two
firstVisibleEditorLine = ScintillaSendMessage(#SciMain, #SCI_GETFIRSTVISIBLELINE, 0) / 2; + (scrollerPosition / 10)
Debug "firstVisibleEditorLine: " + firstVisibleEditorLine
visibleEditorLines = ScintillaSendMessage(#SciMain, #SCI_LINESONSCREEN, 0)
Debug "visible Editor Lines: " + visibleEditorLines
Debug "scrollerPosition: " + scrollerPosition
; Fetch the text from the editor
TextLength = ScintillaSendMessage(#SciMain, #SCI_GETTEXTLENGTH, 0, 0)
Text$ = Space(TextLength)
ScintillaSendMessage(#SciMain, #SCI_GETTEXT, TextLength, @Text$)
; copy the editor text into the linked list
ClearList(Item())
If totalEditorLinesOfCode = 1
; make sure there is at least one list element
AddElement(Item())
Item() = ""
Else
; copy line by line the text from the editor into the linked list for the code scroller
Repeat
Index + 1
AddElement(Item())
Item() = StringField(Text$, Index, Chr(13))
;Debug "Line " + Str(Index) + ": " + Item()
Until Index = totalEditorLinesOfCode
EndIf
;- now that the list is filled lets draw
If StartDrawing(CanvasOutput(Gadget))
; prepare scrollbar window
DrawingMode(#PB_2DDrawing_Default)
Box(0, 0, OutputWidth(), OutputHeight(), RGB(70, 78, 85))
DrawingFont(FontID(#Font))
scrollerFontHeight = TextHeight("#")
If SelectElement(Item(), firstVisibleEditorLine)
Repeat
DrawText(0, Y, Item(), scrollerTextFrontColor, scrollerTextBackColor)
Y + (scrollerFontHeight )
Until NextElement(Item()) = #False Or Y > (OutputHeight() - scrollerBarHeight)
EndIf
Debug "Y: " + Y
; Scrollbar
DrawingMode(#PB_2DDrawing_AlphaBlend);#PB_2DDrawing_XOr)
; set bar height (seems to be correct)
scrollerBarHeight = visibleEditorLines * scrollerFontHeight
; Y is for the visible lines scroller
If OutputHeight() < (totalEditorLinesOfCode * scrollerFontHeight)
Y = (OutputHeight() + scrollerBarHeight) * ((scrollerPosition) * scrollerFontHeight) / ((totalEditorLinesOfCode) * scrollerFontHeight)
Else
Y = ((totalEditorLinesOfCode * scrollerFontHeight) + scrollerBarHeight) * ((scrollerPosition) * scrollerFontHeight) / (OutputHeight()) ; * scrollerFontHeight)
EndIf
If GetGadgetAttribute(Gadget, #PB_Canvas_MouseX) >= 0
EnableScroll = #True
;scrollbar color mouse cursor over
Box(0, Y, OutputWidth(), scrollerBarHeight, scrollerBarActiveColor)
If GetGadgetAttribute(Gadget, #PB_Canvas_Buttons) & #PB_Canvas_LeftButton
scrollerPosition = (ListSize(Item())) * (GetGadgetAttribute(Gadget, #PB_Canvas_MouseY)- scrollerBarHeight / 2) / (OutputHeight() + scrollerBarHeight)
If scrollerPosition < 0
scrollerPosition = 0
ElseIf scrollerPosition > totalEditorLinesOfCode
scrollerPosition = totalEditorLinesOfCode
EndIf
; minus (scrollerPosition / 10) is for compensation
; don't like these king of "magic" numbers
ScintillaSendMessage(#SciMain, #SCI_SETFIRSTVISIBLELINE, scrollerPosition * 2 - (scrollerPosition / 10), 0)
Else
scrollerPosition = firstVisibleEditorLine
EndIf
Else
EnableScroll = #False
Debug "scrollbar color when the cursor is not over the gadget"
;scrollbar color cursor not over
Box(0, Y, OutputWidth(), scrollerBarHeight, scrollerBarDeactiveColor)
EndIf
Debug "Y: " + Y
StopDrawing()
EndIf
EndProcedure
Procedure CodeScrollerGadgetEvent(Gadget)
Shared scrollerPosition ;needs to be global because it's calculated somewhere else...
Select EventType()
Case #PB_EventType_MouseWheel
scrollerPosition - 3 * GetGadgetAttribute(Gadget, #PB_Canvas_WheelDelta)
If scrollerPosition < 0
scrollerPosition = 0
ElseIf scrollerPosition > totalEditorLinesOfCode
scrollerPosition = totalEditorLinesOfCode
EndIf
CodeScrollerGadgetUpdate(Gadget)
Case #PB_EventType_MouseMove, #PB_EventType_LeftButtonDown
CodeScrollerGadgetUpdate(Gadget)
EndSelect
EndProcedure
Procedure CodeScrollerGadget(gad, x, y, w, h, flags = 0)
ProcedureReturn CanvasGadget(gad, x, y, w, h, flags)
EndProcedure
;-
;- SCINTILLA
Procedure ExtendScintillaSelection()
Protected mainSel, selStart, selEnd
mainSel = ScintillaSendMessage(0, #SCI_GETMAINSELECTION)
selStart = ScintillaSendMessage(0, #SCI_GETSELECTIONNSTART, mainSel)
selEnd = ScintillaSendMessage(0, #SCI_GETSELECTIONNEND, mainSel)
EndProcedure
; Set Text Mode
Procedure SetTextMode(gad.i)
ScintillaSendMessage(gad, #SCI_SETWRAPMODE, #SC_WRAP_NONE)
ScintillaSendMessage(gad, #SCI_SETCODEPAGE, #SC_CP_UTF8)
ScintillaSendMessage(gad, #SCI_SETVIRTUALSPACEOPTIONS, #SCVS_RECTANGULARSELECTION | #SCVS_USERACCESSIBLE) ; allow cursor and rect selection to move beyond end of line
EndProcedure
; Set Current Line Highlighting
Procedure SetCurrentLineHighlighting(gad.i)
ScintillaSendMessage(gad, #SCI_SETCARETLINEVISIBLE, 1)
ScintillaSendMessage(gad, #SCI_SETCARETLINEVISIBLEALWAYS, 1)
ScintillaSendMessage(gad, #SCI_SETCARETLINEBACKALPHA, 50)
ScintillaSendMessage(gad, #SCI_SETCARETLINEBACK, RGB(100, 252, 195))
EndProcedure
; Set Text style
Procedure SetTextStyle(gad.i, font.s, size.i)
ScintillaSendMessage(gad, #SCI_STYLESETFONT, #STYLE_DEFAULT, MakeUTF8Text(font)) ; rectangle selection works better with mono-width font
ScintillaSendMessage(gad, #SCI_STYLESETSIZE, #STYLE_DEFAULT, size)
ScintillaSendMessage(gad, #SCI_STYLESETBACK, #STYLE_DEFAULT, RGB(70, 78, 85))
ScintillaSendMessage(gad, #SCI_STYLESETFORE, #STYLE_DEFAULT, RGB(195, 213, 255))
ScintillaSendMessage(gad, #SCI_STYLECLEARALL)
EndProcedure
; Set Margin size and style
Procedure SetMarginStyle(gad.i, font.s, width.s)
Protected marginWidth
ScintillaSendMessage(gad, #SCI_STYLESETFONT, #STYLE_LINENUMBER, MakeUTF8Text(font))
ScintillaSendMessage(gad, #SCI_STYLESETBACK, #STYLE_LINENUMBER, RGB(53, 55, 57))
ScintillaSendMessage(gad, #SCI_STYLESETFORE, #STYLE_LINENUMBER, RGB(200, 200, 200))
marginWidth=ScintillaSendMessage(gad, #SCI_TEXTWIDTH, #STYLE_LINENUMBER, MakeUTF8Text(width))
ScintillaSendMessage(gad, #SCI_SETMARGINTYPEN, 0, #SC_MARGIN_NUMBER)
ScintillaSendMessage(gad, #SCI_SETMARGINWIDTHN, 0, marginWidth)
marginWidth=0
ScintillaSendMessage(gad, #SCI_SETMARGINMASKN, 2, #SC_MASK_FOLDERS)
ScintillaSendMessage(gad, #SCI_SETMARGINWIDTHN, 2, marginWidth)
ScintillaSendMessage(gad, #SCI_SETMARGINSENSITIVEN, 2, #True)
EndProcedure
; Set Main Caret and Selection
Procedure MainCaretSelection(gad.i)
ScintillaSendMessage(gad, #SCI_SETCARETSTICKY, 1) ;make always visible
ScintillaSendMessage(gad, #SCI_SETCARETWIDTH, 3) ;make thicker
ScintillaSendMessage(gad, #SCI_SETCARETFORE, RGB(255, 160, 136))
ScintillaSendMessage(gad, #SCI_SETSELALPHA, 100)
ScintillaSendMessage(gad, #SCI_SETSELBACK, 1, RGB(255, 160, 136))
ScintillaSendMessage(gad, #SCI_SETSELFORE, 1, RGB(200, 200, 200))
EndProcedure
; Set Additional Caret and Selection
Procedure AddCaretSelection(gad.i)
ScintillaSendMessage(gad, #SCI_SETADDITIONALCARETFORE, RGB(157, 64, 41))
ScintillaSendMessage(gad, #SCI_SETADDITIONALCARETSBLINK, 1)
ScintillaSendMessage(gad, #SCI_SETADDITIONALSELALPHA, 100)
ScintillaSendMessage(gad, #SCI_SETADDITIONALSELBACK, RGB(255, 160, 136))
ScintillaSendMessage(gad, #SCI_SETADDITIONALSELFORE, RGB(200, 200, 200))
EndProcedure
; Enable multi cursor editing
Procedure EnableMultiCursor(gad.i)
ScintillaSendMessage(gad, #SCI_SETRECTANGULARSELECTIONMODIFIER, #SCMOD_ALT) ; select rectangle range by holding down the ALT key while dragging with the mouse
ScintillaSendMessage(gad, #SCI_SETMULTIPLESELECTION, 1) ; select multiple ranges by holding down the CTRL or CMD key while dragging with the mouse
ScintillaSendMessage(gad, #SCI_SETMULTIPASTE, #SC_MULTIPASTE_EACH)
ScintillaSendMessage(gad, #SCI_SETADDITIONALSELECTIONTYPING, 1)
EndProcedure
; Enable hotkey for selection auto extension CTRL + D
Procedure HotKeyCTRL_D()
AddKeyboardShortcut(#Window, #PB_Shortcut_Control | #PB_Shortcut_D, #MENU_EXTEND_SELECTION)
BindEvent(#PB_Event_Menu, ExtendScintillaSelection())
EndProcedure
; Change text
Procedure ChangeText(gad.i, txt.s, txtLength.i)
ScintillaSendMessage(gad, #SCI_SETTEXT, 0, MakeUTF8Text(txt))
ScintillaSendMessage(gad, #SCI_GOTOPOS, txtLength)
SetActiveGadget(gad)
Debug "#SCI_LINESONSCREEN: " + ScintillaSendMessage(gad, #SCI_LINESONSCREEN, 0)
EndProcedure
;-
Procedure MainWindowResize()
Protected mainWidth = WindowWidth(#Window)
Protected mainHeight = WindowHeight(#Window)
Protected sideWidth = GadgetWidth(#Gadget)
ResizeGadget(#SciMain, 0, 0, mainWidth - sideWidth, mainHeight)
ResizeGadget(#Gadget, mainWidth - sideWidth, 0, sideWidth, mainHeight)
CodeScrollerGadgetUpdate(#Gadget)
EndProcedure
;-
;- MAIN
InitScintilla()
OpenWindow(#Window, 0, 0, 600, 305, "WindowTitle", #PB_Window_MinimizeGadget|#PB_Window_ScreenCentered|#PB_Window_SizeGadget)
ScintillaGadget(#SciMain, 0, 0, WindowWidth(#Window) - #SideViewWidth, WindowHeight(#Window), 0);@ScintillaCallBack())
CodeScrollerGadget(#Gadget, WindowWidth(#Window) - #SideViewWidth, 0, #SideViewWidth, WindowHeight(#Window), #PB_Canvas_Border | #PB_Canvas_Keyboard)
SetTextMode(#SciMain)
SetCurrentLineHighlighting(#SciMain)
SetTextStyle(#SciMain, "Courier New", 8)
SetMarginStyle(#SciMain, "Arial", "_999")
MainCaretSelection(#SciMain)
AddCaretSelection(#SciMain)
EnableMultiCursor(#SciMain)
HotKeyCTRL_D()
ChangeText(#SciMain, txt$, txtLen)
ScintillaSendMessage(#SciMain, #SCI_GOTOPOS, 0)
; the code scroll Bar is used instead
ScintillaSendMessage(#SciMain, #SCI_SETVSCROLLBAR, 0)
;ScintillaSendMessage(#SciMain, #SCI_SETFIRSTVISIBLELINE, 0, 0)
BindEvent(#PB_Event_SizeWindow, @MainWindowResize(), #Window)
SmartWindowRefresh(#Window, #True)
CodeScrollerGadgetUpdate(#Gadget)
Repeat
Select WaitWindowEvent()
Case #PB_Event_CloseWindow
End
Case #PB_Event_Gadget
Select EventGadget()
Case #SciMain
CodeScrollerGadgetUpdate(#Gadget)
Case #Gadget
CodeScrollerGadgetUpdate(#Gadget)
Default
CodeScrollerGadgetUpdate(#Gadget)
EndSelect
EndSelect
ForEver
End
Procedure.s LoadText()
ProcedureReturn " 1 Scintilla is a free source code editing component."+#CR$+
" 2 "+#CR$+
" 3 It comes with complete source code and a license that permits use in any project or product personal or commercial."+#CR$+
" 4 The license may be viewed here. "+#CR$+
" 5 "+#CR$+
" 6 The source code, as well as the library documentation may be found on the Scintilla Homepage."+#CR$+
" 7 From the Scintilla Homepage : "+#CR$+
" 8 As well as features found in standard text editing components, Scintilla includes features especially useful when editing and debugging source code."+#CR$+
" 9 "+#CR$+
"10 These include support for syntax styling, error indicators, code completion and call tips."+#CR$+
"11 The selection margin can contain markers like those used in debuggers to indicate breakpoints and the current line."+#CR$+
"12 "+#CR$+
"13 Styling choices are more open than with many editors, allowing the use of proportional fonts, bold and italics, multiple foreground and background colors and multiple fonts."+#CR$+
"14 "+#CR$+
"15 "+#CR$+
"16 "+#CR$+
"17 "+#CR$+
"18 "+#CR$+
"19 "+#CR$+
"20 +++++++++++++++++++++++++++++++++++++++++++++++++++"+#CR$+
"21 "+#CR$+
"22 "+#CR$+
"23 "+#CR$+
"24 "+#CR$+
"25 "+#CR$+
"26 "+#CR$+
"27 "+#CR$+
"28 "+#CR$+
"29 "+#CR$+
"30 +++++++++++++++++++++++++++++++++++++++++++++++++++"+#CR$+
"31 "+#CR$+
"32 "+#CR$+
"33 "+#CR$+
"34 "+#CR$+
"35 "+#CR$+
"36 "+#CR$+
"37 "+#CR$+
"38 "+#CR$+
"39 "+#CR$+
"40 +++++++++++++++++++++++++++++++++++++++++++++++++++"+#CR$+
"41 "+#CR$+
"42 "+#CR$+
"43"+#CR$+
"44 "+#CR$+
"45 "+#CR$+
"46 "+#CR$+
"47 "+#CR$+
"48 "+#CR$+
"49 "+#CR$+
"50 +++++++++++++++++++++++++++++++++++++++++++++++++++"+#CR$+
"51 "+#CR$+
"52 "+#CR$+
"53"+#CR$+
"54 "+#CR$+
"55 "+#CR$+
"56 "+#CR$+
"57 "+#CR$+
"58 "+#CR$+
"59 "+#CR$+
"60 +++++++++++++++++++++++++++++++++++++++++++++++++++"+#CR$+
"61 "+#CR$+
"62 "+#CR$+
"63"+#CR$+
"64 "+#CR$+
"65 "+#CR$+
"66 "+#CR$+
"67 "+#CR$+
"68 "+#CR$+
"69 "+#CR$+
"70 +++++++++++++++++++++++++++++++++++++++++++++++++++"+#CR$+
"71 "+#CR$+
"72 "+#CR$+
"73"+#CR$+
"74 "+#CR$+
"75 "+#CR$+
"76 "+#CR$+
"77 "+#CR$+
"78 "+#CR$+
"79 "+#CR$+
"80 +++++++++++++++++++++++++++++++++++++++++++++++++++"+#CR$+
"81 "+#CR$+
"82 "+#CR$+
"83"+#CR$+
"84 "+#CR$+
"85 "+#CR$+
"86 "+#CR$+
"87 "+#CR$+
"88 "+#CR$+
"89 "+#CR$+
"90 +++++++++++++++++++++++++++++++++++++++++++++++++++"+#CR$+
"91 "+#CR$+
"92 "+#CR$+
"93"+#CR$+
"94 "+#CR$+
"95 "+#CR$+
"96 "+#CR$+
"97 "+#CR$+
"98 "+#CR$+
"99 "+#CR$+
"100 +++++++++++++++++++++++++++++++++++++++++++++++++++"+#CR$+
" "+#CR$
EndProcedure