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