Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Scan for external color schemes and combine with the built-in ones #293

Merged
9 changes: 9 additions & 0 deletions Documentation/English/Reference/ide_preferences.txt
Original file line number Diff line number Diff line change
Expand Up @@ -460,6 +460,15 @@

@EndOs

@LineBreak
@LineBreak
More color schemes can be added by creating scheme definition files
with the name format "SchemeName.prefs". The files have to
be copied to the "ColorSchemes" folder in the PureBasic installation directory
to be recognized by the IDE. The default scheme files can be used as examples
to create a new scheme, or you can export your colors from the
Import/Export section of the Preferences window (select "Include Color settings").

;-------------------------------------------------------------------------------------------------------------------
; Editor - Coloring - Custom Keywords
;-------------------------------------------------------------------------------------------------------------------
Expand Down
309 changes: 309 additions & 0 deletions PureBasicIDE/ColorSchemes.pb
Original file line number Diff line number Diff line change
@@ -0,0 +1,309 @@
; --------------------------------------------------------------------------------------------
; Copyright (c) Fantaisie Software. All rights reserved.
; Dual licensed under the GPL and Fantaisie Software licenses.
; See LICENSE and LICENSE-FANTAISIE in the project root for license information.
; --------------------------------------------------------------------------------------------


; Store color names in an array, for indexable lookup
Global Dim ColorName.s(#COLOR_Last_IncludingToolsPanel)

; Color Scheme structure
Structure ColorSchemeStruct
Name$
File$

ColorValue.l[#COLOR_Last_IncludingToolsPanel + 1]

IsIDEDefault.i
IsAccessibility.i
EndStructure

; Global list of built-in / found schemes
Global NewList ColorScheme.ColorSchemeStruct()

; Color values with special meanings
#ColorSchemeValue_UseSysColor = -1
#ColorSchemeValue_Undefined = -2



; Returns #True if the specified color scheme matches the user's current color settings, otherwise #False
Procedure ColorSchemeMatchesCurrentSettings(*ColorScheme.ColorSchemeStruct)
Protected Result = #True

For i = 0 To #COLOR_Last
If i <> #COLOR_Selection And i <> #COLOR_SelectionFront ; selection colors may follow OS, so skip them for scheme match check
If *ColorScheme\ColorValue[i] >= 0
If *ColorScheme\ColorValue[i] <> Colors(i)\UserValue
Result = #False
Break
EndIf
EndIf
EndIf
Next i

ProcedureReturn Result
EndProcedure

; Returns *ColorScheme which matches the user's current settings, otherwise #Null if no match
Procedure FindCurrentColorScheme()
Protected *ColorScheme.ColorSchemeStruct = #Null

ForEach ColorScheme()
If ColorSchemeMatchesCurrentSettings(@ColorScheme())
*ColorScheme = @ColorScheme()
Break
EndIf
Next

ProcedureReturn *ColorScheme
EndProcedure

; Guess the specified color for a given Color Scheme, falling back to its basic background/text colors
Procedure GuessColorSchemeColor(*ColorScheme.ColorSchemeStruct, index)
Select index
Case #COLOR_GlobalBackground
Color = #White
Case #COLOR_NormalText
Color = $FFFFFF - *ColorScheme\ColorValue[#COLOR_GlobalBackground]

Case #COLOR_CurrentLine, #COLOR_DisabledBack, #COLOR_LineNumberBack, #COLOR_PlainBackground, #COLOR_ProcedureBack, #COLOR_ToolsPanelBackColor
Color = *ColorScheme\ColorValue[#COLOR_GlobalBackground] ; assume it should match global background
Case #COLOR_DebuggerBreakPoint, #COLOR_DebuggerError, #COLOR_DebuggerLine, #COLOR_DebuggerWarning
Color = *ColorScheme\ColorValue[#COLOR_GlobalBackground]

Case #COLOR_SelectionFront
CompilerIf #CompileWindows
Color = GetSysColor_(#COLOR_HIGHLIGHTTEXT)
CompilerElse
Color = *ColorScheme\ColorValue[#COLOR_GlobalBackground]
CompilerEndIf
Case #COLOR_Selection, #COLOR_SelectionRepeat
CompilerIf #CompileWindows
Color = GetSysColor_(#COLOR_HIGHLIGHT)
CompilerElse
Color = *ColorScheme\ColorValue[#COLOR_NormalText]
CompilerEndIf

Default
Color = *ColorScheme\ColorValue[#COLOR_NormalText] ; otherwise, assume it should match normal foreground color
EndSelect

ProcedureReturn Color
EndProcedure

; Disable color preference gadgets if appropriate (eg. disable Selection and SelectionFront when Accessibility mode expects them to match system colors)
Procedure DisableSelectionColorGadgets(*ColorScheme.ColorSchemeStruct)
CompilerIf #CompileWindows
ShouldDisable = #False

If EnableAccessibility
ShouldDisable = #True ; Accessibility mode enabled - use system selection colors, don't allow user to change them
Else
If *ColorScheme
If *ColorScheme\IsAccessibility
ShouldDisable = #True ; Accessibility scheme selected - use system selection colors, don't allow user to change them
ElseIf *ColorScheme\ColorValue[#COLOR_Selection] = #ColorSchemeValue_UseSysColor Or *ColorScheme\ColorValue[#COLOR_SelectionFront] = #ColorSchemeValue_UseSysColor
ShouldDisable = #True ; Value of -1 (use system color) specified
EndIf
EndIf
EndIf

DisableGadget(#GADGET_Preferences_FirstColorText + #COLOR_Selection, ShouldDisable)
DisableGadget(#GADGET_Preferences_FirstSelectColor + #COLOR_Selection, ShouldDisable)
DisableGadget(#GADGET_Preferences_FirstColorText + #COLOR_SelectionFront, ShouldDisable)
DisableGadget(#GADGET_Preferences_FirstSelectColor + #COLOR_SelectionFront, ShouldDisable)
CompilerEndIf
EndProcedure

; Load the specified *ColorScheme to the Preferences gadgets
Procedure LoadColorSchemeToPreferencesWindow(*ColorScheme.ColorSchemeStruct)
If *ColorScheme

PreferenceToolsPanelFrontColor = *ColorScheme\ColorValue[#COLOR_ToolsPanelFrontColor]
PreferenceToolsPanelBackColor = *ColorScheme\ColorValue[#COLOR_ToolsPanelBackColor]

For i = 0 To #COLOR_Last
Colors(i)\PrefsValue = *ColorScheme\ColorValue[i]
Next i

CompilerIf #CompileWindows
; Special thing: On windows we always default back to the system colors in
; the PB standard scheme for screenreader support. The 'Accessibility'
; scheme has a special option to always use these colors, so it is not needed here.
;
If *ColorScheme\IsIDEDefault Or *ColorScheme\IsAccessibility Or EnableAccessibility Or (Colors(#COLOR_Selection)\PrefsValue = #ColorSchemeValue_UseSysColor)
Colors(#COLOR_Selection)\PrefsValue = GetSysColor_(#COLOR_HIGHLIGHT)
Colors(#COLOR_SelectionFront)\PrefsValue = GetSysColor_(#COLOR_HIGHLIGHTTEXT)
EndIf
CompilerEndIf

For i = 0 To #COLOR_Last
If Colors(i)\PrefsValue >= 0
UpdatePreferenceSyntaxColor(i, Colors(i)\PrefsValue)
Else
Colors(i)\PrefsValue = GuessColorSchemeColor(*ColorScheme, i)
UpdatePreferenceSyntaxColor(i, Colors(i)\PrefsValue)
EndIf
Next i

DisableSelectionColorGadgets(*ColorScheme)

If PreferenceToolsPanelFrontColor < 0
PreferenceToolsPanelFrontColor = GuessColorSchemeColor(*ColorScheme, #COLOR_ToolsPanelFrontColor)
EndIf
If PreferenceToolsPanelBackColor < 0
PreferenceToolsPanelBackColor = GuessColorSchemeColor(*ColorScheme, #COLOR_ToolsPanelBackColor)
EndIf

If IsImage(#IMAGE_Preferences_ToolsPanelFrontColor)
UpdateImageColorGadget(#GADGET_Preferences_ToolsPanelFrontColor, #IMAGE_Preferences_ToolsPanelFrontColor, PreferenceToolsPanelFrontColor)
EndIf
If IsImage(#IMAGE_Preferences_ToolsPanelBackColor)
UpdateImageColorGadget(#GADGET_Preferences_ToolsPanelBackColor, #IMAGE_Preferences_ToolsPanelBackColor, PreferenceToolsPanelBackColor)
EndIf

EndIf
EndProcedure

; Find and remove a known color scheme by its name
Procedure RemoveColorSchemeIfExists(Name$)
If Name$ <> ""
ForEach ColorScheme()
If ColorScheme()\Name$ = Name$
DeleteElement(ColorScheme())
Break
EndIf
Next
EndIf
EndProcedure

; Read the specified *ColorScheme from data section (for built-in schemes)
Procedure ReadColorSchemeFromDataSection(*ColorScheme.ColorSchemeStruct)
If *ColorScheme
; This assumes the NAME STRING data has already been read!
*ColorScheme\File$ = ""
Read.l *ColorScheme\ColorValue[#COLOR_ToolsPanelFrontColor]
Read.l *ColorScheme\ColorValue[#COLOR_ToolsPanelBackColor]
For i = 0 To #COLOR_Last
Read.l *ColorScheme\ColorValue[i]
Next i
EndIf

ProcedureReturn *ColorScheme
EndProcedure

; Load the specified *ColorScheme from file on disk (for external schemes)
Procedure LoadColorSchemeFromFile(*ColorScheme.ColorSchemeStruct, File$)
Protected Result = #Null

If File$
; Basic validation of color scheme file...
If OpenPreferences(File$)
Name$ = GetFilePart(File$, #PB_FileSystem_NoExtension)
If PreferenceGroup("Sections") And (ReadPreferenceLong("IncludeColors", 0) = 1)
If PreferenceGroup("Colors")

If *ColorScheme
RemoveColorSchemeIfExists(Name$)
*ColorScheme\Name$ = Name$
*ColorScheme\File$ = File$

; Load all defined colors into map...
For i = 0 To #COLOR_Last_IncludingToolsPanel
*ColorScheme\ColorValue[i] = #ColorSchemeValue_Undefined
ColorValueString$ = ReadPreferenceString(ColorName(i), "")
If ColorValueString$ <> ""
If ReadPreferenceLong(ColorName(i) + "_Used", 1) = 1
If FindString(ColorValueString$, "RGB", 1, #PB_String_NoCase)
*ColorScheme\ColorValue[i] = ColorFromRGBString(ColorValueString$)
Else
*ColorScheme\ColorValue[i] = Val(ColorValueString$) & $00FFFFFF
EndIf
EndIf
EndIf
Next i
Result = *ColorScheme

EndIf

EndIf
EndIf
ClosePreferences()
EndIf
EndIf

ProcedureReturn Result
EndProcedure

; Initialize color names, built-in color schemes, and external found color schemes
Procedure InitColorSchemes()

; Only need to initialize color schemes once
If NbSchemes > 0
ProcedureReturn
EndIf

; Read color key names into indexable array
Restore ColorKeys
For i = 0 To #COLOR_Last
Read.s ColorName(i)
Next i
ColorName(#COLOR_ToolsPanelFrontColor) = "ToolsPanel_FrontColor"
ColorName(#COLOR_ToolsPanelBackColor) = "ToolsPanel_BackColor"


; First, load embedded DataSection default color schemes
ClearList(ColorScheme())
Restore DefaultColorSchemes
Read.s Name$
While Name$ <> ""
AddElement(ColorScheme())
ColorScheme()\Name$ = Name$
ReadColorSchemeFromDataSection(@ColorScheme())
If ListIndex(ColorScheme()) = 0
ColorScheme()\IsIDEDefault = #True
EndIf
Read.s Name$
Wend
NbSchemes = ListSize(ColorScheme())

; Then, scan 'ColorSchemes' subfolder!
If PureBasicPath$
Dir = ExamineDirectory(#PB_Any, PureBasicPath$ + #DEFAULT_ColorSchemePath, "*")
If Dir
While NextDirectoryEntry(Dir)
If DirectoryEntryType(Dir) = #PB_DirectoryEntry_File
File$ = PureBasicPath$ + #DEFAULT_ColorSchemePath + #PS$ + DirectoryEntryName(Dir)
Select LCase(GetExtensionPart(File$))
Case "prefs"
AddElement(ColorScheme())
If LoadColorSchemeFromFile(@ColorScheme(), File$)
; OK
Else
DeleteElement(ColorScheme())
EndIf
EndSelect
EndIf
Wend
FinishDirectory(Dir)
EndIf
EndIf


; If additional schemes were found, sort schemes alphabetically, because it could become a long list
If ListSize(ColorScheme()) > NbSchemes
NbSchemes = ListSize(ColorScheme())
SortStructuredList(ColorScheme(), #PB_Sort_Ascending | #PB_Sort_NoCase, OffsetOf(ColorSchemeStruct\Name$), #PB_String)
EndIf

; Ensure "Accessibility" scheme always at bottom of list, for special handling
ForEach ColorScheme()
If ColorScheme()\Name$ = "Accessibility"
ColorScheme()\IsAccessibility = #True
MoveElement(ColorScheme(), #PB_List_Last)
EndIf
Next

EndProcedure
6 changes: 6 additions & 0 deletions PureBasicIDE/Common.pb
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,11 @@ Enumeration 0
#COLOR_PlainBackground

#COLOR_Last = #COLOR_PlainBackground

; Special cases beyond "Last"
#COLOR_ToolsPanelFrontColor = #COLOR_Last + 1
#COLOR_ToolsPanelBackColor
#COLOR_Last_IncludingToolsPanel = #COLOR_Last + 2
EndEnumeration


Expand Down Expand Up @@ -2688,6 +2693,7 @@ Global FakeToolsPanelID ; for the windows vertical toolspanel (only non-XP windo
Global AlwaysHideLog, ErrorLogVisible
Global CustomKeywordFile$
Global ToolsPanelUseFont, ToolsPanelUseColors
Global PreferenceToolsPanelFrontColor, PreferenceToolsPanelBackColor

; OS specific highlighting color representation:
;
Expand Down
3 changes: 3 additions & 0 deletions PureBasicIDE/CompilerFlags.pb
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,7 @@ CompilerSelect #PB_Compiler_OS
#DEFAULT_CatalogPath = "Catalogs\"
#DEFAILT_LibraryViewerPlugin= "Debugger\"
#DEFAULT_ThemePath = "Themes\"
#DEFAULT_ColorSchemePath = "ColorSchemes\"

#DEFAULT_HelpPath = "Help\"

Expand Down Expand Up @@ -226,6 +227,7 @@ CompilerSelect #PB_Compiler_OS
#DEFAULT_HelpPath = "help/"
#DEFAILT_LibraryViewerPlugin= "debugger/"
#DEFAULT_ThemePath = "themes/"
#DEFAULT_ColorSchemePath = "colorschemes/"

DefaultEditorFontName$ = "Monospace" ; "Misc Fixed" doesn't seems to exists anymore on modern distro
#DEFAULT_SplitterWidth = 6
Expand Down Expand Up @@ -285,6 +287,7 @@ CompilerSelect #PB_Compiler_OS
#DEFAULT_HelpPath = "help/"
#DEFAILT_LibraryViewerPlugin= "debugger/"
#DEFAULT_ThemePath = "themes/"
#DEFAULT_ColorSchemePath = "colorschemes/"

DefaultEditorFontName$ = "Monaco"
#DEFAULT_EditorFontSize = 14
Expand Down
2 changes: 2 additions & 0 deletions PureBasicIDE/Declarations.pb
Original file line number Diff line number Diff line change
Expand Up @@ -499,6 +499,8 @@ Declare ApplyPreferences() ; apply prefs changes to the editor and al
Declare OpenPreferencesWindow()
Declare UpdatePreferenceWindow()
Declare PreferencesWindowEvents(EventID)
Declare UpdatePreferenceSyntaxColor(ColorIndex, Color)
Declare UpdateImageColorGadget(Gadget, Image, Color)

;- ProcedureBrowser.pb
;
Expand Down
2 changes: 1 addition & 1 deletion PureBasicIDE/Language.pb
Original file line number Diff line number Diff line change
Expand Up @@ -825,7 +825,7 @@ DataSection
Data$ "EnableMarkers", "Enable Line Markers"
Data$ "ExtraWordChars", "Extra characters included in word selection"
Data$ "SelectFont", "Select Font"
Data$ "DefaultColors", "Default Color Schemes"
Data$ "DefaultColors", "Color Schemes"
Data$ "ShowWhiteSpace", "Show whitespace characters"
Data$ "ShowIndentGuides", "Show indentation guides"
Data$ "UseTabIndentForSplittedLines", "Use tab indent for splitted lines"
Expand Down
Loading