diff --git a/README.md b/README.md
index 786afe20a..e973d2627 100644
--- a/README.md
+++ b/README.md
@@ -53,7 +53,7 @@ OpenRocket needs help to become even better. Implementing features, writing docu
- Daniel Williams, pod support, maintainer
- Joe Pfeiffer (maintainer)
- Billy Olsen (maintainer)
-- Sibo Van Gool (maintainer)
+- Sibo Van Gool (RASAero file format, maintainer)
- Neil Weinstock (tester, icons, forum support)
- H. Craig Miller (tester)
diff --git a/core/resources/l10n/messages.properties b/core/resources/l10n/messages.properties
index b5ec1534c..1cfdddc3f 100644
--- a/core/resources/l10n/messages.properties
+++ b/core/resources/l10n/messages.properties
@@ -380,15 +380,15 @@ pref.dlg.PrefBooleanSelector2 = Confirm
pref.dlg.Add = Add
pref.dlg.DescriptionArea.Adddirectories = Add directories, RASP motor files (*.eng), RockSim engine files (*.rse) or ZIP archives separated by a semicolon (;) to load external thrust curves. Changes will take effect the next time you start OpenRocket.
-PreferencesDialog.lbl.language = Interface language:
-PreferencesDialog.languages.default = System default
-PreferencesDialog.lbl.languageEffect = The language will change the next time you start OpenRocket.
PreferencesDialog.CancelOperation.title = Discard Preference Changes
PreferencesDialog.CancelOperation.msg.discardChanges = Are you sure you want to discard the preference changes?
generalprefs.lbl.language = Interface language
generalprefs.languages.default = System default
generalprefs.lbl.languageEffect = The language will change the next time you start OpenRocket.
+generalprefs.lbl.UITheme = UI Theme
+generalprefs.lbl.FontSize = UI Font Size
+generalprefs.lbl.themeRestartOR = You must restart OpenRocket for the UI changes to take effect.
generalprefs.ImportWarning.title = Reload OpenRocket
generalprefs.ImportWarning.msg = You may need to restart OpenRocket for some of the changes to take effect.
@@ -402,6 +402,10 @@ PreferencesOptionPanel.checkbox.userDirectories.ttip = If unchecked, user direct
PreferencesOptionPanel.checkbox.windowInfo = Export window information (position, size\u2026)
PreferencesOptionPanel.checkbox.windowInfo.ttip = If unchecked, window information (position, size\u2026) will not be exported.
+! UI Themes
+UITheme.Light = Light (Default)
+UITheme.Dark = Dark
+
! Welcome dialog
welcome.dlg.title = Welcome to OpenRocket
welcome.dlg.lbl.thankYou = Thank you for downloading OpenRocket
@@ -2331,7 +2335,7 @@ ComponentPresetChooserDialog.menu.units = Units
ComponentPresetChooserDialog.checkbox.showLegacyCheckBox = Show Legacy Database
ComponentPresetChooserDialog.lbl.favorites = Check to add preset to the preset drop-down menu in the component edit dialog
Directly apply a preset by double-clicking it or by selecting it and closing this window.
ComponentPresetChooserDialog.checkbox.alwaysOpenPreset = Always open this dialog when creating a new %s
-table.column.Favorite = Favorite
+table.column.Favorite = \u2026 Favorite
table.column.Legacy = Legacy
table.column.Manufacturer = Manufacturer
table.column.PartNo = Part Number
diff --git a/core/resources/l10n/messages_ar.properties b/core/resources/l10n/messages_ar.properties
index 65d2651d3..5a957d4cc 100644
--- a/core/resources/l10n/messages_ar.properties
+++ b/core/resources/l10n/messages_ar.properties
@@ -343,9 +343,6 @@ pref.dlg.PrefBooleanSelector2 = تأكيد
pref.dlg.Add = أضف
pref.dlg.DescriptionArea.Adddirectories =.ملفات محرك روكسيم أو أرشيفات زيب مفصولة بفاصلة منقوطة لتحميل منحنيات الدفع الخارجية. ستدخل التغييرات حيز التنفيذ في المرة التالية التي تفتح فيها أوبنروكت (* .rse)أو RASP ملفات محرك (* .eng), أضف الدلائل
-PreferencesDialog.lbl.language = :لغة الواجهة
-PreferencesDialog.languages.default = النظام الافتراضي
-PreferencesDialog.lbl.languageEffect = .ستتغير اللغة في المرة التالية التي تعيد تشغيل أوبنروكت
generalprefs.lbl.language = :لغة الواجهة
generalprefs.languages.default = النظام الافتراضي
generalprefs.lbl.languageEffect = .ستتغير اللغة في المرة التالية التي تعيد تشغيل أوبنروكت
diff --git a/core/resources/l10n/messages_cs.properties b/core/resources/l10n/messages_cs.properties
index 27d128273..a1b326463 100644
--- a/core/resources/l10n/messages_cs.properties
+++ b/core/resources/l10n/messages_cs.properties
@@ -264,9 +264,9 @@ pref.dlg.PrefBooleanSelector2 = Potvrd
pref.dlg.Add = Pridej
pref.dlg.DescriptionArea.Adddirectories = Pridej adresre, soubory RASP motor (*.eng), RockSim engine soubory (*.rse) nebo ZIP archiv oddelen oddelovacem (;) k nahrn externch vkonovch prubehu. Zmeny se projev po restaru programu OpenRocket.
-PreferencesDialog.lbl.language = Jazyk rohrann:
-PreferencesDialog.languages.default = Vchoz
-PreferencesDialog.lbl.languageEffect = Jazyk se zmen pri dal\u0161m spu\u0161ten programu OpenRocket.
+generalprefs.lbl.language = Jazyk rohrann:
+generalprefs.languages.default = Vchoz
+generalprefs.lbl.languageEffect = Jazyk se zmen pri dal\u0161m spu\u0161ten programu OpenRocket.
! Software update checker
update.dlg.error.title = Nemohu zskat informace o aktualizacch
diff --git a/core/resources/l10n/messages_de.properties b/core/resources/l10n/messages_de.properties
index 908a48690..007a46da3 100644
--- a/core/resources/l10n/messages_de.properties
+++ b/core/resources/l10n/messages_de.properties
@@ -266,9 +266,9 @@ pref.dlg.PrefBooleanSelector2 = Best
pref.dlg.Add = Hinzufgen
pref.dlg.DescriptionArea.Adddirectories = Um eigene Schubkurven zu laden, Verzeichnisse, RASP-Motordateien (*.eng), RockSim-Motordateien (*.rse) oder ZIP-Archive mit Semikolon getrennt eingeben. nderungen werden beim nchsten Neustart von OpenRocket bernommen.
-PreferencesDialog.lbl.language = Sprache:
-PreferencesDialog.languages.default = Systemeinstellung
-PreferencesDialog.lbl.languageEffect = Die Sprache wird beim nchsten Neustart von OpenRocket gendert.
+generalprefs.lbl.language = Sprache:
+generalprefs.languages.default = Systemeinstellung
+generalprefs.lbl.languageEffect = Die Sprache wird beim nchsten Neustart von OpenRocket gendert.
! Software update checker
update.dlg.error.title = Es konnten keine Informationen ber Programmaktualisierungen empfangen werden.
diff --git a/core/resources/l10n/messages_es.properties b/core/resources/l10n/messages_es.properties
index 30b830045..db5dc37d1 100644
--- a/core/resources/l10n/messages_es.properties
+++ b/core/resources/l10n/messages_es.properties
@@ -733,9 +733,9 @@ PlotDialog.title.Flightdataplot = Representaci\u00f3n de los datos de vuelo
ComponentTreeRenderer.total = total
-PreferencesDialog.languages.default = Idioma por defecto
-PreferencesDialog.lbl.language = Idioma de la interfaz:
-PreferencesDialog.lbl.languageEffect = El idioma cambiar\u00e1 la pr\u00f3xima vez que abra OpenRocket.
+generalprefs.languages.default = Idioma por defecto
+generalprefs.lbl.language = Idioma de la interfaz:
+generalprefs.lbl.languageEffect = El idioma cambiar\u00e1 la pr\u00f3xima vez que abra OpenRocket.
PresetModel.lbl.custompreset = Personalizado
PresetModel.lbl.partsLib = Biblioteca de piezas
diff --git a/core/resources/l10n/messages_fr.properties b/core/resources/l10n/messages_fr.properties
index 2fd925ed2..fbbdd38e5 100644
--- a/core/resources/l10n/messages_fr.properties
+++ b/core/resources/l10n/messages_fr.properties
@@ -725,9 +725,9 @@ PlotDialog.title.Flightdataplot = Trac\u00E9 du vol
ComponentTreeRenderer.total = total
-PreferencesDialog.languages.default = Valeur syst\u00E8me par d\u00E9faut
-PreferencesDialog.lbl.language = Langue du programme:
-PreferencesDialog.lbl.languageEffect = La langue sera chang\u00E9e apr\u00E8s avoir red\u00E9marr\u00E9 OpenRocket.
+generalprefs.languages.default = Valeur syst\u00E8me par d\u00E9faut
+generalprefs.lbl.language = Langue du programme:
+generalprefs.lbl.languageEffect = La langue sera chang\u00E9e apr\u00E8s avoir red\u00E9marr\u00E9 OpenRocket.
PresetModel.lbl.custompreset = Personnalis
PresetModel.lbl.partsLib = Biblioth\u00E8que de pi\u00E8ces
diff --git a/core/resources/l10n/messages_it.properties b/core/resources/l10n/messages_it.properties
index 34980ad02..58d24f634 100644
--- a/core/resources/l10n/messages_it.properties
+++ b/core/resources/l10n/messages_it.properties
@@ -268,9 +268,9 @@ pref.dlg.PrefBooleanSelector2 = Conferma
pref.dlg.Add = Aggiungi
pref.dlg.DescriptionArea.Adddirectories = Aggiungi cartelle, RASP motor files (*.eng), RockSim engine files (*.rse) or ZIP archives separate da puntoevirgola (;) per caricare curve di spinta esterne. I cambiamenti avranno effetto la prossima volta che avvierai OpenRocket.
-PreferencesDialog.lbl.language = Lingua dell'interfaccia:
-PreferencesDialog.languages.default = Predefinita di sistema
-PreferencesDialog.lbl.languageEffect = La lingua sara' cambiata la prossima volta che avvierai OpenRocket.
+generalprefs.lbl.language = Lingua dell'interfaccia:
+generalprefs.languages.default = Predefinita di sistema
+generalprefs.lbl.languageEffect = La lingua sara' cambiata la prossima volta che avvierai OpenRocket.
! Software update checker
update.dlg.error.title = Non sono in grado di recuperare informazioni sugli aggiornamenti
diff --git a/core/resources/l10n/messages_ja.properties b/core/resources/l10n/messages_ja.properties
index 45d5c4003..a593c4c62 100644
--- a/core/resources/l10n/messages_ja.properties
+++ b/core/resources/l10n/messages_ja.properties
@@ -265,9 +265,9 @@ pref.dlg.PrefBooleanSelector2 = \u78BA\u8A8D
pref.dlg.Add = \u8FFD\u52A0
pref.dlg.DescriptionArea.Adddirectories = \u30D5\u30A9\u30EB\u30C0, RASP motor files (*.eng), RockSim engine files (*.rse) \u3082\u3057\u304F\u306F ZIP archives \u3092\u30BB\u30DF\u30B3\u30ED\u30F3(;)\u306B\u3088\u3063\u3066\u5206\u3051\u3089\u308C\u305F\u5F62\u3067\u8FFD\u52A0\u306E\u63A8\u529B\u5C65\u6B74\u3068\u3057\u3066\u8FFD\u52A0\u3067\u304D\u307E\u3059\u3002\u3053\u306E\u5909\u66F4\u306FOpenRocket\u306E\u518D\u8D77\u52D5\u6642\u306B\u6709\u52B9\u306B\u306A\u308A\u307E\u3059
-PreferencesDialog.lbl.language = \u8A00\u8A9E\uFF1A
-PreferencesDialog.languages.default = \u30B7\u30B9\u30C6\u30E0\u8A00\u8A9E
-PreferencesDialog.lbl.languageEffect = \u8A00\u8A9E\u306F\u518D\u8D77\u52D5\u6642\u306B\u5909\u66F4\u3055\u308C\u307E\u3059
+generalprefs.lbl.language = \u8A00\u8A9E\uFF1A
+generalprefs.languages.default = \u30B7\u30B9\u30C6\u30E0\u8A00\u8A9E
+generalprefs.lbl.languageEffect = \u8A00\u8A9E\u306F\u518D\u8D77\u52D5\u6642\u306B\u5909\u66F4\u3055\u308C\u307E\u3059
! Software update checker
update.dlg.error.title = \u30A2\u30C3\u30D7\u30C7\u30FC\u30C8\u60C5\u5831\u306E\u8AAD\u307F\u51FA\u3057\u304C\u3067\u304D\u307E\u305B\u3093
diff --git a/core/resources/l10n/messages_nl.properties b/core/resources/l10n/messages_nl.properties
index c44ec545e..9b9615c49 100644
--- a/core/resources/l10n/messages_nl.properties
+++ b/core/resources/l10n/messages_nl.properties
@@ -345,10 +345,6 @@ pref.dlg.PrefBooleanSelector2 = Bevestig
pref.dlg.Add = Voeg toe
pref.dlg.DescriptionArea.Adddirectories = Voeg folders, RASP motorbestanden (*.eng), RockSim motorbestanden (*.rse) of ZIP-archieven gescheiden door een puntkomma (;) om externe stuwkrachtcurves te laden. Wijzigingen zullen van kracht gaan de volgende keer dat u OpenRocket start.
-PreferencesDialog.lbl.language = Interface taal:
-PreferencesDialog.languages.default = Systeemstandaard
-PreferencesDialog.lbl.languageEffect = De taal zal veranderen de volgende keer dat u OpenRocket start.
-
generalprefs.lbl.language = Interface taal
generalprefs.languages.default = Systeemstandaard
generalprefs.lbl.languageEffect = De taal zal veranderen de volgende keer dat u OpenRocket start.
diff --git a/core/resources/l10n/messages_pl.properties b/core/resources/l10n/messages_pl.properties
index cc4246b5f..69ba91cdb 100644
--- a/core/resources/l10n/messages_pl.properties
+++ b/core/resources/l10n/messages_pl.properties
@@ -265,10 +265,10 @@
pref.dlg.PrefBooleanSelector2 = Potwierd\u017A
pref.dlg.Add = Dodaj
pref.dlg.DescriptionArea.Adddirectories = Dodaj katalogi, pliki silnikowe RASP (*.eng), Pliki silnikowe RockSim (*.rse) albo archiwa ZIP rozdzielone \u015Brednikiem (;) by za\u0142adowa\u0107 zewn\u0119trzne krzywe si\u0142y ci\u0105gu. Zmiany zostan\u0105 wprowadzone przy kolejnym uruchomieniu OpenRocket.
-
- PreferencesDialog.lbl.language = J\u0119zyk programu:
- PreferencesDialog.languages.default = Domy\u015Blny j\u0119zyk systemu
- PreferencesDialog.lbl.languageEffect = Nowy j\u0119zyk zostanie ustawiony przy kolejnym uruchomieniu OpenRocket.
+
+generalprefs.lbl.language = J\u0119zyk programu:
+generalprefs.languages.default = Domy\u015Blny j\u0119zyk systemu
+generalprefs.lbl.languageEffect = Nowy j\u0119zyk zostanie ustawiony przy kolejnym uruchomieniu OpenRocket.
! Software update checker
update.dlg.error.title = Nie mo\u017Cna uzyska\u0107 informacji o aktualizacji
diff --git a/core/resources/l10n/messages_pt.properties b/core/resources/l10n/messages_pt.properties
index b5d9d3ee9..89feaa3e6 100644
--- a/core/resources/l10n/messages_pt.properties
+++ b/core/resources/l10n/messages_pt.properties
@@ -709,9 +709,9 @@ PlotDialog.lbl.Chart = Clique e arraste para baixo+direita para am
# PlotDialog
PlotDialog.title.Flightdataplot = Plotagem dos dados de voo
-PreferencesDialog.languages.default = Padr\u00e3o do sistema
-PreferencesDialog.lbl.language = Idioma da interface:
-PreferencesDialog.lbl.languageEffect = A linguagem vai mudar na pr\u00f3xima vez que voc\u00ea iniciar o OpenRocket.
+generalprefs.languages.default = Padr\u00e3o do sistema
+generalprefs.lbl.language = Idioma da interface:
+generalprefs.lbl.languageEffect = A linguagem vai mudar na pr\u00f3xima vez que voc\u00ea iniciar o OpenRocket.
PresetModel.lbl.custompreset = Personalizado
PresetModel.lbl.partsLib = Biblioteca de pe\u00e7as
diff --git a/core/resources/l10n/messages_ru.properties b/core/resources/l10n/messages_ru.properties
index f5622ac0c..7da3fd403 100644
--- a/core/resources/l10n/messages_ru.properties
+++ b/core/resources/l10n/messages_ru.properties
@@ -341,10 +341,6 @@ pref.dlg.PrefBooleanSelector2 = \u041F\u043E\u0434\u0442\u0432\u0435\u0440\u0434
pref.dlg.Add = \u0414\u043E\u0431\u0430\u0432\u0438\u0442\u044C
pref.dlg.DescriptionArea.Adddirectories = \u0414\u043B\u044F \u0437\u0430\u0433\u0440\u0443\u0437\u043A\u0438 \u0434\u0430\u043D\u043D\u044B\u0445 \u0441\u0432\u043E\u0438\u0445 \u0434\u0432\u0438\u0433\u0430\u0442\u0435\u043B\u0435\u0439 \u0434\u043E\u0431\u0430\u0432\u044C\u0442\u0435 \u043A\u0430\u0442\u0430\u043B\u043E\u0433\u0438, \u0444\u0430\u0439\u043B\u044B \u0434\u0432\u0438\u0433\u0430\u0442\u0435\u043B\u0435\u0439 RASP (*.eng), \u0444\u0430\u0439\u043B\u044B \u0434\u0432\u0438\u0433\u0430\u0442\u0435\u043B\u0435\u0439 RockSim (*.rse) \u0438\u043B\u0438 ZIP-\u0430\u0440\u0445\u0438\u0432\u044B, \u0440\u0430\u0437\u0434\u0435\u043B\u0435\u043D\u043D\u044B\u0435 \u0442\u043E\u0447\u043A\u043E\u0439 \u0441 \u0437\u0430\u043F\u044F\u0442\u043E\u0439 (;). \u0418\u0437\u043C\u0435\u043D\u0435\u043D\u0438\u044F \u0432\u0441\u0442\u0443\u043F\u044F\u0442 \u0432 \u0441\u0438\u043B\u0443 \u043F\u0440\u0438 \u0441\u043B\u0435\u0434\u0443\u044E\u0449\u0435\u043C \u0437\u0430\u043F\u0443\u0441\u043A\u0435 OpenRocket.
-PreferencesDialog.lbl.language = \u042F\u0437\u044B\u043A \u0438\u043D\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u0430:
-PreferencesDialog.languages.default = \u0421\u0438\u0441\u0442\u0435\u043C\u043D\u044B\u0439
-PreferencesDialog.lbl.languageEffect = \u042F\u0437\u044B\u043A \u0441\u043C\u0435\u043D\u0438\u0442\u0441\u044F \u043F\u0440\u0438 \u043F\u0435\u0440\u0435\u0437\u0430\u043F\u0443\u0441\u043A\u0435 OpenRocket.
-
generalprefs.lbl.language = \u042F\u0437\u044B\u043A \u0438\u043D\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u0430
generalprefs.languages.default = \u0421\u0438\u0441\u0442\u0435\u043C\u043D\u044B\u0439
generalprefs.lbl.languageEffect = \u042F\u0437\u044B\u043A \u0441\u043C\u0435\u043D\u0438\u0442\u0441\u044F \u043F\u0440\u0438 \u043F\u0435\u0440\u0435\u0437\u0430\u043F\u0443\u0441\u043A\u0435 OpenRocket.
diff --git a/core/resources/l10n/messages_uk_UA.properties b/core/resources/l10n/messages_uk_UA.properties
index 4fea26db2..08550fd70 100644
--- a/core/resources/l10n/messages_uk_UA.properties
+++ b/core/resources/l10n/messages_uk_UA.properties
@@ -304,9 +304,9 @@ pref.dlg.PrefBooleanSelector2 = Confirm
pref.dlg.Add = Add
pref.dlg.DescriptionArea.Adddirectories = Add directories, RASP motor files (*.eng), RockSim engine files (*.rse) or ZIP archives separated by a semicolon (;) to load external thrust curves. Changes will take effect the next time you start OpenRocket.
-PreferencesDialog.lbl.language = Interface language:
-PreferencesDialog.languages.default = System default
-PreferencesDialog.lbl.languageEffect = The language will change the next time you start OpenRocket.
+generalprefs.lbl.language = Interface language:
+generalprefs.languages.default = System default
+generalprefs.lbl.languageEffect = The language will change the next time you start OpenRocket.
! Software update checker
update.dlg.error.title = Unable to retrieve update information
diff --git a/core/resources/l10n/messages_zh_CN.properties b/core/resources/l10n/messages_zh_CN.properties
index 05811ded0..0a6180ad8 100644
--- a/core/resources/l10n/messages_zh_CN.properties
+++ b/core/resources/l10n/messages_zh_CN.properties
@@ -797,9 +797,9 @@ PlotConfiguration.Verticalmotion = \u5782\u76F4\u8FD0\u52A8 vs. \u65F6\u95F
PlotDialog.CheckBox.Showdatapoints = \u663E\u793A\u6570\u636E\u70B9
PlotDialog.lbl.Chart = \u5DE6\u952E\u62D6\u62FD\u79FB\u52A8\u6570\u636E\u533A. \u6EDA\u8F6E\u7F29\u653E. ctrl-\u6EDA\u8F6E\u4EC5\u7F29\u653EX\u8F74. ctrl-\u5DE6\u952E\u62D6\u62FD\u79FB\u52A8\u89C6\u56FE. \u53F3\u952E\u8FC7\u62FD\u8C03\u6574\u663E\u793A\u5927\u5C0F.
-PreferencesDialog.languages.default = \u7CFB\u7EDF\u9ED8\u8BA4
-PreferencesDialog.lbl.language = \u754C\u9762\u8BED\u8A00:
-PreferencesDialog.lbl.languageEffect = \u8BED\u8A00\u8BBE\u7F6E\u5C06\u5728OpenRocket\u91CD\u542F\u540E\u751F\u6548
+generalprefs.languages.default = \u7CFB\u7EDF\u9ED8\u8BA4
+generalprefs.lbl.language = \u754C\u9762\u8BED\u8A00:
+generalprefs.lbl.languageEffect = \u8BED\u8A00\u8BBE\u7F6E\u5C06\u5728OpenRocket\u91CD\u542F\u540E\u751F\u6548
PresetModel.lbl.custompreset = \u5b9a\u5236
PresetModel.lbl.partsLib = \u96f6\u4ef6\u5e93
diff --git a/core/resources/pix/componenticons/pods-small.png b/core/resources/pix/componenticons/pods-small.png
index 17371e846..d000955a2 100644
Binary files a/core/resources/pix/componenticons/pods-small.png and b/core/resources/pix/componenticons/pods-small.png differ
diff --git a/core/resources/pix/icons/cd-override-subcomponent_dark.png b/core/resources/pix/icons/cd-override-subcomponent_dark.png
new file mode 100644
index 000000000..6ffec0bc4
Binary files /dev/null and b/core/resources/pix/icons/cd-override-subcomponent_dark.png differ
diff --git a/core/resources/pix/icons/cd-override-subcomponent.png b/core/resources/pix/icons/cd-override-subcomponent_light.png
similarity index 100%
rename from core/resources/pix/icons/cd-override-subcomponent.png
rename to core/resources/pix/icons/cd-override-subcomponent_light.png
diff --git a/core/resources/pix/icons/cd-override_dark.png b/core/resources/pix/icons/cd-override_dark.png
new file mode 100644
index 000000000..566d47bcf
Binary files /dev/null and b/core/resources/pix/icons/cd-override_dark.png differ
diff --git a/core/resources/pix/icons/cd-override.png b/core/resources/pix/icons/cd-override_light.png
similarity index 100%
rename from core/resources/pix/icons/cd-override.png
rename to core/resources/pix/icons/cd-override_light.png
diff --git a/core/resources/pix/icons/cg-override-subcomponent_dark.png b/core/resources/pix/icons/cg-override-subcomponent_dark.png
new file mode 100644
index 000000000..4930915e0
Binary files /dev/null and b/core/resources/pix/icons/cg-override-subcomponent_dark.png differ
diff --git a/core/resources/pix/icons/cg-override-subcomponent.png b/core/resources/pix/icons/cg-override-subcomponent_light.png
similarity index 100%
rename from core/resources/pix/icons/cg-override-subcomponent.png
rename to core/resources/pix/icons/cg-override-subcomponent_light.png
diff --git a/core/resources/pix/icons/cg-override_dark.png b/core/resources/pix/icons/cg-override_dark.png
new file mode 100644
index 000000000..1e1bb74f7
Binary files /dev/null and b/core/resources/pix/icons/cg-override_dark.png differ
diff --git a/core/resources/pix/icons/cg-override.png b/core/resources/pix/icons/cg-override_light.png
similarity index 100%
rename from core/resources/pix/icons/cg-override.png
rename to core/resources/pix/icons/cg-override_light.png
diff --git a/core/resources/pix/icons/mass-override-subcomponent_dark.png b/core/resources/pix/icons/mass-override-subcomponent_dark.png
new file mode 100755
index 000000000..60d6fa5b8
Binary files /dev/null and b/core/resources/pix/icons/mass-override-subcomponent_dark.png differ
diff --git a/core/resources/pix/icons/mass-override-subcomponent.png b/core/resources/pix/icons/mass-override-subcomponent_light.png
similarity index 100%
rename from core/resources/pix/icons/mass-override-subcomponent.png
rename to core/resources/pix/icons/mass-override-subcomponent_light.png
diff --git a/core/resources/pix/icons/mass-override_dark.png b/core/resources/pix/icons/mass-override_dark.png
new file mode 100755
index 000000000..3096bf60f
Binary files /dev/null and b/core/resources/pix/icons/mass-override_dark.png differ
diff --git a/core/resources/pix/icons/mass-override.png b/core/resources/pix/icons/mass-override_light.png
similarity index 100%
rename from core/resources/pix/icons/mass-override.png
rename to core/resources/pix/icons/mass-override_light.png
diff --git a/core/src/net/sf/openrocket/startup/Preferences.java b/core/src/net/sf/openrocket/startup/Preferences.java
index 02d272f03..4c4f60ef0 100644
--- a/core/src/net/sf/openrocket/startup/Preferences.java
+++ b/core/src/net/sf/openrocket/startup/Preferences.java
@@ -118,6 +118,8 @@ public abstract class Preferences implements ChangeSource {
public static final String LAUNCH_USE_ISA = "LaunchUseISA";
public static final String SIMULATION_TIME_STEP = "SimulationTimeStep";
public static final String GEODETIC_COMPUTATION = "GeodeticComputationStrategy";
+
+ public static final String UI_THEME = "UITheme";
private static final AtmosphericModel ISA_ATMOSPHERIC_MODEL = new ExtendedISAModel();
@@ -549,11 +551,6 @@ public abstract class Preferences implements ChangeSource {
fireChangeEvent();
}
-
- public final float getRocketInfoFontSize() {
- return (float) (11.0 + 3 * Application.getPreferences().getChoice(Preferences.ROCKET_INFO_FONT_SIZE, 2, 0));
- }
-
/**
* Enable/Disable the auto-opening of the last edited design file on startup.
*/
@@ -820,25 +817,6 @@ public abstract class Preferences implements ChangeSource {
}
}
- public Color getDefaultColor(Class extends RocketComponent> c) {
- String color = get("componentColors", c, StaticFieldHolder.DEFAULT_COLORS);
- if (color == null)
- return Color.BLACK;
-
- Color clr = parseColor(color);
- if (clr != null) {
- return clr;
- } else {
- return Color.BLACK;
- }
- }
-
- public final void setDefaultColor(Class extends RocketComponent> c, Color color) {
- if (color == null)
- return;
- putString("componentColors", c.getSimpleName(), stringifyColor(color));
- }
-
/**
* Retrieve a Line style for the given component.
@@ -1014,6 +992,29 @@ public abstract class Preferences implements ChangeSource {
public abstract void setComponentFavorite(ComponentPreset preset, ComponentPreset.Type type, boolean favorite);
public abstract Set getComponentFavorites(ComponentPreset.Type type);
+
+
+ /*
+ NOTE: It is unusual for the UI Theme to be stored in the preferences instead of SwingPreferences. In fact, this code
+ is not pretty. Sometimes I just really hate Java and circular dependencies...
+ But the reason why this is implemented is because it would otherwise be an even bigger nightmare to fix unit tests
+ that use their own preferences... Also wasn't a fan of always casting the preferences to SwingPreferences.
+ */
+ /**
+ * Get the current theme used for the UI.
+ * @return the current theme
+ */
+ public Object getUITheme() {
+ return null;
+ }
+
+ /**
+ * Set the theme used for the UI.
+ * @param theme the theme to set
+ */
+ public void setUITheme(Object theme) {}
+
+
/*
* Within a holder class so they will load only when needed.
@@ -1032,19 +1033,6 @@ public abstract class Preferences implements ChangeSource {
DEFAULT_LINE_STYLES.put(RocketComponent.class, LineStyle.SOLID.name());
DEFAULT_LINE_STYLES.put(MassObject.class, LineStyle.DASHED.name());
}
-
- private static final HashMap, String> DEFAULT_COLORS = new HashMap, String>();
-
- static {
- DEFAULT_COLORS.put(BodyComponent.class, "0,0,240");
- DEFAULT_COLORS.put(TubeFinSet.class, "0,0,200");
- DEFAULT_COLORS.put(FinSet.class, "0,0,200");
- DEFAULT_COLORS.put(LaunchLug.class, "0,0,180");
- DEFAULT_COLORS.put(RailButton.class, "0,0,180");
- DEFAULT_COLORS.put(InternalComponent.class, "170,0,100");
- DEFAULT_COLORS.put(MassObject.class, "0,0,0");
- DEFAULT_COLORS.put(RecoveryDevice.class, "255,0,0");
- }
}
private final List listeners = new ArrayList();
diff --git a/core/src/net/sf/openrocket/util/Color.java b/core/src/net/sf/openrocket/util/Color.java
index 7c3186d34..4f912bb4e 100644
--- a/core/src/net/sf/openrocket/util/Color.java
+++ b/core/src/net/sf/openrocket/util/Color.java
@@ -66,6 +66,10 @@ public class Color {
return new java.awt.Color(red, green, blue, alpha);
}
+ public static Color fromAWTColor(java.awt.Color AWTColor) {
+ return new Color(AWTColor.getRed(), AWTColor.getGreen(), AWTColor.getBlue(), AWTColor.getAlpha());
+ }
+
@Override
public boolean equals(Object obj) {
if (super.equals(obj)) {
diff --git a/install4j/23.xx/openrocket-23.xx.install4j b/install4j/23.xx/openrocket-23.xx.install4j
index 10a55f19a..c9c664d8f 100644
--- a/install4j/23.xx/openrocket-23.xx.install4j
+++ b/install4j/23.xx/openrocket-23.xx.install4j
@@ -48,6 +48,7 @@
+
diff --git a/swing/.classpath b/swing/.classpath
index d57e69a4c..5aa1c307e 100644
--- a/swing/.classpath
+++ b/swing/.classpath
@@ -11,6 +11,7 @@
+
diff --git a/swing/OpenRocket Swing.iml b/swing/OpenRocket Swing.iml
index c9df46ca4..55f1c84b1 100644
--- a/swing/OpenRocket Swing.iml
+++ b/swing/OpenRocket Swing.iml
@@ -5,6 +5,7 @@
+
@@ -107,6 +108,39 @@
+
+
+
+
+
+
+
+
+
+
+
+ ""
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/swing/build.xml b/swing/build.xml
index d1945c272..af521340f 100644
--- a/swing/build.xml
+++ b/swing/build.xml
@@ -74,7 +74,7 @@
Java/JVM detail version: ${java.version}
Compiling main classes
-
+
@@ -116,6 +116,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/swing/lib/darklaf/darklaf-compatibility-3.0.3-SNAPSHOT.jar b/swing/lib/darklaf/darklaf-compatibility-3.0.3-SNAPSHOT.jar
new file mode 100644
index 000000000..22945e1ff
Binary files /dev/null and b/swing/lib/darklaf/darklaf-compatibility-3.0.3-SNAPSHOT.jar differ
diff --git a/swing/lib/darklaf/darklaf-core-3.0.3-SNAPSHOT.jar b/swing/lib/darklaf/darklaf-core-3.0.3-SNAPSHOT.jar
new file mode 100644
index 000000000..964cae4b1
Binary files /dev/null and b/swing/lib/darklaf/darklaf-core-3.0.3-SNAPSHOT.jar differ
diff --git a/swing/lib/darklaf/darklaf-iconset-3.0.3-SNAPSHOT.jar b/swing/lib/darklaf/darklaf-iconset-3.0.3-SNAPSHOT.jar
new file mode 100644
index 000000000..e355b8786
Binary files /dev/null and b/swing/lib/darklaf/darklaf-iconset-3.0.3-SNAPSHOT.jar differ
diff --git a/swing/lib/darklaf/darklaf-macos-3.0.3-SNAPSHOT.jar b/swing/lib/darklaf/darklaf-macos-3.0.3-SNAPSHOT.jar
new file mode 100644
index 000000000..f2cc41960
Binary files /dev/null and b/swing/lib/darklaf/darklaf-macos-3.0.3-SNAPSHOT.jar differ
diff --git a/swing/lib/darklaf/darklaf-native-utils-3.0.3-SNAPSHOT.jar b/swing/lib/darklaf/darklaf-native-utils-3.0.3-SNAPSHOT.jar
new file mode 100644
index 000000000..26fdbb0ab
Binary files /dev/null and b/swing/lib/darklaf/darklaf-native-utils-3.0.3-SNAPSHOT.jar differ
diff --git a/swing/lib/darklaf/darklaf-platform-base-3.0.3-SNAPSHOT.jar b/swing/lib/darklaf/darklaf-platform-base-3.0.3-SNAPSHOT.jar
new file mode 100644
index 000000000..6b91bc95e
Binary files /dev/null and b/swing/lib/darklaf/darklaf-platform-base-3.0.3-SNAPSHOT.jar differ
diff --git a/swing/lib/darklaf/darklaf-platform-decorations-3.0.3-SNAPSHOT.jar b/swing/lib/darklaf/darklaf-platform-decorations-3.0.3-SNAPSHOT.jar
new file mode 100644
index 000000000..246e8d1b1
Binary files /dev/null and b/swing/lib/darklaf/darklaf-platform-decorations-3.0.3-SNAPSHOT.jar differ
diff --git a/swing/lib/darklaf/darklaf-platform-preferences-3.0.3-SNAPSHOT.jar b/swing/lib/darklaf/darklaf-platform-preferences-3.0.3-SNAPSHOT.jar
new file mode 100644
index 000000000..b1768e88f
Binary files /dev/null and b/swing/lib/darklaf/darklaf-platform-preferences-3.0.3-SNAPSHOT.jar differ
diff --git a/swing/lib/darklaf/darklaf-property-loader-3.0.3-SNAPSHOT.jar b/swing/lib/darklaf/darklaf-property-loader-3.0.3-SNAPSHOT.jar
new file mode 100644
index 000000000..1db5e00e0
Binary files /dev/null and b/swing/lib/darklaf/darklaf-property-loader-3.0.3-SNAPSHOT.jar differ
diff --git a/swing/lib/darklaf/darklaf-theme-3.0.3-SNAPSHOT.jar b/swing/lib/darklaf/darklaf-theme-3.0.3-SNAPSHOT.jar
new file mode 100644
index 000000000..b39af8f8b
Binary files /dev/null and b/swing/lib/darklaf/darklaf-theme-3.0.3-SNAPSHOT.jar differ
diff --git a/swing/lib/darklaf/darklaf-theme-spec-3.0.3-SNAPSHOT.jar b/swing/lib/darklaf/darklaf-theme-spec-3.0.3-SNAPSHOT.jar
new file mode 100644
index 000000000..e8119013f
Binary files /dev/null and b/swing/lib/darklaf/darklaf-theme-spec-3.0.3-SNAPSHOT.jar differ
diff --git a/swing/lib/darklaf/darklaf-utils-3.0.3-SNAPSHOT.jar b/swing/lib/darklaf/darklaf-utils-3.0.3-SNAPSHOT.jar
new file mode 100644
index 000000000..8e22ee456
Binary files /dev/null and b/swing/lib/darklaf/darklaf-utils-3.0.3-SNAPSHOT.jar differ
diff --git a/swing/lib/darklaf/darklaf-windows-3.0.3-SNAPSHOT.jar b/swing/lib/darklaf/darklaf-windows-3.0.3-SNAPSHOT.jar
new file mode 100644
index 000000000..139299518
Binary files /dev/null and b/swing/lib/darklaf/darklaf-windows-3.0.3-SNAPSHOT.jar differ
diff --git a/swing/lib/darklaf/jsvg-0.0.9.jar b/swing/lib/darklaf/jsvg-0.0.9.jar
new file mode 100644
index 000000000..e8fbeba38
Binary files /dev/null and b/swing/lib/darklaf/jsvg-0.0.9.jar differ
diff --git a/swing/lib/darklaf/swing-extensions-laf-support-0.1.3.jar b/swing/lib/darklaf/swing-extensions-laf-support-0.1.3.jar
new file mode 100644
index 000000000..2d2ec6b86
Binary files /dev/null and b/swing/lib/darklaf/swing-extensions-laf-support-0.1.3.jar differ
diff --git a/swing/lib/darklaf/swing-extensions-visual-padding-0.1.3.jar b/swing/lib/darklaf/swing-extensions-visual-padding-0.1.3.jar
new file mode 100644
index 000000000..5b39901e5
Binary files /dev/null and b/swing/lib/darklaf/swing-extensions-visual-padding-0.1.3.jar differ
diff --git a/swing/src/net/sf/openrocket/communication/AssetHandler.java b/swing/src/net/sf/openrocket/communication/AssetHandler.java
index a04d0080b..b0aa4d7b1 100644
--- a/swing/src/net/sf/openrocket/communication/AssetHandler.java
+++ b/swing/src/net/sf/openrocket/communication/AssetHandler.java
@@ -2,12 +2,10 @@ package net.sf.openrocket.communication;
import net.sf.openrocket.gui.util.SwingPreferences;
import net.sf.openrocket.startup.Application;
-import net.sf.openrocket.util.BuildProperties;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
-import java.util.TreeMap;
/**
* This class handles assets extracted from a GitHub release page.
diff --git a/swing/src/net/sf/openrocket/database/ComponentPresetDatabaseLoader.java b/swing/src/net/sf/openrocket/database/ComponentPresetDatabaseLoader.java
index ea0961462..177188785 100644
--- a/swing/src/net/sf/openrocket/database/ComponentPresetDatabaseLoader.java
+++ b/swing/src/net/sf/openrocket/database/ComponentPresetDatabaseLoader.java
@@ -3,9 +3,7 @@ package net.sf.openrocket.database;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
-import java.io.ObjectInputStream;
import java.util.Collection;
-import java.util.List;
import net.sf.openrocket.file.iterator.DirectoryIterator;
import net.sf.openrocket.file.iterator.FileIterator;
@@ -14,7 +12,6 @@ import net.sf.openrocket.gui.util.SwingPreferences;
import net.sf.openrocket.preset.ComponentPreset;
import net.sf.openrocket.preset.xml.OpenRocketComponentLoader;
import net.sf.openrocket.startup.Application;
-import net.sf.openrocket.util.BugException;
import net.sf.openrocket.util.Pair;
import org.slf4j.Logger;
diff --git a/swing/src/net/sf/openrocket/gui/components/BasicTree.java b/swing/src/net/sf/openrocket/gui/components/BasicTree.java
index 09d8b3d72..c3d75231e 100644
--- a/swing/src/net/sf/openrocket/gui/components/BasicTree.java
+++ b/swing/src/net/sf/openrocket/gui/components/BasicTree.java
@@ -1,5 +1,7 @@
package net.sf.openrocket.gui.components;
+import net.sf.openrocket.gui.util.GUIUtil;
+
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Component;
@@ -14,7 +16,6 @@ import javax.swing.tree.TreePath;
@SuppressWarnings("serial")
public class BasicTree extends JTree {
-
public BasicTree() {
super();
setDefaultOptions();
@@ -36,7 +37,7 @@ public class BasicTree extends JTree {
plainUI.setLeftChildIndent(15);
- this.setBackground(Color.WHITE);
+ this.setBackground(GUIUtil.getUITheme().getBackgroundColor());
this.setShowsRootHandles(false);
}
diff --git a/swing/src/net/sf/openrocket/gui/components/DescriptionArea.java b/swing/src/net/sf/openrocket/gui/components/DescriptionArea.java
index 2c666eff2..947f70335 100644
--- a/swing/src/net/sf/openrocket/gui/components/DescriptionArea.java
+++ b/swing/src/net/sf/openrocket/gui/components/DescriptionArea.java
@@ -1,9 +1,9 @@
package net.sf.openrocket.gui.components;
+import net.sf.openrocket.gui.util.GUIUtil;
import net.sf.openrocket.gui.util.URLUtil;
import java.awt.Color;
-import java.awt.Desktop;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Rectangle;
@@ -31,6 +31,8 @@ import javax.swing.SwingUtilities;
public class DescriptionArea extends JScrollPane {
private final JEditorPane editorPane;
+
+ private final float size;
/**
@@ -86,15 +88,14 @@ public class DescriptionArea extends JScrollPane {
public DescriptionArea(String text, int rows, float size, boolean opaque) {
super(ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED,
ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
+ this.size = size;
editorPane = new JEditorPane("text/html", "");
- Font font = editorPane.getFont();
- editorPane.setFont(font.deriveFont(font.getSize2D() + size));
editorPane.setEditable(false);
editorPane.addHyperlinkListener(new HyperlinkListener() {
public void hyperlinkUpdate(HyperlinkEvent e) {
if(e.getEventType() == HyperlinkEvent.EventType.ACTIVATED) {
- URI uri = null;
+ URI uri;
try {
uri = e.getURL().toURI();
}
@@ -125,8 +126,8 @@ public class DescriptionArea extends JScrollPane {
// create temporary file and copy resource to it
- File of = null;
- BufferedOutputStream os = null;
+ File of;
+ BufferedOutputStream os;
try {
of = File.createTempFile(prefix, suffix);
os = new BufferedOutputStream(new FileOutputStream(of));
@@ -163,11 +164,11 @@ public class DescriptionArea extends JScrollPane {
}
// Calculate correct height
- editorPane.setText("abc");
+ this.setText("abc");
Dimension oneline = editorPane.getPreferredSize();
- editorPane.setText("abc
def");
+ this.setText("abc
def");
Dimension twolines = editorPane.getPreferredSize();
- editorPane.setText("");
+ this.setText("");
int lineheight = twolines.height - oneline.height;
int extraheight = oneline.height - lineheight;
@@ -175,13 +176,20 @@ public class DescriptionArea extends JScrollPane {
Dimension dim = editorPane.getPreferredSize();
dim.height = lineheight * rows + extraheight + 2;
this.setPreferredSize(dim);
+
+ editorPane.setBorder(GUIUtil.getUITheme().getBorder());
this.setViewportView(editorPane);
this.setText(text);
}
public void setText(String txt) {
- editorPane.setText(txt);
+ // Set the font size (we can't simply set the font to change the font size, because we're using text/html)
+ Font defaultFont = editorPane.getFont();
+ String fontName = defaultFont.getFontName();
+ float fontSize = defaultFont.getSize2D() + size;
+
+ editorPane.setText("" + txt + "");
editorPane.revalidate();
SwingUtilities.invokeLater(new Runnable() {
diff --git a/swing/src/net/sf/openrocket/gui/components/URLLabel.java b/swing/src/net/sf/openrocket/gui/components/URLLabel.java
index 5656eaa96..3d9a45321 100644
--- a/swing/src/net/sf/openrocket/gui/components/URLLabel.java
+++ b/swing/src/net/sf/openrocket/gui/components/URLLabel.java
@@ -1,23 +1,18 @@
package net.sf.openrocket.gui.components;
-import java.awt.Color;
import java.awt.Cursor;
import java.awt.Desktop;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.font.TextAttribute;
-import java.io.IOException;
-import java.net.URI;
-import java.net.URISyntaxException;
import java.util.HashMap;
import java.util.Map;
+import net.sf.openrocket.gui.util.GUIUtil;
import net.sf.openrocket.gui.util.URLUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import net.sf.openrocket.util.BugException;
-
/**
* A label of a URL that is clickable. Clicking the URL will launch the URL in
* the default browser if the Desktop class is supported.
@@ -26,7 +21,7 @@ import net.sf.openrocket.util.BugException;
*/
public class URLLabel extends SelectableLabel {
private static final Logger log = LoggerFactory.getLogger(URLLabel.class);
-
+
/**
* Create a label showing the url it will direct to.
*
@@ -53,7 +48,7 @@ public class URLLabel extends SelectableLabel {
Map map = new HashMap();
map.put(TextAttribute.UNDERLINE, TextAttribute.UNDERLINE_ON);
this.setFont(this.getFont().deriveFont(map));
- this.setForeground(Color.BLUE);
+ this.setForeground(GUIUtil.getUITheme().getURLColor());
this.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
diff --git a/swing/src/net/sf/openrocket/gui/components/compass/Tester.java b/swing/src/net/sf/openrocket/gui/components/compass/Tester.java
index 97ca176b2..edac03799 100644
--- a/swing/src/net/sf/openrocket/gui/components/compass/Tester.java
+++ b/swing/src/net/sf/openrocket/gui/components/compass/Tester.java
@@ -22,7 +22,7 @@ public class Tester {
BasicApplication baseApp = new BasicApplication();
baseApp.initializeApplication();
- GUIUtil.setBestLAF();
+ GUIUtil.applyLAF();
SwingUtilities.invokeAndWait(new Runnable() {
@Override
diff --git a/swing/src/net/sf/openrocket/gui/configdialog/AppearancePanel.java b/swing/src/net/sf/openrocket/gui/configdialog/AppearancePanel.java
index d0569ea2f..f35e5dba2 100644
--- a/swing/src/net/sf/openrocket/gui/configdialog/AppearancePanel.java
+++ b/swing/src/net/sf/openrocket/gui/configdialog/AppearancePanel.java
@@ -2,7 +2,6 @@ package net.sf.openrocket.gui.configdialog;
import java.awt.Color;
import java.awt.Component;
-import java.awt.Desktop;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.lang.reflect.Method;
@@ -170,6 +169,7 @@ public class AppearancePanel extends JPanel {
Color awtColor = ColorConversion.toAwtColor(c);
colorChooser.setColor(awtColor);
+ colorChooser.updateUI(); // Needed for darklaf color chooser to update
// Bind a change of color selection to a change in the components color
ColorSelectionModel model = colorChooser.getSelectionModel();
@@ -183,7 +183,8 @@ public class AppearancePanel extends JPanel {
JDialog d = JColorChooser.createDialog(AppearancePanel.this,
trans.get("RocketCompCfg.lbl.Choosecolor"), true,
- colorChooser, new ActionListener() {
+ colorChooser,
+ new ActionListener() {
@Override
public void actionPerformed(ActionEvent okEvent) {
changeComponentColor(colorChooser.getColor());
@@ -262,8 +263,7 @@ public class AppearancePanel extends JPanel {
net.sf.openrocket.util.Color figureColor = c.getColor();
if (figureColor == null) {
- figureColor = Application.getPreferences().getDefaultColor(
- c.getClass());
+ figureColor = ((SwingPreferences) Application.getPreferences()).getDefaultColor(c.getClass());
}
final JButton figureColorButton = new SelectColorButton(
new ColorIcon(figureColor));
@@ -278,8 +278,7 @@ public class AppearancePanel extends JPanel {
public void stateChanged(EventObject e) {
net.sf.openrocket.util.Color col = c.getColor();
if (col == null) {
- col = Application.getPreferences().getDefaultColor(
- c.getClass());
+ col = ((SwingPreferences) Application.getPreferences()).getDefaultColor(c.getClass());
}
figureColorButton.setIcon(new ColorIcon(col));
}
diff --git a/swing/src/net/sf/openrocket/gui/configdialog/InnerTubeConfig.java b/swing/src/net/sf/openrocket/gui/configdialog/InnerTubeConfig.java
index c98e2c6c2..98709e734 100644
--- a/swing/src/net/sf/openrocket/gui/configdialog/InnerTubeConfig.java
+++ b/swing/src/net/sf/openrocket/gui/configdialog/InnerTubeConfig.java
@@ -40,6 +40,7 @@ import net.sf.openrocket.gui.adaptors.DoubleModel;
import net.sf.openrocket.gui.components.BasicSlider;
import net.sf.openrocket.gui.components.DescriptionArea;
import net.sf.openrocket.gui.components.UnitSelector;
+import net.sf.openrocket.gui.util.GUIUtil;
import net.sf.openrocket.gui.widgets.SelectColorButton;
import net.sf.openrocket.l10n.Translator;
import net.sf.openrocket.material.Material;
@@ -465,10 +466,17 @@ class ClusterSelectionPanel extends JPanel {
private static final int BUTTON_SIZE = 50;
private static final int MOTOR_DIAMETER = 10;
- private static final Color SELECTED_COLOR = Color.RED;
- private static final Color UNSELECTED_COLOR = Color.WHITE;
- private static final Color MOTOR_FILL_COLOR = Color.GREEN;
- private static final Color MOTOR_BORDER_COLOR = Color.BLACK;
+ private static final Color SELECTED_COLOR;
+ private static final Color UNSELECTED_COLOR;
+ private static final Color MOTOR_FILL_COLOR;
+ private static final Color MOTOR_BORDER_COLOR;
+
+ static {
+ SELECTED_COLOR = Color.RED;
+ UNSELECTED_COLOR = GUIUtil.getUITheme().getBackgroundColor();
+ MOTOR_FILL_COLOR = Color.GREEN;
+ MOTOR_BORDER_COLOR = Color.BLACK;
+ }
public ClusterSelectionPanel(Clusterable component) {
super(new MigLayout("gap 0 0",
diff --git a/swing/src/net/sf/openrocket/gui/configdialog/RocketComponentConfig.java b/swing/src/net/sf/openrocket/gui/configdialog/RocketComponentConfig.java
index fb9dc9a95..6a8dad3ef 100644
--- a/swing/src/net/sf/openrocket/gui/configdialog/RocketComponentConfig.java
+++ b/swing/src/net/sf/openrocket/gui/configdialog/RocketComponentConfig.java
@@ -506,7 +506,7 @@ public class RocketComponentConfig extends JPanel {
StyledLabel labelMassOverriddenBy = new StyledLabel(
String.format(trans.get("RocketCompCfg.lbl.MassOverriddenBy"), component.getMassOverriddenBy().getName()),
0, StyledLabel.Style.BOLD);
- labelMassOverriddenBy.setFontColor(net.sf.openrocket.util.Color.DARK_RED.toAWTColor());
+ labelMassOverriddenBy.setFontColor(GUIUtil.getUITheme().getDarkWarningColor());
labelMassOverriddenBy.setToolTipText(
String.format(trans.get("RocketCompCfg.lbl.MassOverriddenBy.ttip"), component.getMassOverriddenBy().getName()));
checkboxes.add(labelMassOverriddenBy, "gapleft 25lp, wrap");
@@ -569,7 +569,7 @@ public class RocketComponentConfig extends JPanel {
StyledLabel labelCGOverriddenBy = new StyledLabel(
String.format(trans.get("RocketCompCfg.lbl.CGOverriddenBy"), component.getCGOverriddenBy().getName()),
0, StyledLabel.Style.BOLD);
- labelCGOverriddenBy.setFontColor(net.sf.openrocket.util.Color.DARK_RED.toAWTColor());
+ labelCGOverriddenBy.setFontColor(GUIUtil.getUITheme().getDarkWarningColor());
labelCGOverriddenBy.setToolTipText(
String.format(trans.get("RocketCompCfg.lbl.CGOverriddenBy.ttip"), component.getCGOverriddenBy().getName()));
checkboxes.add(labelCGOverriddenBy, "gapleft 25lp, wrap");
@@ -663,7 +663,7 @@ public class RocketComponentConfig extends JPanel {
StyledLabel labelCDOverriddenBy = new StyledLabel(
String.format(trans.get("RocketCompCfg.lbl.CDOverriddenBy"), component.getCDOverriddenBy().getName()),
0, StyledLabel.Style.BOLD);
- labelCDOverriddenBy.setFontColor(net.sf.openrocket.util.Color.DARK_RED.toAWTColor());
+ labelCDOverriddenBy.setFontColor(GUIUtil.getUITheme().getDarkWarningColor());
labelCDOverriddenBy.setToolTipText(
String.format(trans.get("RocketCompCfg.lbl.CDOverriddenBy"), component.getCDOverriddenBy().getName()));
checkboxes.add(labelCDOverriddenBy, "gapleft 25lp, wrap");
@@ -719,6 +719,7 @@ public class RocketComponentConfig extends JPanel {
commentTextArea.setLineWrap(true);
commentTextArea.setWrapStyleWord(true);
commentTextArea.setEditable(true);
+ commentTextArea.setBorder(GUIUtil.getUITheme().getBorder());
GUIUtil.setTabToFocusing(commentTextArea);
commentTextArea.addFocusListener(textFieldListener);
commentTextArea.addKeyListener(new TextComponentSelectionKeyListener(commentTextArea));
diff --git a/swing/src/net/sf/openrocket/gui/configdialog/RocketConfig.java b/swing/src/net/sf/openrocket/gui/configdialog/RocketConfig.java
index acd4ee47b..9835778ca 100644
--- a/swing/src/net/sf/openrocket/gui/configdialog/RocketConfig.java
+++ b/swing/src/net/sf/openrocket/gui/configdialog/RocketConfig.java
@@ -27,7 +27,7 @@ import net.sf.openrocket.startup.Application;
public class RocketConfig extends RocketComponentConfig {
private static final Translator trans = Application.getTranslator();
-
+
private TextFieldListener textFieldListener;
private JTextArea designerTextArea;
@@ -55,6 +55,7 @@ public class RocketConfig extends RocketComponentConfig {
designerTextArea.setLineWrap(true);
designerTextArea.setWrapStyleWord(true);
designerTextArea.setEditable(true);
+ designerTextArea.setBorder(GUIUtil.getUITheme().getBorder());
GUIUtil.setTabToFocusing(designerTextArea);
designerTextArea.addFocusListener(textFieldListener);
this.add(new JScrollPane(designerTextArea), "wmin 400lp, height 60lp:60lp:, grow 30, wrap para");
@@ -69,6 +70,7 @@ public class RocketConfig extends RocketComponentConfig {
revisionTextArea.setLineWrap(true);
revisionTextArea.setWrapStyleWord(true);
revisionTextArea.setEditable(true);
+ revisionTextArea.setBorder(GUIUtil.getUITheme().getBorder());
GUIUtil.setTabToFocusing(revisionTextArea);
revisionTextArea.addFocusListener(textFieldListener);
diff --git a/swing/src/net/sf/openrocket/gui/customexpression/CustomExpressionPanel.java b/swing/src/net/sf/openrocket/gui/customexpression/CustomExpressionPanel.java
index 394daec86..c37e8ed2b 100644
--- a/swing/src/net/sf/openrocket/gui/customexpression/CustomExpressionPanel.java
+++ b/swing/src/net/sf/openrocket/gui/customexpression/CustomExpressionPanel.java
@@ -1,6 +1,5 @@
package net.sf.openrocket.gui.customexpression;
-import java.awt.Color;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
@@ -18,6 +17,7 @@ import javax.swing.JScrollPane;
import javax.swing.SwingUtilities;
import javax.swing.filechooser.FileNameExtensionFilter;
+import net.sf.openrocket.gui.util.GUIUtil;
import net.sf.openrocket.gui.util.SwingPreferences;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -39,7 +39,7 @@ public class CustomExpressionPanel extends JPanel {
private static final Logger log = LoggerFactory.getLogger(CustomExpressionPanel.class);
private static final Translator trans = Application.getTranslator();
-
+
private JPanel expressionSelectorPanel;
private OpenRocketDocument doc;
@@ -51,6 +51,7 @@ public class CustomExpressionPanel extends JPanel {
expressionSelectorPanel.setToolTipText(trans.get("customExpressionPanel.lbl.CalcNote"));
JScrollPane scroll = new JScrollPane(expressionSelectorPanel);
+ expressionSelectorPanel.setBorder(GUIUtil.getUITheme().getBorder());
//Border bdr = BorderFactory.createTitledBorder(trans.get("customExpressionPanel.lbl.CustomExpressions"));
//scroll.setBorder(bdr);
@@ -170,10 +171,10 @@ public class CustomExpressionPanel extends JPanel {
* A JPanel which configures a single expression
*/
private class SingleExpression extends JPanel {
-
+
// Convenience method to make the labels consistent
private JLabel setLabelStyle(JLabel l) {
- l.setBackground(Color.WHITE);
+ l.setBackground(GUIUtil.getUITheme().getBackgroundColor());
l.setOpaque(true);
l.setBorder(BorderFactory.createRaisedBevelBorder());
l.setText(" " + l.getText() + " ");
@@ -191,13 +192,13 @@ public class CustomExpressionPanel extends JPanel {
JLabel symbolLabel = new JLabel(trans.get("customExpression.Symbol") + " :");
JLabel symbol = new JLabel(expression.getSymbol());
symbol = setLabelStyle(symbol);
- symbol.setBackground(Color.WHITE);
+ symbol.setBackground(GUIUtil.getUITheme().getBackgroundColor());
JLabel unitLabel = new JLabel(trans.get("customExpression.Units") + " :");
UnitSelector unitSelector = new UnitSelector(expression.getType().getUnitGroup());
//JLabel unitSelector = new JLabel ( expression.getUnit() );
//unitSelector = setLabelStyle(unitSelector);
- //unitSelector.setBackground(Color.WHITE);
+ //unitSelector.setBackground(GUIUtil.getUITheme().getBackgroundColor());
JButton editButton = new SelectColorButton(Icons.EDIT_EDIT);
editButton.setToolTipText(trans.get("customExpression.Units.but.ttip.Edit"));
diff --git a/swing/src/net/sf/openrocket/gui/dialogs/AboutDialog.java b/swing/src/net/sf/openrocket/gui/dialogs/AboutDialog.java
index bb9f0b1eb..480c32367 100644
--- a/swing/src/net/sf/openrocket/gui/dialogs/AboutDialog.java
+++ b/swing/src/net/sf/openrocket/gui/dialogs/AboutDialog.java
@@ -60,6 +60,9 @@ public class AboutDialog extends JDialog {
"
" +
"See all contributors at
" + href("https://github.com/openrocket/openrocket/graphs/contributors", false, false) + "
" +
"
" +
+ "Thank you to our financial contributors who have provided us with the necessary resources to continue this project:
" +
+ href("https://opencollective.com/openrocket", true, true) + "
" +
+ "
" +
"OpenRocket utilizes the following libraries:
" +
"
" +
"MiG Layout" + href("http://www.miglayout.com", true, true) + "
" +
@@ -72,6 +75,7 @@ public class AboutDialog extends JDialog {
"Simple Logging Facade for Java" + href("http://www.slf4j.org", true, true) + "
" +
"Java library for parsing and rendering CommonMark" + href("https://github.com/commonmark/commonmark-java", true, true) + "
" +
"RSyntaxTextArea" + href("http://bobbylight.github.io/RSyntaxTextArea", true, true) + "
" +
+ "Darklaf (dark theme)" + href("https://github.com/weisJ/darklaf", true, true) + "
" +
"
" +
"OpenRocket gratefully acknowledges our use of the following databases:
" +
"
" +
diff --git a/swing/src/net/sf/openrocket/gui/dialogs/BugReportDialog.java b/swing/src/net/sf/openrocket/gui/dialogs/BugReportDialog.java
index 763f873b0..117384b51 100644
--- a/swing/src/net/sf/openrocket/gui/dialogs/BugReportDialog.java
+++ b/swing/src/net/sf/openrocket/gui/dialogs/BugReportDialog.java
@@ -1,5 +1,6 @@
package net.sf.openrocket.gui.dialogs;
+import java.awt.Color;
import java.awt.Dialog;
import java.awt.Dimension;
import java.awt.Window;
@@ -177,8 +178,9 @@ public class BugReportDialog extends JDialog {
private static void addBugReportInformation(StringBuilder sb) {
sb.append("---------- Bug report ----------\n");
sb.append('\n');
- sb.append("Please include a description about what actions you were " +
- "performing when the exception occurred:\n");
+ Color color = GUIUtil.getUITheme().getDarkWarningColor();
+ sb.append(String.format("Please include a description about what actions you were " +
+ "performing when the exception occurred:\n", color.getRed(), color.getGreen(), color.getBlue()));
sb.append("(You can edit text directly in this window)\n");
sb.append('\n');
sb.append("1. \n");
@@ -204,6 +206,7 @@ public class BugReportDialog extends JDialog {
sbTemp.append("OpenRocket source: " + BuildProperties.getBuildSource() + "\n");
sbTemp.append("OpenRocket location: " + JarUtil.getCurrentJarFile() + "\n");
sbTemp.append("User-defined thrust curves location: " + preferences.getUserThrustCurveFilesAsString() + "\n");
+ sbTemp.append("LAF: " + UIManager.getLookAndFeel().getClass().getName() + "\n");
sbTemp.append("JOGL version: " + JoglVersion.getInstance().getImplementationVersion() + "\n");
sbTemp.append("Current default locale: " + Locale.getDefault() + "\n");
sbTemp.append("System properties:\n");
diff --git a/swing/src/net/sf/openrocket/gui/dialogs/ComponentAnalysisDialog.java b/swing/src/net/sf/openrocket/gui/dialogs/ComponentAnalysisDialog.java
index 12cd39195..2a9d598d7 100644
--- a/swing/src/net/sf/openrocket/gui/dialogs/ComponentAnalysisDialog.java
+++ b/swing/src/net/sf/openrocket/gui/dialogs/ComponentAnalysisDialog.java
@@ -150,6 +150,7 @@ public class ComponentAnalysisDialog extends JDialog implements StateChangeListe
warningList = new JList<>();
JScrollPane scrollPane = new JScrollPane(warningList);
+ warningList.setBorder(GUIUtil.getUITheme().getBorder());
////Warnings:
scrollPane.setBorder(BorderFactory.createTitledBorder(trans.get("componentanalysisdlg.TitledBorder.warnings")));
panel.add(scrollPane, "gap paragraph, spany 4, w 300lp, grow, height :100lp:, wrap");
@@ -646,7 +647,7 @@ public class ComponentAnalysisDialog extends JDialog implements StateChangeListe
}
label.setOpaque(true);
- label.setBackground(Color.WHITE);
+ label.setBackground(GUIUtil.getUITheme().getBackgroundColor());
label.setHorizontalAlignment(SwingConstants.LEFT);
if ((row < 0) || (row >= data.size()))
@@ -693,7 +694,6 @@ public class ComponentAnalysisDialog extends JDialog implements StateChangeListe
private class DragCellRenderer extends CustomCellRenderer {
private static final long serialVersionUID = 1L;
-
public DragCellRenderer() {
super(dragData, 3);
}
@@ -712,6 +712,7 @@ public class ComponentAnalysisDialog extends JDialog implements StateChangeListe
float val = 1.0f;
label.setBackground(Color.getHSBColor(hue, sat, val));
+ label.setForeground(Color.BLACK);
}
if ((row < 0) || (row >= dragData.size()))
diff --git a/swing/src/net/sf/openrocket/gui/dialogs/DebugLogDialog.java b/swing/src/net/sf/openrocket/gui/dialogs/DebugLogDialog.java
index 4a2e81794..26bbd5ddf 100644
--- a/swing/src/net/sf/openrocket/gui/dialogs/DebugLogDialog.java
+++ b/swing/src/net/sf/openrocket/gui/dialogs/DebugLogDialog.java
@@ -67,7 +67,7 @@ public class DebugLogDialog extends JDialog {
private static final int POLL_TIME = 250;
private static final String STACK_TRACE_MARK = "\uFF01";
private static final Translator trans = Application.getTranslator();
-
+
private static final EnumMap backgroundColors = new EnumMap(LogLevel.class);
static {
for (LogLevel l : LogLevel.values()) {
@@ -343,6 +343,7 @@ public class DebugLogDialog extends JDialog {
bottomPanel.add(new JLabel(trans.get("debuglogdlg.lbl.Stacktrace")), "wrap rel");
stackTraceLabel = new JTextArea(8, 80);
stackTraceLabel.setEditable(false);
+ stackTraceLabel.setBorder(GUIUtil.getUITheme().getBorder());
GUIUtil.changeFontSize(stackTraceLabel, -2);
bottomPanel.add(new JScrollPane(stackTraceLabel), "grow, pushy 200, growprioy 200");
@@ -503,7 +504,7 @@ public class DebugLogDialog extends JDialog {
if (STACK_TRACE_MARK.equals(value)) {
fg = Color.RED;
} else {
- fg = table1.getForeground();
+ fg = Color.BLACK;
}
bg = backgroundColors.get(buffer.get(row).getLevel());
diff --git a/swing/src/net/sf/openrocket/gui/dialogs/ErrorWarningDialog.java b/swing/src/net/sf/openrocket/gui/dialogs/ErrorWarningDialog.java
index 335fe25b8..29e74dcde 100644
--- a/swing/src/net/sf/openrocket/gui/dialogs/ErrorWarningDialog.java
+++ b/swing/src/net/sf/openrocket/gui/dialogs/ErrorWarningDialog.java
@@ -3,6 +3,7 @@ package net.sf.openrocket.gui.dialogs;
import net.miginfocom.swing.MigLayout;
import net.sf.openrocket.gui.components.StyledLabel;
import net.sf.openrocket.gui.util.BetterListCellRenderer;
+import net.sf.openrocket.gui.util.GUIUtil;
import net.sf.openrocket.logging.Error;
import net.sf.openrocket.logging.ErrorSet;
import net.sf.openrocket.logging.Warning;
@@ -15,7 +16,6 @@ import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSeparator;
import javax.swing.ListSelectionModel;
-import java.awt.Color;
import java.awt.Component;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
@@ -25,11 +25,12 @@ import java.awt.event.MouseEvent;
*/
@SuppressWarnings("serial")
public abstract class ErrorWarningDialog {
+
public static void showErrorsAndWarnings(Component parent, Object message, String title, ErrorSet errors, WarningSet warnings) {
JPanel content = new JPanel(new MigLayout("ins 0, fillx"));
StyledLabel label = new StyledLabel("Errors");
- label.setFontColor(net.sf.openrocket.util.Color.DARK_RED.toAWTColor());
+ label.setFontColor(GUIUtil.getUITheme().getDarkWarningColor());
content.add(label, "wrap, gaptop 15lp");
Error[] e = errors.toArray(new Error[0]);
@@ -37,6 +38,7 @@ public abstract class ErrorWarningDialog {
errorList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
errorList.setCellRenderer(new ErrorListCellRenderer());
JScrollPane errorPane = new JScrollPane(errorList);
+ errorList.setBorder(GUIUtil.getUITheme().getBorder());
content.add(errorPane, "wrap, growx");
// Deselect items if clicked on blank region
@@ -58,6 +60,7 @@ public abstract class ErrorWarningDialog {
final JList warningList = new JList<>(w);
warningList.setCellRenderer(new BetterListCellRenderer());
JScrollPane warningPane = new JScrollPane(warningList);
+ warningList.setBorder(GUIUtil.getUITheme().getBorder());
content.add(warningPane, "wrap, growx");
// Deselect items if clicked on blank region
@@ -84,9 +87,9 @@ public abstract class ErrorWarningDialog {
// Text color
if (isSelected) {
- label.setForeground(Color.WHITE);
+ label.setForeground(GUIUtil.getUITheme().getTextSelectionForegroundColor());
} else {
- label.setForeground(net.sf.openrocket.util.Color.DARK_RED.toAWTColor());
+ label.setForeground(GUIUtil.getUITheme().getDarkWarningColor());
}
return label;
diff --git a/swing/src/net/sf/openrocket/gui/dialogs/UpdateInfoDialog.java b/swing/src/net/sf/openrocket/gui/dialogs/UpdateInfoDialog.java
index 006222aed..cf84659ca 100644
--- a/swing/src/net/sf/openrocket/gui/dialogs/UpdateInfoDialog.java
+++ b/swing/src/net/sf/openrocket/gui/dialogs/UpdateInfoDialog.java
@@ -1,16 +1,15 @@
package net.sf.openrocket.gui.dialogs;
import java.awt.Component;
-import java.awt.Desktop;
import java.awt.Dimension;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
-import java.net.URI;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
+import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
@@ -75,6 +74,7 @@ public class UpdateInfoDialog extends JDialog {
// Release information box
final JTextPane textPane = new JTextPane();
+ textPane.setBorder(BorderFactory.createLineBorder(GUIUtil.getUITheme().getTextColor()));
textPane.setEditable(false);
textPane.setContentType("text/html");
textPane.setMargin(new Insets(10, 10, 40, 10));
diff --git a/swing/src/net/sf/openrocket/gui/dialogs/WarningDialog.java b/swing/src/net/sf/openrocket/gui/dialogs/WarningDialog.java
index f856bd8a0..ec2a0a34a 100644
--- a/swing/src/net/sf/openrocket/gui/dialogs/WarningDialog.java
+++ b/swing/src/net/sf/openrocket/gui/dialogs/WarningDialog.java
@@ -2,23 +2,25 @@ package net.sf.openrocket.gui.dialogs;
import java.awt.Component;
+import javax.swing.BorderFactory;
import javax.swing.JList;
import javax.swing.JOptionPane;
import javax.swing.JScrollPane;
import net.sf.openrocket.gui.util.BetterListCellRenderer;
+import net.sf.openrocket.gui.util.GUIUtil;
import net.sf.openrocket.logging.Warning;
import net.sf.openrocket.logging.WarningSet;
@SuppressWarnings("serial")
public abstract class WarningDialog {
-
public static void showWarnings(Component parent, Object message, String title, WarningSet warnings) {
Warning[] w = warnings.toArray(new Warning[0]);
final JList list = new JList(w);
list.setCellRenderer(new BetterListCellRenderer());
JScrollPane pane = new JScrollPane(list);
+ pane.setBorder(GUIUtil.getUITheme().getBorder());
JOptionPane.showMessageDialog(parent, new Object[] { message, pane },
title, JOptionPane.WARNING_MESSAGE);
diff --git a/swing/src/net/sf/openrocket/gui/dialogs/flightconfiguration/RenameConfigDialog.java b/swing/src/net/sf/openrocket/gui/dialogs/flightconfiguration/RenameConfigDialog.java
index ee295ee9f..88222ec54 100644
--- a/swing/src/net/sf/openrocket/gui/dialogs/flightconfiguration/RenameConfigDialog.java
+++ b/swing/src/net/sf/openrocket/gui/dialogs/flightconfiguration/RenameConfigDialog.java
@@ -1,6 +1,5 @@
package net.sf.openrocket.gui.dialogs.flightconfiguration;
-import java.awt.Color;
import java.awt.Dialog;
import java.awt.Window;
import java.awt.event.ActionEvent;
@@ -25,7 +24,7 @@ import net.sf.openrocket.gui.widgets.SelectColorButton;
public class RenameConfigDialog extends JDialog {
private static final long serialVersionUID = -5423008694485357248L;
private static final Translator trans = Application.getTranslator();
-
+
public RenameConfigDialog(final Window parent, final Rocket rocket, final FlightConfigurationId fcid) {
super(parent, trans.get("RenameConfigDialog.title"), Dialog.ModalityType.APPLICATION_MODAL);
@@ -47,7 +46,7 @@ public class RenameConfigDialog extends JDialog {
RenameConfigDialog.this.setVisible(false);
}
});
- panel.add(okButton);
+ panel.add(okButton, "growx");
JButton renameToDefaultButton = new SelectColorButton(trans.get("RenameConfigDialog.but.reset"));
renameToDefaultButton.addActionListener(new ActionListener() {
@@ -57,7 +56,7 @@ public class RenameConfigDialog extends JDialog {
RenameConfigDialog.this.setVisible(false);
}
});
- panel.add(renameToDefaultButton);
+ panel.add(renameToDefaultButton, "growx");
JButton cancel = new SelectColorButton(trans.get("button.cancel"));
cancel.addActionListener(new ActionListener() {
@@ -66,14 +65,14 @@ public class RenameConfigDialog extends JDialog {
RenameConfigDialog.this.setVisible(false);
}
});
- panel.add(cancel, "wrap para");
+ panel.add(cancel, "growx, wrap para");
// {motors} & {manufacturers} info
String text = "" + CommonStrings.dagger + " " + trans.get("RenameConfigDialog.lbl.infoMotors")
+ trans.get("RenameConfigDialog.lbl.infoManufacturers")
+ trans.get("RenameConfigDialog.lbl.infoCombination");
StyledLabel info = new StyledLabel(text, -2);
- info.setFontColor(Color.DARK_GRAY);
+ info.setFontColor(GUIUtil.getUITheme().getDimTextColor());
panel.add(info, "spanx, growx, wrap");
this.add(panel);
diff --git a/swing/src/net/sf/openrocket/gui/dialogs/motor/thrustcurve/MotorInformationPanel.java b/swing/src/net/sf/openrocket/gui/dialogs/motor/thrustcurve/MotorInformationPanel.java
index dcbf04aa0..de63acf0e 100644
--- a/swing/src/net/sf/openrocket/gui/dialogs/motor/thrustcurve/MotorInformationPanel.java
+++ b/swing/src/net/sf/openrocket/gui/dialogs/motor/thrustcurve/MotorInformationPanel.java
@@ -37,14 +37,13 @@ import net.sf.openrocket.unit.UnitGroup;
@SuppressWarnings("serial")
class MotorInformationPanel extends JPanel {
-
+ private static final Translator trans = Application.getTranslator();
+
private static final int ZOOM_ICON_POSITION_NEGATIVE_X = 50;
private static final int ZOOM_ICON_POSITION_POSITIVE_Y = 12;
- private static final Color NO_COMMENT_COLOR = Color.GRAY;
- private static final Color WITH_COMMENT_COLOR = Color.BLACK;
-
- private static final Translator trans = Application.getTranslator();
+ private static final Color NO_COMMENT_COLOR = GUIUtil.getUITheme().getDimTextColor();
+ private static final Color WITH_COMMENT_COLOR = GUIUtil.getUITheme().getTextColor();
// Motors in set
private List selectedMotorSet;
@@ -190,7 +189,9 @@ class MotorInformationPanel extends JPanel {
changeLabelFont(plot.getDomainAxis(), -2);
//// Thrust curve:
- chart.setTitle(new TextTitle(trans.get("TCMotorSelPan.title.Thrustcurve"), this.getFont()));
+ TextTitle title = new TextTitle(trans.get("TCMotorSelPan.title.Thrustcurve"), this.getFont());
+ title.setPaint(GUIUtil.getUITheme().getTextColor());
+ chart.setTitle(title);
chart.setBackgroundPaint(this.getBackground());
plot.setBackgroundPaint(Color.WHITE);
plot.setDomainGridlinePaint(Color.LIGHT_GRAY);
diff --git a/swing/src/net/sf/openrocket/gui/dialogs/optimization/SimulationModifierTree.java b/swing/src/net/sf/openrocket/gui/dialogs/optimization/SimulationModifierTree.java
index 64b106336..8718a48a6 100644
--- a/swing/src/net/sf/openrocket/gui/dialogs/optimization/SimulationModifierTree.java
+++ b/swing/src/net/sf/openrocket/gui/dialogs/optimization/SimulationModifierTree.java
@@ -1,6 +1,5 @@
package net.sf.openrocket.gui.dialogs.optimization;
-import java.awt.Color;
import java.awt.Component;
import java.awt.Font;
import java.util.Enumeration;
@@ -17,6 +16,7 @@ import javax.swing.tree.TreePath;
import net.sf.openrocket.gui.components.BasicTree;
import net.sf.openrocket.gui.main.ComponentIcons;
+import net.sf.openrocket.gui.util.GUIUtil;
import net.sf.openrocket.l10n.Translator;
import net.sf.openrocket.optimization.rocketoptimization.SimulationModifier;
import net.sf.openrocket.rocketcomponent.Rocket;
@@ -37,7 +37,7 @@ public class SimulationModifierTree extends BasicTree {
private final List selectedModifiers;
private static final Translator trans = Application.getTranslator();
-
+
/**
* Sole constructor.
*
@@ -151,11 +151,13 @@ public class SimulationModifierTree extends BasicTree {
// Set icon (for rocket components, null for others)
setIcon(ComponentIcons.getSmallIcon(object.getClass()));
-
+
+ // By default, set background to transparent
+ setOpaque(false);
// Set text color/style
if (object instanceof RocketComponent) {
- setForeground(Color.GRAY);
+ setForeground(GUIUtil.getUITheme().getDimTextColor());
setFont(componentFont);
// Set tooltip
@@ -169,21 +171,24 @@ public class SimulationModifierTree extends BasicTree {
this.setToolTipText(null);
}
} else if (object instanceof String) {
- setForeground(Color.GRAY);
+ setForeground(GUIUtil.getUITheme().getDimTextColor());
setFont(stringFont);
} else if (object instanceof SimulationModifier) {
if (selectedModifiers.contains(object)) {
- setForeground(Color.GRAY);
+ setForeground(GUIUtil.getUITheme().getDimTextColor());
+ setFont(stringFont);
} else {
if (tree.getSelectionRows() != null &&
IntStream.of(tree.getSelectionRows()).anyMatch(r -> r == row)) {
- setForeground(Color.WHITE);
+ setForeground(GUIUtil.getUITheme().getTextSelectionForegroundColor());
+ setBackground(GUIUtil.getUITheme().getTextSelectionBackgroundColor());
+ setOpaque(true);
} else {
- setForeground(Color.BLACK);
+ setForeground(GUIUtil.getUITheme().getTextColor());
}
+ setFont(modifierFont);
}
- setFont(modifierFont);
setText(((SimulationModifier) object).getName());
setToolTipText(((SimulationModifier) object).getDescription());
}
diff --git a/swing/src/net/sf/openrocket/gui/dialogs/preferences/GeneralPreferencesPanel.java b/swing/src/net/sf/openrocket/gui/dialogs/preferences/GeneralPreferencesPanel.java
index 4457b9786..98e993c6f 100644
--- a/swing/src/net/sf/openrocket/gui/dialogs/preferences/GeneralPreferencesPanel.java
+++ b/swing/src/net/sf/openrocket/gui/dialogs/preferences/GeneralPreferencesPanel.java
@@ -20,9 +20,12 @@ import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
+import javax.swing.JSpinner;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
@@ -31,6 +34,8 @@ import net.sf.openrocket.communication.ReleaseInfo;
import net.sf.openrocket.communication.UpdateInfo;
import net.sf.openrocket.communication.UpdateInfoRetriever;
import net.sf.openrocket.communication.UpdateInfoRetriever.ReleaseStatus;
+import net.sf.openrocket.gui.SpinnerEditor;
+import net.sf.openrocket.gui.adaptors.IntegerModel;
import net.sf.openrocket.gui.components.DescriptionArea;
import net.sf.openrocket.gui.components.StyledLabel;
import net.sf.openrocket.gui.components.StyledLabel.Style;
@@ -40,6 +45,7 @@ import net.sf.openrocket.gui.util.SimpleFileFilter;
import net.sf.openrocket.gui.util.SwingPreferences;
import net.sf.openrocket.gui.util.PreferencesExporter;
import net.sf.openrocket.gui.util.PreferencesImporter;
+import net.sf.openrocket.gui.util.UITheme;
import net.sf.openrocket.l10n.L10N;
import net.sf.openrocket.logging.Markers;
import net.sf.openrocket.startup.Preferences;
@@ -50,13 +56,17 @@ import net.sf.openrocket.gui.widgets.SelectColorButton;
@SuppressWarnings("serial")
public class GeneralPreferencesPanel extends PreferencesPanel {
+ private final UITheme.Theme currentTheme;
+ private final int currentFontSize;
public GeneralPreferencesPanel(PreferencesDialog parent) {
super(parent, new MigLayout("fillx, ins 30lp n n n"));
-
+
+ this.currentTheme = GUIUtil.getUITheme();
+ this.currentFontSize = preferences.getUIFontSize();
//// Language selector
- Locale userLocale = null;
+ Locale userLocale;
{
String locale = preferences.getString("locale", null);
userLocale = L10N.toLocale(locale);
@@ -79,6 +89,7 @@ public class GeneralPreferencesPanel extends PreferencesPanel {
@SuppressWarnings("unchecked")
public void actionPerformed(ActionEvent e) {
Named selection = (Named) languageCombo.getSelectedItem();
+ if (selection == null) return;
Locale l = selection.get();
preferences.putString(Preferences.USER_LOCAL, l == null ? null : l.toString());
}
@@ -86,8 +97,64 @@ public class GeneralPreferencesPanel extends PreferencesPanel {
this.add(new JLabel(trans.get("generalprefs.lbl.language")), "gapright para");
this.add(languageCombo, "wrap rel, growx, sg combos");
- this.add(new StyledLabel(trans.get("generalprefs.lbl.languageEffect"), -3, Style.ITALIC), "span, wrap para*2");
-
+ this.add(new StyledLabel(trans.get("generalprefs.lbl.languageEffect"), -3, Style.ITALIC), "span, wrap rel");
+
+ //// UI Theme
+ UITheme.Theme currentTheme = GUIUtil.getUITheme();
+ List> themes = new ArrayList<>();
+ for (UITheme.Theme t : UITheme.Themes.values()) {
+ themes.add(new Named<>(t, t.getDisplayName()));
+ }
+ Collections.sort(themes);
+
+ final JComboBox> themesCombo = new JComboBox<>(themes.toArray());
+ for (int i = 0; i < themes.size(); i++) {
+ if (Utils.equals(currentTheme, themes.get(i).get())) {
+ themesCombo.setSelectedIndex(i);
+ }
+ }
+
+ this.add(new JLabel(trans.get("generalprefs.lbl.UITheme")), "gapright para");
+ this.add(themesCombo, "wrap, growx, sg combos");
+
+ //// Font size
+ this.add(new JLabel(trans.get("generalprefs.lbl.FontSize")), "gapright para");
+ final IntegerModel fontSizeModel = new IntegerModel(preferences, "UIFontSize", 5, 25);
+ final JSpinner fontSizeSpinner = new JSpinner(fontSizeModel.getSpinnerModel());
+ fontSizeSpinner.setEditor(new SpinnerEditor(fontSizeSpinner));
+ this.add(fontSizeSpinner, "growx, wrap");
+
+ //// You need to restart OpenRocket for the theme change to take effect.
+ final JLabel lblRestartORTheme = new JLabel();
+ lblRestartORTheme.setForeground(GUIUtil.getUITheme().getDarkWarningColor());
+ this.add(lblRestartORTheme, "spanx, wrap para*2, growx");
+
+ fontSizeSpinner.addChangeListener(new ChangeListener() {
+ @Override
+ public void stateChanged(ChangeEvent e) {
+ if (fontSizeModel.getValue() == currentFontSize) {
+ lblRestartORTheme.setText("");
+ return;
+ }
+ lblRestartORTheme.setText(trans.get("generalprefs.lbl.themeRestartOR"));
+ }
+ });
+ themesCombo.addActionListener(new ActionListener() {
+ @Override
+ @SuppressWarnings("unchecked")
+ public void actionPerformed(ActionEvent e) {
+ Named selection = (Named) themesCombo.getSelectedItem();
+ if (selection == null) return;
+ UITheme.Theme t = selection.get();
+ if (t == currentTheme) {
+ lblRestartORTheme.setText("");
+ return;
+ }
+ preferences.setUITheme(t);
+ lblRestartORTheme.setText(trans.get("generalprefs.lbl.themeRestartOR"));
+ }
+ });
+
//// User-defined thrust curves:
this.add(new JLabel(trans.get("pref.dlg.lbl.User-definedthrust")), "spanx, wrap");
final JTextField field = new JTextField();
@@ -180,8 +247,9 @@ public class GeneralPreferencesPanel extends PreferencesPanel {
this.add(button, "wrap");
//// Add directories, RASP motor files (*.eng), RockSim engine files (*.rse) or ZIP archives separated by a semicolon (;) to load external thrust curves. Changes will take effect the next time you start OpenRocket.
- DescriptionArea desc = new DescriptionArea(trans.get("pref.dlg.DescriptionArea.Adddirectories"), 3, -3, false);
- desc.setBackground(getBackground());
+ DescriptionArea desc = new DescriptionArea(trans.get("pref.dlg.DescriptionArea.Adddirectories"), 3, -1.5f, false);
+ desc.setBackground(GUIUtil.getUITheme().getBackgroundColor());
+ desc.setForeground(GUIUtil.getUITheme().getTextColor());
this.add(desc, "spanx, growx, wrap 40lp");
diff --git a/swing/src/net/sf/openrocket/gui/dialogs/preferences/LaunchPreferencesPanel.java b/swing/src/net/sf/openrocket/gui/dialogs/preferences/LaunchPreferencesPanel.java
index 459c6f747..2cb386954 100644
--- a/swing/src/net/sf/openrocket/gui/dialogs/preferences/LaunchPreferencesPanel.java
+++ b/swing/src/net/sf/openrocket/gui/dialogs/preferences/LaunchPreferencesPanel.java
@@ -23,6 +23,7 @@ import net.sf.openrocket.gui.adaptors.DoubleModel;
import net.sf.openrocket.gui.components.BasicSlider;
import net.sf.openrocket.gui.components.StyledLabel;
import net.sf.openrocket.gui.components.UnitSelector;
+import net.sf.openrocket.gui.util.GUIUtil;
import net.sf.openrocket.models.atmosphere.ExtendedISAModel;
import net.sf.openrocket.simulation.SimulationOptions;
import net.sf.openrocket.unit.UnitGroup;
@@ -43,7 +44,7 @@ public class LaunchPreferencesPanel extends PreferencesPanel {
StyledLabel warning = new StyledLabel(String.format(
"%s", trans.get("pref.dlg.lbl.launchWarning")),
0.5f, StyledLabel.Style.BOLD);
- warning.setFontColor(net.sf.openrocket.util.Color.DARK_RED.toAWTColor());
+ warning.setFontColor(GUIUtil.getUITheme().getDarkWarningColor());
warning.setToolTipText(trans.get("pref.dlg.lbl.launchWarning.ttip"));
add(warning, "spanx, growx 0, gapbottom para, wrap");
diff --git a/swing/src/net/sf/openrocket/gui/dialogs/preferences/SimulationPreferencesPanel.java b/swing/src/net/sf/openrocket/gui/dialogs/preferences/SimulationPreferencesPanel.java
index a45a68efc..051e312ac 100644
--- a/swing/src/net/sf/openrocket/gui/dialogs/preferences/SimulationPreferencesPanel.java
+++ b/swing/src/net/sf/openrocket/gui/dialogs/preferences/SimulationPreferencesPanel.java
@@ -17,6 +17,7 @@ import net.sf.openrocket.gui.adaptors.EnumModel;
import net.sf.openrocket.gui.components.BasicSlider;
import net.sf.openrocket.gui.components.StyledLabel;
import net.sf.openrocket.gui.components.UnitSelector;
+import net.sf.openrocket.gui.util.GUIUtil;
import net.sf.openrocket.simulation.RK4SimulationStepper;
import net.sf.openrocket.unit.UnitGroup;
import net.sf.openrocket.util.GeodeticComputationStrategy;
@@ -24,7 +25,7 @@ import net.sf.openrocket.gui.widgets.SelectColorButton;
public class SimulationPreferencesPanel extends PreferencesPanel {
private static final long serialVersionUID = 7983195730016979888L;
-
+
/*
* private GeodeticComputationStrategy geodeticComputation =
* GeodeticComputationStrategy.SPHERICAL;
@@ -84,7 +85,7 @@ public class SimulationPreferencesPanel extends PreferencesPanel {
StyledLabel warning = new StyledLabel(String.format(
"%s", trans.get("pref.dlg.lbl.launchWarning")),
0, StyledLabel.Style.BOLD);
- warning.setFontColor(net.sf.openrocket.util.Color.DARK_RED.toAWTColor());
+ warning.setFontColor(GUIUtil.getUITheme().getDarkWarningColor());
warning.setToolTipText(trans.get("pref.dlg.lbl.launchWarning.ttip"));
subsub.add(warning, "spanx, wrap para");
diff --git a/swing/src/net/sf/openrocket/gui/figure3d/FigureRenderer.java b/swing/src/net/sf/openrocket/gui/figure3d/FigureRenderer.java
index 65df4d52f..a9c2eacf4 100644
--- a/swing/src/net/sf/openrocket/gui/figure3d/FigureRenderer.java
+++ b/swing/src/net/sf/openrocket/gui/figure3d/FigureRenderer.java
@@ -10,6 +10,7 @@ import com.jogamp.opengl.fixedfunc.GLLightingFunc;
import net.sf.openrocket.gui.figure3d.geometry.Geometry;
import net.sf.openrocket.gui.figure3d.geometry.Geometry.Surface;
+import net.sf.openrocket.gui.util.SwingPreferences;
import net.sf.openrocket.motor.Motor;
import net.sf.openrocket.rocketcomponent.BodyTube;
import net.sf.openrocket.rocketcomponent.ExternalComponent;
@@ -80,7 +81,7 @@ public class FigureRenderer extends RocketRenderer {
if (defaultColorCache.containsKey(c.getClass())) {
figureColor = defaultColorCache.get(c.getClass());
} else {
- figureColor = Application.getPreferences().getDefaultColor(c.getClass());
+ figureColor = ((SwingPreferences) Application.getPreferences()).getDefaultColor(c.getClass());
defaultColorCache.put(c.getClass(), figureColor);
}
}
diff --git a/swing/src/net/sf/openrocket/gui/figure3d/RealisticRenderer.java b/swing/src/net/sf/openrocket/gui/figure3d/RealisticRenderer.java
index 578817921..41cac74ba 100644
--- a/swing/src/net/sf/openrocket/gui/figure3d/RealisticRenderer.java
+++ b/swing/src/net/sf/openrocket/gui/figure3d/RealisticRenderer.java
@@ -42,9 +42,9 @@ public class RealisticRenderer extends RocketRenderer {
gl.glLightModelfv(GL2ES1.GL_LIGHT_MODEL_AMBIENT, new float[] { 0, 0, 0 }, 0);
- float amb = 0.3f;
- float dif = 1.0f - amb;
- float spc = 1.0f;
+ float amb = 0.4f;
+ float dif = 0.65f;
+ float spc = 0.65f;
gl.glLightfv(GLLightingFunc.GL_LIGHT1, GLLightingFunc.GL_AMBIENT, new float[] { amb, amb, amb, 1 }, 0);
gl.glLightfv(GLLightingFunc.GL_LIGHT1, GLLightingFunc.GL_DIFFUSE, new float[] { dif, dif, dif, 1 }, 0);
gl.glLightfv(GLLightingFunc.GL_LIGHT1, GLLightingFunc.GL_SPECULAR, new float[] { spc, spc, spc, 1 }, 0);
diff --git a/swing/src/net/sf/openrocket/gui/figure3d/RocketFigure3d.java b/swing/src/net/sf/openrocket/gui/figure3d/RocketFigure3d.java
index 38769db88..cc65d95ae 100644
--- a/swing/src/net/sf/openrocket/gui/figure3d/RocketFigure3d.java
+++ b/swing/src/net/sf/openrocket/gui/figure3d/RocketFigure3d.java
@@ -34,6 +34,7 @@ import javax.swing.JPopupMenu;
import javax.swing.SwingUtilities;
import javax.swing.event.MouseInputAdapter;
+import net.sf.openrocket.gui.util.GUIUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -64,7 +65,7 @@ public class RocketFigure3d extends JPanel implements GLEventListener {
private static final long serialVersionUID = 1L;
private static final Logger log = LoggerFactory.getLogger(RocketFigure3d.class);
-
+
static {
//this allows the GL canvas and things like the motor selection
//drop down to z-order themselves.
@@ -288,7 +289,9 @@ public class RocketFigure3d extends JPanel implements GLEventListener {
GL2 gl = drawable.getGL().getGL2();
GLU glu = new GLU();
- gl.glClearColor(1, 1, 1, 1);
+ Color backgroundColor = GUIUtil.getUITheme().getBackgroundColor();
+ gl.glClearColor(backgroundColor.getRed()/255f, backgroundColor.getGreen()/255f,
+ backgroundColor.getBlue()/255f, backgroundColor.getAlpha()/255f);
gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);
setupView(gl, glu);
@@ -315,8 +318,9 @@ public class RocketFigure3d extends JPanel implements GLEventListener {
}
pickPoint = null;
-
- gl.glClearColor(1, 1, 1, 1);
+
+ gl.glClearColor(backgroundColor.getRed()/255f, backgroundColor.getGreen()/255f,
+ backgroundColor.getBlue()/255f, backgroundColor.getAlpha()/255f);
gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);
gl.glEnable(GL.GL_MULTISAMPLE);
diff --git a/swing/src/net/sf/openrocket/gui/figure3d/photo/PhotoFrame.java b/swing/src/net/sf/openrocket/gui/figure3d/photo/PhotoFrame.java
index 76af03823..d43c79ef6 100644
--- a/swing/src/net/sf/openrocket/gui/figure3d/photo/PhotoFrame.java
+++ b/swing/src/net/sf/openrocket/gui/figure3d/photo/PhotoFrame.java
@@ -371,9 +371,9 @@ public class PhotoFrame extends JFrame {
guiModule.startLoader();
- // Set the best available look-and-feel
- log.info("Setting best LAF");
- GUIUtil.setBestLAF();
+ // Set the look-and-feel
+ log.info("Setting LAF");
+ GUIUtil.applyLAF();
// Load defaults
((SwingPreferences) Application.getPreferences()).loadDefaultUnits();
diff --git a/swing/src/net/sf/openrocket/gui/figureelements/CGCaret.java b/swing/src/net/sf/openrocket/gui/figureelements/CGCaret.java
index 14a8a677d..77361260f 100644
--- a/swing/src/net/sf/openrocket/gui/figureelements/CGCaret.java
+++ b/swing/src/net/sf/openrocket/gui/figureelements/CGCaret.java
@@ -1,5 +1,7 @@
package net.sf.openrocket.gui.figureelements;
+import net.sf.openrocket.gui.util.GUIUtil;
+
import java.awt.Color;
import java.awt.geom.Area;
import java.awt.geom.Ellipse2D;
@@ -16,7 +18,7 @@ public class CGCaret extends Caret {
private static final float RADIUS = 7;
private static Area caret = null;
-
+
/**
* Create a new CGCaret at the specified coordinates.
*/
@@ -56,7 +58,7 @@ public class CGCaret extends Caret {
*/
@Override
protected Color getColor() {
- return Color.BLUE;
+ return GUIUtil.getUITheme().getCGColor();
}
}
diff --git a/swing/src/net/sf/openrocket/gui/figureelements/CPCaret.java b/swing/src/net/sf/openrocket/gui/figureelements/CPCaret.java
index 09e9cceba..a5b66a233 100644
--- a/swing/src/net/sf/openrocket/gui/figureelements/CPCaret.java
+++ b/swing/src/net/sf/openrocket/gui/figureelements/CPCaret.java
@@ -1,5 +1,7 @@
package net.sf.openrocket.gui.figureelements;
+import net.sf.openrocket.gui.util.GUIUtil;
+
import java.awt.Color;
import java.awt.geom.Area;
import java.awt.geom.Ellipse2D;
@@ -15,7 +17,7 @@ public class CPCaret extends Caret {
private static final float RADIUS = 7;
private static Area caret = null;
-
+
/**
* Create a new CPCaret at the specified coordinates.
*/
@@ -51,6 +53,6 @@ public class CPCaret extends Caret {
*/
@Override
protected Color getColor() {
- return Color.RED;
+ return GUIUtil.getUITheme().getCPColor();
}
}
diff --git a/swing/src/net/sf/openrocket/gui/figureelements/RocketInfo.java b/swing/src/net/sf/openrocket/gui/figureelements/RocketInfo.java
index 1af88b65b..1836ae644 100644
--- a/swing/src/net/sf/openrocket/gui/figureelements/RocketInfo.java
+++ b/swing/src/net/sf/openrocket/gui/figureelements/RocketInfo.java
@@ -3,7 +3,6 @@ package net.sf.openrocket.gui.figureelements;
import static net.sf.openrocket.util.Chars.ALPHA;
import static net.sf.openrocket.util.Chars.THETA;
-import java.awt.Color;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics2D;
@@ -11,6 +10,7 @@ import java.awt.Rectangle;
import java.awt.font.GlyphVector;
import java.awt.geom.Rectangle2D;
+import net.sf.openrocket.gui.util.GUIUtil;
import net.sf.openrocket.gui.util.SwingPreferences;
import net.sf.openrocket.logging.Warning;
import net.sf.openrocket.logging.WarningSet;
@@ -176,7 +176,7 @@ public class RocketInfo implements FigureElement {
GlyphVector massLineWithoutMotors = createText(massTextWithoutMotors);
- g2.setColor(Color.BLACK);
+ g2.setColor(GUIUtil.getUITheme().getTextColor());
g2.drawGlyphVector(name, x1, y1);
g2.drawGlyphVector(lengthLine, x1, y1+line);
@@ -234,7 +234,7 @@ public class RocketInfo implements FigureElement {
unitWidth = unitWidth + spaceWidth;
stabUnitWidth = stabUnitWidth + spaceWidth;
- g2.setColor(Color.BLACK);
+ g2.setColor(GUIUtil.getUITheme().getTextColor());
// Draw the stability, CG & CP values (and units)
g2.drawGlyphVector(stabValue, (float)(x2-stabRect.getWidth()), y1);
@@ -261,7 +261,7 @@ public class RocketInfo implements FigureElement {
atPos = (float)(x2 - atTextRect.getWidth());
}
- g2.setColor(Color.GRAY);
+ g2.setColor(GUIUtil.getUITheme().getDimTextColor());
g2.drawGlyphVector(atText, atPos, y1 + 3*line);
}
@@ -411,7 +411,7 @@ public class RocketInfo implements FigureElement {
float y = y2 - line * (texts.length-1);
- g2.setColor(Color.RED);
+ g2.setColor(GUIUtil.getUITheme().getWarningColor());
for (GlyphVector v: texts) {
Rectangle2D rect = v.getVisualBounds();
@@ -427,7 +427,7 @@ public class RocketInfo implements FigureElement {
if (calculatingData) {
//// Calculating...
GlyphVector calculating = createText(trans.get("RocketInfo.Calculating"));
- g2.setColor(Color.BLACK);
+ g2.setColor(GUIUtil.getUITheme().getTextColor());
g2.drawGlyphVector(calculating, x1, (float)(y2-height));
}
}
@@ -485,11 +485,10 @@ public class RocketInfo implements FigureElement {
width += 5;
if (!calculatingData)
- g2.setColor(new Color(0,0,127));
+ g2.setColor(GUIUtil.getUITheme().getFlightDataTextActiveColor());
else
- g2.setColor(new Color(0,0,127,127));
+ g2.setColor(GUIUtil.getUITheme().getFlightDataTextInactiveColor());
-
g2.drawGlyphVector(apogee, (float)x1, (float)(y2-2*line));
g2.drawGlyphVector(maxVelocity, (float)x1, (float)(y2-line));
g2.drawGlyphVector(maxAcceleration, (float)x1, (float)(y2));
@@ -502,7 +501,7 @@ public class RocketInfo implements FigureElement {
}
private synchronized void updateFontSizes() {
- float size = Application.getPreferences().getRocketInfoFontSize();
+ float size = ((SwingPreferences) Application.getPreferences()).getRocketInfoFontSize();
// No change necessary as the font is the same size, just use the existing version
if (font.getSize2D() == size) {
return;
diff --git a/swing/src/net/sf/openrocket/gui/help/tours/SlideSetManager.java b/swing/src/net/sf/openrocket/gui/help/tours/SlideSetManager.java
index 0d9e38155..7828dfd8d 100644
--- a/swing/src/net/sf/openrocket/gui/help/tours/SlideSetManager.java
+++ b/swing/src/net/sf/openrocket/gui/help/tours/SlideSetManager.java
@@ -1,5 +1,6 @@
package net.sf.openrocket.gui.help.tours;
+import java.awt.Color;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
@@ -11,6 +12,7 @@ import java.util.Map;
import javax.swing.text.html.StyleSheet;
+import net.sf.openrocket.gui.util.GUIUtil;
import net.sf.openrocket.util.BugException;
/**
@@ -130,6 +132,9 @@ public class SlideSetManager {
try {
StyleSheet ss = new StyleSheet();
+ Color textColor = GUIUtil.getUITheme().getTextColor();
+ ss.addRule(String.format("p { color: rgb(%d, %d, %d, %d)",
+ textColor.getRed(), textColor.getGreen(), textColor.getBlue(), textColor.getAlpha()));
InputStreamReader reader = new InputStreamReader(in, "UTF-8");
ss.loadRules(reader, null);
return ss;
diff --git a/swing/src/net/sf/openrocket/gui/help/tours/SlideShowComponent.java b/swing/src/net/sf/openrocket/gui/help/tours/SlideShowComponent.java
index 07cffdcc6..561ee518b 100644
--- a/swing/src/net/sf/openrocket/gui/help/tours/SlideShowComponent.java
+++ b/swing/src/net/sf/openrocket/gui/help/tours/SlideShowComponent.java
@@ -12,6 +12,7 @@ import javax.swing.text.html.HTMLDocument;
import javax.swing.text.html.StyleSheet;
import net.sf.openrocket.gui.components.ImageDisplayComponent;
+import net.sf.openrocket.gui.util.GUIUtil;
/**
* Component that displays a single slide, with the image on top and
@@ -28,7 +29,7 @@ public class SlideShowComponent extends JSplitPane {
private final ImageDisplayComponent imageDisplay;
private final JEditorPane textPane;
-
+
public SlideShowComponent() {
super(VERTICAL_SPLIT);
@@ -44,6 +45,7 @@ public class SlideShowComponent extends JSplitPane {
textPane.setPreferredSize(new Dimension(WIDTH, HEIGHT_TEXT));
JScrollPane scrollPanel = new JScrollPane(textPane);
+ textPane.setBorder(GUIUtil.getUITheme().getBorder());
this.setRightComponent(scrollPanel);
this.setResizeWeight(((double) HEIGHT_IMAGE) / (HEIGHT_IMAGE + HEIGHT_TEXT));
diff --git a/swing/src/net/sf/openrocket/gui/main/ComponentAddButtons.java b/swing/src/net/sf/openrocket/gui/main/ComponentAddButtons.java
index 69869526c..1f194ce05 100644
--- a/swing/src/net/sf/openrocket/gui/main/ComponentAddButtons.java
+++ b/swing/src/net/sf/openrocket/gui/main/ComponentAddButtons.java
@@ -4,6 +4,7 @@ package net.sf.openrocket.gui.main;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Rectangle;
+import java.awt.Font;
import java.awt.event.ActionEvent;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
@@ -209,7 +210,7 @@ public class ComponentAddButtons extends JPanel implements Scrollable {
for (col = 0; col < buttons[row].length; col++) {
buttons[row][col].setMinimumSize(d);
buttons[row][col].setPreferredSize(d);
- buttons[row][col].getComponent(0).validate();
+ buttons[row][col].validate();
}
}
@@ -315,29 +316,32 @@ public class ComponentAddButtons extends JPanel implements Scrollable {
* The label may contain "\n" as a newline.
*/
public ComponentButton(String text, Icon enabled, Icon disabled) {
- super();
- setLayout(new MigLayout("fill, flowy, insets 0, gap 0", "", ""));
-
- add(new JLabel(), "push, sizegroup spacing");
-
- // Add Icon
- if (enabled != null) {
- JLabel label = new JLabel(enabled);
- if (disabled != null)
- label.setDisabledIcon(disabled);
- add(label, "growx");
+ super(text, enabled);
+
+ setVerticalTextPosition(SwingConstants.BOTTOM); // this will put the text below the icon
+ setHorizontalTextPosition(SwingConstants.CENTER); // this will center the text horizontally beneath the icon
+ //setIconTextGap(0); // this is optional, it sets the gap between the icon and the text
+
+ // set the disabled icon if it is not null
+ if (disabled != null) {
+ setDisabledIcon(disabled);
}
-
- // Add labels
- String[] l = text.split("\n");
- for (int i = 0; i < l.length; i++) {
- add(new StyledLabel(l[i], SwingConstants.CENTER, -2.0f), "growx");
+
+ setHorizontalAlignment(SwingConstants.CENTER); // this will center the button itself in its parent component
+
+ // if you have multiline text, you could use html to format it
+ if (text != null && text.contains("\n")) {
+ text = "" + text.replace("\n", "
") + "";
+ setText(text);
+ }
+
+ // Initialize enabled status
+ valueChanged(null);
+
+ // Attach a tree selection listener if selection model is not null
+ if (selectionModel != null) {
+ selectionModel.addTreeSelectionListener(this);
}
-
- add(new JLabel(), "push, sizegroup spacing");
-
- valueChanged(null); // Update enabled status
- selectionModel.addTreeSelectionListener(this);
}
diff --git a/swing/src/net/sf/openrocket/gui/main/DesignPanel.java b/swing/src/net/sf/openrocket/gui/main/DesignPanel.java
index 226c7f8c8..f4e7c274d 100644
--- a/swing/src/net/sf/openrocket/gui/main/DesignPanel.java
+++ b/swing/src/net/sf/openrocket/gui/main/DesignPanel.java
@@ -174,6 +174,7 @@ public class DesignPanel extends JSplitPane {
// Place tree inside scroll pane
JScrollPane scroll = new JScrollPane(tree);
+ tree.setBorder(GUIUtil.getUITheme().getBorder());
panel.add(scroll, "spany, wmin 140px, grow, wrap");
diff --git a/swing/src/net/sf/openrocket/gui/main/SimulationPanel.java b/swing/src/net/sf/openrocket/gui/main/SimulationPanel.java
index 0bac65b69..36f87e2da 100644
--- a/swing/src/net/sf/openrocket/gui/main/SimulationPanel.java
+++ b/swing/src/net/sf/openrocket/gui/main/SimulationPanel.java
@@ -5,6 +5,7 @@ import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
+import java.awt.Font;
import java.awt.Toolkit;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.DataFlavor;
diff --git a/swing/src/net/sf/openrocket/gui/main/componenttree/ComponentTreeRenderer.java b/swing/src/net/sf/openrocket/gui/main/componenttree/ComponentTreeRenderer.java
index a852e94b7..e5d422e36 100644
--- a/swing/src/net/sf/openrocket/gui/main/componenttree/ComponentTreeRenderer.java
+++ b/swing/src/net/sf/openrocket/gui/main/componenttree/ComponentTreeRenderer.java
@@ -1,20 +1,22 @@
package net.sf.openrocket.gui.main.componenttree;
+import java.awt.BorderLayout;
import java.awt.Component;
-import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Font;
import java.util.List;
+import javax.swing.BorderFactory;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTree;
import javax.swing.UIManager;
+import javax.swing.border.Border;
import javax.swing.tree.DefaultTreeCellRenderer;
import javax.swing.tree.TreePath;
import net.sf.openrocket.gui.main.ComponentIcons;
-import net.sf.openrocket.gui.util.Icons;
+import net.sf.openrocket.gui.util.GUIUtil;
import net.sf.openrocket.l10n.Translator;
import net.sf.openrocket.rocketcomponent.MassComponent;
import net.sf.openrocket.rocketcomponent.MassComponent.MassComponentType;
@@ -34,45 +36,83 @@ public class ComponentTreeRenderer extends DefaultTreeCellRenderer {
boolean sel, boolean expanded, boolean leaf, int row,
boolean hasFocus1) {
- Component comp = super.getTreeCellRendererComponent(tree, value, sel,
- expanded, leaf, row, hasFocus1);
+ // Create a new JPanel
+ JPanel panel = new JPanel();
+ panel.setOpaque(false); // Set this to false if you want to keep the tree's default background intact
+ panel.setLayout(new BorderLayout());
+
+ // Create two JLabels, one for the icon and one for the text
+ JLabel iconLabel = new JLabel();
+ JLabel textLabel = new JLabel();
+
+ // Retrieve the component from the super method
+ Component comp = super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf, row, hasFocus1);
if (tree == null) return comp;
TreePath[] paths = tree.getSelectionPaths();
List components = null;
if (paths != null && paths.length > 0) {
components = new ArrayList<>(ComponentTreeModel.componentsFromPaths(paths));
}
+ if (comp instanceof JLabel) {
+ textLabel.setText(((JLabel) comp).getText());
- // Set icon
+ // Set the font to the tree font
+ Font treeFont = UIManager.getFont("Tree.font");
+ textLabel.setFont(treeFont);
+ }
+
+ // Set the icon
RocketComponent c = (RocketComponent) value;
+ Border iconMarginBorder = BorderFactory.createEmptyBorder(0, 0, 0, 4); // 4-pixel gap to the right of the icon
if (c.getClass().isAssignableFrom(MassComponent.class)) {
MassComponentType t = ((MassComponent) c).getMassComponentType();
- setIcon(ComponentIcons.getSmallMassTypeIcon(t));
+ iconLabel.setIcon(ComponentIcons.getSmallMassTypeIcon(t));
+ iconLabel.setBorder(iconMarginBorder);
} else {
- setIcon(ComponentIcons.getSmallIcon(value.getClass()));
+ iconLabel.setIcon(ComponentIcons.getSmallIcon(value.getClass()));
+ iconLabel.setBorder(iconMarginBorder);
}
+
+ // Add the JLabels to the JPanel
+ panel.add(iconLabel, BorderLayout.WEST);
+ panel.add(textLabel, BorderLayout.CENTER);
+
+ // Set the background and foreground colors of the text JLabel
+ if (sel) {
+ textLabel.setOpaque(true);
+ textLabel.setBackground(GUIUtil.getUITheme().getTextSelectionBackgroundColor());
+ textLabel.setForeground(GUIUtil.getUITheme().getTextSelectionForegroundColor());
+ } else {
+ textLabel.setOpaque(true); // Set this to true to allow the background color to be visible
+ textLabel.setBackground(GUIUtil.getUITheme().getComponentTreeBackgroundColor());
+ textLabel.setForeground(GUIUtil.getUITheme().getComponentTreeForegroundColor());
+ }
+
+ comp = panel;
+
+ // Add mass/CG/CD overridden icons
if (c.isMassOverridden() || c.getMassOverriddenBy() != null ||
c.isCGOverridden() || c.getCGOverriddenBy() != null ||
c.isCDOverridden() || c.getCDOverriddenBy() != null) {
JPanel p = new JPanel();
p.setLayout(new FlowLayout(FlowLayout.LEFT, 1, 1));
- p.setBackground(UIManager.getColor("Tree.textBackground"));
- p.setForeground(UIManager.getColor("Tree.textForeground"));
+ p.setBackground(GUIUtil.getUITheme().getComponentTreeBackgroundColor());
+ p.setForeground(GUIUtil.getUITheme().getComponentTreeForegroundColor());
p.add(comp/* , BorderLayout.WEST */);
if (c.getMassOverriddenBy() != null) {
- p.add(new JLabel(Icons.MASS_OVERRIDE_SUBCOMPONENT));
+ p.add(new JLabel(GUIUtil.getUITheme().getMassOverrideSubcomponentIcon()));
} else if (c.isMassOverridden()) {
- p.add(new JLabel(Icons.MASS_OVERRIDE));
+ p.add(new JLabel(GUIUtil.getUITheme().getMassOverrideIcon()));
}
if (c.getCGOverriddenBy() != null) {
- p.add(new JLabel(Icons.CG_OVERRIDE_SUBCOMPONENT));
+ p.add(new JLabel(GUIUtil.getUITheme().getCGOverrideSubcomponentIcon()));
} else if (c.isCGOverridden()) {
- p.add(new JLabel(Icons.CG_OVERRIDE));
+ p.add(new JLabel(GUIUtil.getUITheme().getCGOverrideIcon()));
}
if (c.getCDOverriddenBy() != null) {
- p.add(new JLabel(Icons.CD_OVERRIDE_SUBCOMPONENT));
+ p.add(new JLabel(GUIUtil.getUITheme().getCDOverrideSubcomponentIcon()));
} else if (c.isCDOverridden()) {
- p.add(new JLabel(Icons.CD_OVERRIDE));
+ p.add(new JLabel(GUIUtil.getUITheme().getCDOverrideIcon()));
}
// Make sure the tooltip also works on the override icons
diff --git a/swing/src/net/sf/openrocket/gui/main/flightconfigpanel/FlightConfigurablePanel.java b/swing/src/net/sf/openrocket/gui/main/flightconfigpanel/FlightConfigurablePanel.java
index 5baec6802..ee9e53dec 100644
--- a/swing/src/net/sf/openrocket/gui/main/flightconfigpanel/FlightConfigurablePanel.java
+++ b/swing/src/net/sf/openrocket/gui/main/flightconfigpanel/FlightConfigurablePanel.java
@@ -23,6 +23,7 @@ import javax.swing.table.AbstractTableModel;
import javax.swing.table.DefaultTableCellRenderer;
import net.sf.openrocket.gui.main.FlightConfigurationPanel;
+import net.sf.openrocket.gui.util.SwingPreferences;
import net.sf.openrocket.util.ArrayList;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -44,6 +45,8 @@ import net.sf.openrocket.util.Pair;
public abstract class FlightConfigurablePanel extends JPanel implements ComponentChangeListener {
protected static final Translator trans = Application.getTranslator();
+ protected static final SwingPreferences prefs = (SwingPreferences) Application.getPreferences();
+
private static final Logger log = LoggerFactory.getLogger(FlightConfigurablePanel.class);
protected RocketDescriptor descriptor = Application.getInjector().getInstance(RocketDescriptor.class);
@@ -387,12 +390,12 @@ public abstract class FlightConfigurablePanel
*/
public class SimulationPlotDialog extends JDialog {
-
private static final Translator trans = Application.getTranslator();
-
+
private SimulationPlotDialog(Window parent, Simulation simulation, PlotConfiguration config) {
//// Flight data plot
super(parent, simulation.getName());
@@ -77,7 +73,7 @@ public class SimulationPlotDialog extends JDialog {
// Add warning if X axis type is not time
if (config.getDomainAxisType() != FlightDataType.TYPE_TIME) {
JLabel msg = new StyledLabel(trans.get("PlotDialog.lbl.timeSeriesWarning"), -2);
- msg.setForeground(Color.DARK_RED.toAWTColor());
+ msg.setForeground(GUIUtil.getUITheme().getDarkWarningColor());
panel.add(msg, "wrap");
}
diff --git a/swing/src/net/sf/openrocket/gui/rocketfigure/ComponentAssemblyShapes.java b/swing/src/net/sf/openrocket/gui/rocketfigure/ComponentAssemblyShapes.java
index d3a4068f0..edc683f1c 100644
--- a/swing/src/net/sf/openrocket/gui/rocketfigure/ComponentAssemblyShapes.java
+++ b/swing/src/net/sf/openrocket/gui/rocketfigure/ComponentAssemblyShapes.java
@@ -40,11 +40,6 @@ public class ComponentAssemblyShapes extends RocketComponentShape {
Shape[] s = EmptyShapes.getShapesSideWithSelectionSquare(correctedTransform, markerRadius);
RocketComponentShape[] shapes = RocketComponentShape.toArray(s, component);
- // Set the color of the shapes
- Color color = getColor(component);
- for (int i = 0; i < shapes.length - 1; i++) {
- shapes[i].setColor(color);
- }
shapes[shapes.length - 1].setColor(Color.INVISIBLE);
return shapes;
@@ -69,11 +64,7 @@ public class ComponentAssemblyShapes extends RocketComponentShape {
Shape[] s = EmptyShapes.getShapesBackWithSelectionSquare(correctedTransform, markerRadius);
RocketComponentShape[] shapes = RocketComponentShape.toArray(s, component);
- // Set the color of the shapes
- Color color = getColor(component);
- for (int i = 0; i < shapes.length - 1; i++) {
- shapes[i].setColor(color);
- }
+ shapes[shapes.length - 1].setColor(Color.INVISIBLE);
return shapes;
}
@@ -86,14 +77,4 @@ public class ComponentAssemblyShapes extends RocketComponentShape {
private static double getDisplayRadius(RocketComponent component) {
return component.getRocket().getBoundingRadius() * 0.03;
}
-
- private static Color getColor(RocketComponent component) {
- if (component instanceof PodSet) {
- return new Color(160,160,215);
- } else if (component instanceof ParallelStage) {
- return new Color(198,163,184);
- } else {
- return new Color(160, 160, 160);
- }
- }
}
diff --git a/swing/src/net/sf/openrocket/gui/scalefigure/AbstractScaleFigure.java b/swing/src/net/sf/openrocket/gui/scalefigure/AbstractScaleFigure.java
index 25585320c..01f5120b6 100644
--- a/swing/src/net/sf/openrocket/gui/scalefigure/AbstractScaleFigure.java
+++ b/swing/src/net/sf/openrocket/gui/scalefigure/AbstractScaleFigure.java
@@ -70,7 +70,7 @@ public abstract class AbstractScaleFigure extends JPanel {
this.setPreferredSize(new Dimension(100,100));
setSize(100,100);
- setBackground(Color.WHITE);
+ setBackground(GUIUtil.getUITheme().getBackgroundColor());
setOpaque(true);
}
diff --git a/swing/src/net/sf/openrocket/gui/scalefigure/FinPointFigure.java b/swing/src/net/sf/openrocket/gui/scalefigure/FinPointFigure.java
index 83d5f9600..cf5662574 100644
--- a/swing/src/net/sf/openrocket/gui/scalefigure/FinPointFigure.java
+++ b/swing/src/net/sf/openrocket/gui/scalefigure/FinPointFigure.java
@@ -1,7 +1,6 @@
package net.sf.openrocket.gui.scalefigure;
import java.awt.BasicStroke;
-import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
@@ -15,6 +14,7 @@ import java.awt.geom.Rectangle2D;
import java.util.LinkedList;
import java.util.List;
+import net.sf.openrocket.gui.util.GUIUtil;
import net.sf.openrocket.rocketcomponent.FreeformFinSet;
import net.sf.openrocket.rocketcomponent.RocketComponent;
import net.sf.openrocket.rocketcomponent.SymmetricComponent;
@@ -31,11 +31,7 @@ import net.sf.openrocket.util.StateChangeListener;
@SuppressWarnings("serial")
public class FinPointFigure extends AbstractScaleFigure {
-
- //private final static Logger log = LoggerFactory.getLogger(FinPointFigure.class);
- private static final Color GRID_MAJOR_LINE_COLOR = new Color( 0, 0, 255, 80);
- private static final Color GRID_MINOR_LINE_COLOR = new Color( 0, 0, 255, 30);
private static final int GRID_LINE_BASE_WIDTH_PIXELS = 1;
private static final int LINE_WIDTH_FIN_PIXELS = 1;
@@ -45,8 +41,6 @@ public class FinPointFigure extends AbstractScaleFigure {
private static final int LINE_WIDTH_BOX_PIXELS = 1;
private static final float BOX_WIDTH_PIXELS = 12;
private static final float SELECTED_BOX_WIDTH_PIXELS = BOX_WIDTH_PIXELS + 4;
- private static final Color POINT_COLOR = new Color(200, 0, 0, 255);
- private static final Color SELECTED_POINT_COLOR = new Color(200, 0, 0, 255);
private static final double MINOR_TICKS = 10.0;
private static final double MAJOR_TICKS = 100.0;
@@ -65,9 +59,8 @@ public class FinPointFigure extends AbstractScaleFigure {
public FinPointFigure(FreeformFinSet finset) {
this.finset = finset;
-
- // useful for debugging -- shows a contrast against un-drawn space.
- setBackground(Color.WHITE);
+
+ setBackground(GUIUtil.getUITheme().getBackgroundColor());
setOpaque(true);
updateFigure();
@@ -129,11 +122,11 @@ public class FinPointFigure extends AbstractScaleFigure {
Line2D.Double line = new Line2D.Double();
for (Tick t : verticalTicks) {
if (t.major) {
- g2.setColor(FinPointFigure.GRID_MAJOR_LINE_COLOR);
+ g2.setColor(GUIUtil.getUITheme().getFinPointGridMajorLineColor());
line.setLine( t.value, y0, t.value, y1);
g2.draw(line);
}else{
- g2.setColor(FinPointFigure.GRID_MINOR_LINE_COLOR);
+ g2.setColor(GUIUtil.getUITheme().getFinPointGridMinorLineColor());
line.setLine( t.value, y0, t.value, y1);
g2.draw(line);
}
@@ -143,11 +136,11 @@ public class FinPointFigure extends AbstractScaleFigure {
Tick[] horizontalTicks = unit.getTicks(y0, y1, MINOR_TICKS / this.scale, MAJOR_TICKS / this.scale);
for (Tick t : horizontalTicks) {
if (t.major) {
- g2.setColor(FinPointFigure.GRID_MAJOR_LINE_COLOR);
+ g2.setColor(GUIUtil.getUITheme().getFinPointGridMajorLineColor());
line.setLine( x0, t.value, x1, t.value);
g2.draw(line);
}else{
- g2.setColor(FinPointFigure.GRID_MINOR_LINE_COLOR);
+ g2.setColor(GUIUtil.getUITheme().getFinPointGridMinorLineColor());
line.setLine( x0, t.value, x1, t.value);
g2.draw(line);
}
@@ -170,7 +163,7 @@ public class FinPointFigure extends AbstractScaleFigure {
// setup lines
final float bodyLineWidth = (float) ( LINE_WIDTH_BODY_PIXELS / scale );
g2.setStroke(new BasicStroke( bodyLineWidth, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL));
- g2.setColor(Color.BLACK);
+ g2.setColor(GUIUtil.getUITheme().getFinPointBodyLineColor());
Transition body = (Transition) finset.getParent();
final float xResolution_m = 0.01f; // distance between draw points, in meters
@@ -220,7 +213,7 @@ public class FinPointFigure extends AbstractScaleFigure {
final float bodyLineWidth = (float) ( LINE_WIDTH_BODY_PIXELS / scale );
g2.setStroke(new BasicStroke( bodyLineWidth, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL));
- g2.setColor(Color.BLACK);
+ g2.setColor(GUIUtil.getUITheme().getFinPointBodyLineColor());
g2.draw(shape);
}
@@ -237,7 +230,7 @@ public class FinPointFigure extends AbstractScaleFigure {
final float finEdgeWidth_m = (float) (LINE_WIDTH_FIN_PIXELS / scale );
g2.setStroke(new BasicStroke( finEdgeWidth_m, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL));
- g2.setColor(Color.BLACK);
+ g2.setColor(GUIUtil.getUITheme().getFinPointBodyLineColor());
g2.draw(shape);
}
@@ -251,7 +244,7 @@ public class FinPointFigure extends AbstractScaleFigure {
final float boxEdgeWidth_m = (float) ( LINE_WIDTH_BOX_PIXELS / scale );
g2.setStroke(new BasicStroke( boxEdgeWidth_m, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL));
- g2.setColor(POINT_COLOR);
+ g2.setColor(GUIUtil.getUITheme().getFinPointPointColor());
finPointHandles = new Rectangle2D.Double[ drawPoints.length];
for (int currentIndex = 0; currentIndex < drawPoints.length; currentIndex++) {
@@ -264,11 +257,11 @@ public class FinPointFigure extends AbstractScaleFigure {
final Rectangle2D.Double selectedPointHighlight = new Rectangle2D.Double(c.x - selBoxHalfWidth, c.y - selBoxHalfWidth, selBoxWidth, selBoxWidth);
// switch to the highlight color
- g2.setColor(SELECTED_POINT_COLOR);
+ g2.setColor(GUIUtil.getUITheme().getFinPointSelectedPointColor());
g2.draw(selectedPointHighlight);
// reset to the normal color
- g2.setColor(POINT_COLOR);
+ g2.setColor(GUIUtil.getUITheme().getFinPointPointColor());
}
// normal boxes
diff --git a/swing/src/net/sf/openrocket/gui/scalefigure/RocketFigure.java b/swing/src/net/sf/openrocket/gui/scalefigure/RocketFigure.java
index df1ebb08d..22f4d8d67 100644
--- a/swing/src/net/sf/openrocket/gui/scalefigure/RocketFigure.java
+++ b/swing/src/net/sf/openrocket/gui/scalefigure/RocketFigure.java
@@ -17,6 +17,7 @@ import java.awt.geom.Rectangle2D;
import java.util.*;
import java.util.Map.Entry;
+import net.sf.openrocket.gui.util.GUIUtil;
import net.sf.openrocket.rocketcomponent.AxialStage;
import net.sf.openrocket.rocketcomponent.ParallelStage;
import net.sf.openrocket.rocketcomponent.PodSet;
@@ -257,7 +258,7 @@ public class RocketFigure extends AbstractScaleFigure {
// Set component color and line style
net.sf.openrocket.util.Color color = rcs.color;
if (color == null) {
- color = Application.getPreferences().getDefaultColor(c.getClass());
+ color = ((SwingPreferences) Application.getPreferences()).getDefaultColor(c.getClass());
}
g2.setColor(ColorConversion.toAwtColor(color));
@@ -290,8 +291,8 @@ public class RocketFigure extends AbstractScaleFigure {
RenderingHints.VALUE_STROKE_NORMALIZE);
// Draw motors
- Color fillColor = ((SwingPreferences)Application.getPreferences()).getMotorFillColor();
- Color borderColor = ((SwingPreferences)Application.getPreferences()).getMotorBorderColor();
+ Color fillColor = GUIUtil.getUITheme().getMotorFillColor();
+ Color borderColor = GUIUtil.getUITheme().getMotorBorderColor();
FlightConfiguration config = rocket.getSelectedConfiguration();
for (MotorConfiguration curInstance : config.getActiveMotors()) {
diff --git a/swing/src/net/sf/openrocket/gui/scalefigure/RocketPanel.java b/swing/src/net/sf/openrocket/gui/scalefigure/RocketPanel.java
index bce0980f0..7cf764261 100644
--- a/swing/src/net/sf/openrocket/gui/scalefigure/RocketPanel.java
+++ b/swing/src/net/sf/openrocket/gui/scalefigure/RocketPanel.java
@@ -35,6 +35,7 @@ import net.miginfocom.swing.MigLayout;
import net.sf.openrocket.aerodynamics.AerodynamicCalculator;
import net.sf.openrocket.aerodynamics.BarrowmanCalculator;
import net.sf.openrocket.aerodynamics.FlightConditions;
+import net.sf.openrocket.gui.components.StyledLabel;
import net.sf.openrocket.logging.WarningSet;
import net.sf.openrocket.document.OpenRocketDocument;
import net.sf.openrocket.document.Simulation;
@@ -439,8 +440,7 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change
JPanel bottomRow = new JPanel(new MigLayout("fill, gapy 0, ins 0"));
//// Click to select Shift+click to select other Double-click to edit Click+drag to move
- infoMessage = new JLabel(trans.get("RocketPanel.lbl.infoMessage"));
- infoMessage.setFont(new Font("Sans Serif", Font.PLAIN, 9));
+ infoMessage = new StyledLabel(trans.get("RocketPanel.lbl.infoMessage"), -3);
bottomRow.add(infoMessage);
//// Show warnings
diff --git a/swing/src/net/sf/openrocket/gui/scalefigure/ScaleScrollPane.java b/swing/src/net/sf/openrocket/gui/scalefigure/ScaleScrollPane.java
index db7ebad82..e30bfe840 100644
--- a/swing/src/net/sf/openrocket/gui/scalefigure/ScaleScrollPane.java
+++ b/swing/src/net/sf/openrocket/gui/scalefigure/ScaleScrollPane.java
@@ -25,6 +25,7 @@ import javax.swing.event.ChangeListener;
import net.sf.openrocket.gui.adaptors.DoubleModel;
import net.sf.openrocket.gui.components.UnitSelector;
+import net.sf.openrocket.gui.util.GUIUtil;
import net.sf.openrocket.unit.Tick;
import net.sf.openrocket.unit.Unit;
import net.sf.openrocket.unit.UnitGroup;
@@ -398,7 +399,7 @@ public class ScaleScrollPane extends JScrollPane
}
// Set color & hints
- g2.setColor(Color.BLACK);
+ g2.setColor(GUIUtil.getUITheme().getTextColor());
g2.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL,
RenderingHints.VALUE_STROKE_NORMALIZE);
g2.setRenderingHint(RenderingHints.KEY_RENDERING,
diff --git a/swing/src/net/sf/openrocket/gui/scalefigure/ScaleSelector.java b/swing/src/net/sf/openrocket/gui/scalefigure/ScaleSelector.java
index c216b4f50..27de23c00 100644
--- a/swing/src/net/sf/openrocket/gui/scalefigure/ScaleSelector.java
+++ b/swing/src/net/sf/openrocket/gui/scalefigure/ScaleSelector.java
@@ -13,7 +13,7 @@ import javax.swing.JPanel;
import net.miginfocom.swing.MigLayout;
import net.sf.openrocket.gui.util.Icons;
-import net.sf.openrocket.gui.widgets.SelectColorButton;
+import net.sf.openrocket.gui.widgets.IconButton;
import net.sf.openrocket.util.StateChangeListener;
@SuppressWarnings("serial")
@@ -41,7 +41,7 @@ public class ScaleSelector {
this.scrollPane = scroll;
// Zoom out button
- zoomOutButton = new SelectColorButton(Icons.ZOOM_OUT);
+ zoomOutButton = new IconButton(Icons.ZOOM_OUT);
zoomOutButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
@@ -93,7 +93,7 @@ public class ScaleSelector {
});
// Zoom in button
- zoomInButton = new SelectColorButton(Icons.ZOOM_IN);
+ zoomInButton = new IconButton(Icons.ZOOM_IN);
zoomInButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
diff --git a/swing/src/net/sf/openrocket/gui/simulation/SimulationOptionsPanel.java b/swing/src/net/sf/openrocket/gui/simulation/SimulationOptionsPanel.java
index e105af540..82e49ff9a 100644
--- a/swing/src/net/sf/openrocket/gui/simulation/SimulationOptionsPanel.java
+++ b/swing/src/net/sf/openrocket/gui/simulation/SimulationOptionsPanel.java
@@ -207,6 +207,8 @@ class SimulationOptionsPanel extends JPanel {
currentExtensions = new JPanel(new MigLayout("fillx, gap 0 0, ins 0"));
JScrollPane scroll = new JScrollPane(currentExtensions);
+ currentExtensions.setBorder(GUIUtil.getUITheme().getBorder());
+ scroll.setForeground(GUIUtil.getUITheme().getTextColor());
// $%! scroll pane will not honor "growy"...
sub.add(scroll, "growx, growy, h 100%");
diff --git a/swing/src/net/sf/openrocket/gui/simulation/SimulationPlotPanel.java b/swing/src/net/sf/openrocket/gui/simulation/SimulationPlotPanel.java
index d926118a9..ac2f98010 100644
--- a/swing/src/net/sf/openrocket/gui/simulation/SimulationPlotPanel.java
+++ b/swing/src/net/sf/openrocket/gui/simulation/SimulationPlotPanel.java
@@ -205,7 +205,7 @@ public class SimulationPlotPanel extends JPanel {
//// The data will be plotted in time order even if the X axis type is not time.
simPlotPanelDesc = new DescriptionArea("", 2, -2f, false);
simPlotPanelDesc.setVisible(false);
- simPlotPanelDesc.setForeground(Color.DARK_RED.toAWTColor());
+ simPlotPanelDesc.setForeground(GUIUtil.getUITheme().getDarkWarningColor());
simPlotPanelDesc.setViewportBorder(BorderFactory.createEmptyBorder());
this.add(simPlotPanelDesc, "width 1px, growx 1, wrap unrel");
diff --git a/swing/src/net/sf/openrocket/gui/util/BetterListCellRenderer.java b/swing/src/net/sf/openrocket/gui/util/BetterListCellRenderer.java
index b8bbdd40f..a91da30c5 100644
--- a/swing/src/net/sf/openrocket/gui/util/BetterListCellRenderer.java
+++ b/swing/src/net/sf/openrocket/gui/util/BetterListCellRenderer.java
@@ -1,9 +1,9 @@
package net.sf.openrocket.gui.util;
+
import javax.swing.DefaultListCellRenderer;
import javax.swing.JLabel;
import javax.swing.JList;
-import java.awt.Color;
import java.awt.Component;
/**
@@ -12,6 +12,7 @@ import java.awt.Component;
* @author Sibo Van Gool
*/
public class BetterListCellRenderer extends DefaultListCellRenderer {
+
@Override
public Component getListCellRendererComponent(JList> list, Object value, int index,
boolean isSelected, boolean cellHasFocus) {
@@ -20,16 +21,16 @@ public class BetterListCellRenderer extends DefaultListCellRenderer {
// Alternating row colors
if (!isSelected) {
if (index % 2 == 0) {
- label.setBackground(Color.WHITE);
+ label.setBackground(GUIUtil.getUITheme().getRowBackgroundDarkerColor());
} else {
- label.setBackground(new Color(245, 245, 245));
+ label.setBackground(GUIUtil.getUITheme().getRowBackgroundLighterColor());
}
}
// Text color
if (isSelected) {
- label.setForeground(Color.WHITE);
+ label.setForeground(GUIUtil.getUITheme().getTextSelectionForegroundColor());
} else {
- label.setForeground(Color.BLACK);
+ label.setForeground(GUIUtil.getUITheme().getTextColor());
}
return label;
}
diff --git a/swing/src/net/sf/openrocket/gui/util/GUIUtil.java b/swing/src/net/sf/openrocket/gui/util/GUIUtil.java
index af1d3d16d..4fac6edd7 100644
--- a/swing/src/net/sf/openrocket/gui/util/GUIUtil.java
+++ b/swing/src/net/sf/openrocket/gui/util/GUIUtil.java
@@ -64,9 +64,13 @@ import javax.swing.tree.DefaultTreeSelectionModel;
import javax.swing.tree.TreeModel;
import javax.swing.tree.TreeSelectionModel;
+import com.github.weisj.darklaf.LafManager;
+import com.github.weisj.darklaf.theme.IntelliJTheme;
+import net.sf.openrocket.arch.SystemInfo;
import net.sf.openrocket.gui.Resettable;
import net.sf.openrocket.logging.Markers;
import net.sf.openrocket.startup.Application;
+import net.sf.openrocket.startup.Preferences;
import net.sf.openrocket.util.BugException;
import net.sf.openrocket.util.Invalidatable;
import net.sf.openrocket.util.MemoryManagement;
@@ -76,7 +80,7 @@ import org.slf4j.LoggerFactory;
public class GUIUtil {
private static final Logger log = LoggerFactory.getLogger(GUIUtil.class);
-
+
private static final KeyStroke ESCAPE = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0);
private static final String CLOSE_ACTION_KEY = "escape:WINDOW_CLOSING";
@@ -252,8 +256,24 @@ public class GUIUtil {
}
});
}
+
+ /**
+ * Get the current theme used for the UI.
+ * @return the current theme
+ */
+ public static UITheme.Theme getUITheme() {
+ Preferences prefs = Application.getPreferences();
+ Object theme = prefs.getUITheme();
+ if (theme instanceof UITheme.Theme) {
+ return (UITheme.Theme) theme;
+ }
+ return UITheme.Themes.LIGHT;
+ }
-
+ public static void applyLAF() {
+ UITheme.Theme theme = getUITheme();
+ theme.applyTheme();
+ }
/**
* Set the best available look-and-feel into use.
@@ -265,8 +285,13 @@ public class GUIUtil {
* other alternatives.
*/
try {
- // Set system L&F
- UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
+ // Linux systems often default to a dark mode LAF, so explicitly use light mode
+ if (SystemInfo.getPlatform() == SystemInfo.Platform.UNIX) {
+ LafManager.install(new IntelliJTheme());
+ } else {
+ // Set system L&F
+ UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
+ }
// Check whether we have an ugly L&F
LookAndFeel laf = UIManager.getLookAndFeel();
@@ -295,6 +320,15 @@ public class GUIUtil {
}
// Set the select foreground for buttons to not be black on a blue background
UIManager.put("Button.selectForeground", Color.WHITE);
+
+ // Fix some UI bugs on macOS
+ if (SystemInfo.getPlatform() == SystemInfo.Platform.MAC_OS) {
+ // Set the foreground of active tabs to black; there was a bug where you had a white background and white foreground
+ UIManager.put("TabbedPane.foreground", Color.black);
+
+ // Set the select foreground for buttons to not be black on a blue background
+ UIManager.put("ToggleButton.selectForeground", Color.WHITE);
+ }
} catch (Exception e) {
log.warn("Error setting LAF: " + e);
}
diff --git a/swing/src/net/sf/openrocket/gui/util/Icons.java b/swing/src/net/sf/openrocket/gui/util/Icons.java
index 89f827483..db8fdaa7e 100644
--- a/swing/src/net/sf/openrocket/gui/util/Icons.java
+++ b/swing/src/net/sf/openrocket/gui/util/Icons.java
@@ -94,13 +94,19 @@ public class Icons {
public static final Icon NOT_FAVORITE = loadImageIcon("pix/icons/star_silver.png", "Not favorite");
public static final Icon FAVORITE = loadImageIcon("pix/icons/star_gold.png", "Favorite");
-
- public static final Icon CG_OVERRIDE = loadImageIcon("pix/icons/cg-override.png", "CG Override");
- public static final Icon CG_OVERRIDE_SUBCOMPONENT = loadImageIcon("pix/icons/cg-override-subcomponent.png", "CG Override Subcomponent");
- public static final Icon CD_OVERRIDE = loadImageIcon("pix/icons/cd-override.png", "CD Override");
- public static final Icon CD_OVERRIDE_SUBCOMPONENT = loadImageIcon("pix/icons/cd-override-subcomponent.png", "CD Override Subcomponent");
- public static final Icon MASS_OVERRIDE = loadImageIcon("pix/icons/mass-override.png", "Mass Override");
- public static final Icon MASS_OVERRIDE_SUBCOMPONENT = loadImageIcon("pix/icons/mass-override-subcomponent.png", "Mass Override Subcomponent");
+
+ public static final Icon MASS_OVERRIDE_LIGHT = loadImageIcon("pix/icons/mass-override_light.png", "Mass Override");
+ public static final Icon MASS_OVERRIDE_DARK = loadImageIcon("pix/icons/mass-override_dark.png", "Mass Override");
+ public static final Icon MASS_OVERRIDE_SUBCOMPONENT_LIGHT = loadImageIcon("pix/icons/mass-override-subcomponent_light.png", "Mass Override Subcomponent");
+ public static final Icon MASS_OVERRIDE_SUBCOMPONENT_DARK = loadImageIcon("pix/icons/mass-override-subcomponent_dark.png", "Mass Override Subcomponent");
+ public static final Icon CG_OVERRIDE_LIGHT = loadImageIcon("pix/icons/cg-override_light.png", "CG Override");
+ public static final Icon CG_OVERRIDE_DARK = loadImageIcon("pix/icons/cg-override_dark.png", "CG Override");
+ public static final Icon CG_OVERRIDE_SUBCOMPONENT_LIGHT = loadImageIcon("pix/icons/cg-override-subcomponent_light.png", "CG Override Subcomponent");
+ public static final Icon CG_OVERRIDE_SUBCOMPONENT_DARK = loadImageIcon("pix/icons/cg-override-subcomponent_dark.png", "CG Override Subcomponent");
+ public static final Icon CD_OVERRIDE_LIGHT = loadImageIcon("pix/icons/cd-override_light.png", "CD Override");
+ public static final Icon CD_OVERRIDE_DARK = loadImageIcon("pix/icons/cd-override_dark.png", "CD Override");
+ public static final Icon CD_OVERRIDE_SUBCOMPONENT_LIGHT = loadImageIcon("pix/icons/cd-override-subcomponent_light.png", "CD Override Subcomponent");
+ public static final Icon CD_OVERRIDE_SUBCOMPONENT_DARK = loadImageIcon("pix/icons/cd-override-subcomponent_dark.png", "CD Override Subcomponent");
// MANUFACTURERS ICONS
public static final Icon RASAERO = loadImageIcon("pix/icons/RASAero_16.png", "RASAero Icon");
diff --git a/swing/src/net/sf/openrocket/gui/util/SwingPreferences.java b/swing/src/net/sf/openrocket/gui/util/SwingPreferences.java
index 7e9db6af1..004895c02 100644
--- a/swing/src/net/sf/openrocket/gui/util/SwingPreferences.java
+++ b/swing/src/net/sf/openrocket/gui/util/SwingPreferences.java
@@ -12,6 +12,7 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
+import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
@@ -20,6 +21,17 @@ import java.util.prefs.BackingStoreException;
import java.util.prefs.Preferences;
import net.sf.openrocket.communication.AssetHandler.UpdatePlatform;
+import net.sf.openrocket.rocketcomponent.BodyComponent;
+import net.sf.openrocket.rocketcomponent.FinSet;
+import net.sf.openrocket.rocketcomponent.InternalComponent;
+import net.sf.openrocket.rocketcomponent.LaunchLug;
+import net.sf.openrocket.rocketcomponent.MassObject;
+import net.sf.openrocket.rocketcomponent.ParallelStage;
+import net.sf.openrocket.rocketcomponent.PodSet;
+import net.sf.openrocket.rocketcomponent.RailButton;
+import net.sf.openrocket.rocketcomponent.RecoveryDevice;
+import net.sf.openrocket.rocketcomponent.RocketComponent;
+import net.sf.openrocket.rocketcomponent.TubeFinSet;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -46,6 +58,7 @@ public class SwingPreferences extends net.sf.openrocket.startup.Preferences {
public static final String NODE_WINDOWS = "windows";
public static final String NODE_TABLES = "tables";
+ private static final String UI_FONT_SIZE = "UIFontSize";
public static final String UPDATE_PLATFORM = "UpdatePlatform";
private static final List SUPPORTED_LOCALES;
@@ -58,6 +71,8 @@ public class SwingPreferences extends net.sf.openrocket.startup.Preferences {
list.add(new Locale("uk", "UA"));
SUPPORTED_LOCALES = Collections.unmodifiableList(list);
}
+
+ private final HashMap, String> DEFAULT_COLORS = new HashMap<>();
/**
@@ -94,8 +109,22 @@ public class SwingPreferences extends net.sf.openrocket.startup.Preferences {
}
}
PREFNODE = root.node(NODENAME);
+ fillDefaultComponentColors();
}
-
+
+ private void fillDefaultComponentColors() {
+ DEFAULT_COLORS.put(BodyComponent.class, getUIThemeAsTheme().getDefaultBodyComponentColor());
+ DEFAULT_COLORS.put(TubeFinSet.class, getUIThemeAsTheme().getDefaultTubeFinSetColor());
+ DEFAULT_COLORS.put(FinSet.class, getUIThemeAsTheme().getDefaultFinSetColor());
+ DEFAULT_COLORS.put(LaunchLug.class, getUIThemeAsTheme().getDefaultLaunchLugColor());
+ DEFAULT_COLORS.put(RailButton.class, getUIThemeAsTheme().getDefaultRailButtonColor());
+ DEFAULT_COLORS.put(InternalComponent.class, getUIThemeAsTheme().getDefaultInternalComponentColor());
+ DEFAULT_COLORS.put(MassObject.class, getUIThemeAsTheme().getDefaultMassObjectColor());
+ DEFAULT_COLORS.put(RecoveryDevice.class, getUIThemeAsTheme().getDefaultRecoveryDeviceColor());
+ DEFAULT_COLORS.put(PodSet.class, getUIThemeAsTheme().getDefaultPodSetColor());
+ DEFAULT_COLORS.put(ParallelStage.class, getUIThemeAsTheme().getDefaultParallelStageColor());
+ }
+
public String getNodename() {
return NODENAME;
}
@@ -297,6 +326,82 @@ public class SwingPreferences extends net.sf.openrocket.startup.Preferences {
public static List getSupportedLocales() {
return SUPPORTED_LOCALES;
}
+
+ /**
+ * Get the current theme used for the UI.
+ * @return the current theme
+ */
+ @Override
+ public Object getUITheme() {
+ return getUIThemeAsTheme();
+ }
+
+ private UITheme.Theme getUIThemeAsTheme() {
+ String themeName = getString(net.sf.openrocket.startup.Preferences.UI_THEME, UITheme.Themes.LIGHT.name());
+ if (themeName == null) return UITheme.Themes.LIGHT; // Default theme
+ return UITheme.Themes.valueOf(themeName);
+ }
+
+ /**
+ * Set the theme used for the UI.
+ * @param theme the theme to set
+ */
+ @Override
+ public void setUITheme(Object theme) {
+ if (!(theme instanceof UITheme.Theme)) return;
+ putString(net.sf.openrocket.startup.Preferences.UI_THEME, ((UITheme.Theme) theme).name());
+ storeVersion();
+ }
+
+ /**
+ * Get the current font size used for the UI.
+ * @return the current font size
+ */
+ public int getUIFontSize() {
+ return getInt(UI_FONT_SIZE, getDefaultFontSize());
+ }
+
+ public final float getRocketInfoFontSize() {
+ return (float) ((getUIFontSize() - 2) + 3 * Application.getPreferences().getChoice(net.sf.openrocket.startup.Preferences.ROCKET_INFO_FONT_SIZE, 2, 0));
+ }
+
+ private static int getDefaultFontSize() {
+ javax.swing.UIDefaults uiDefaults = javax.swing.UIManager.getDefaults();
+ Object value = uiDefaults.get("defaultFont");
+ if (value instanceof javax.swing.plaf.FontUIResource fontUIResource) {
+ return fontUIResource.getSize();
+ } else {
+ return 12;
+ }
+ }
+
+ /**
+ * Set the font size used for the UI.
+ * @param size the font size to set
+ */
+ public void setUIFontSize(int size) {
+ putInt(UI_FONT_SIZE, size);
+ storeVersion();
+ }
+
+ public net.sf.openrocket.util.Color getDefaultColor(Class extends RocketComponent> c) {
+ String color = get("componentColors", c, DEFAULT_COLORS);
+ if (color == null)
+ return net.sf.openrocket.util.Color.fromAWTColor(getUIThemeAsTheme().getTextColor());
+
+ net.sf.openrocket.util.Color clr = parseColor(color);
+ if (clr != null) {
+ return clr;
+ } else {
+ return net.sf.openrocket.util.Color.fromAWTColor(getUIThemeAsTheme().getTextColor());
+ }
+ }
+
+ public final void setDefaultColor(Class extends RocketComponent> c, net.sf.openrocket.util.Color color) {
+ if (color == null)
+ return;
+ putString("componentColors", c.getSimpleName(), stringifyColor(color));
+ }
public File getDefaultDirectory() {
String file = getString(net.sf.openrocket.startup.Preferences.DEFAULT_DIRECTORY, null);
@@ -432,18 +537,6 @@ public class SwingPreferences extends net.sf.openrocket.startup.Preferences {
putString(USER_THRUST_CURVES_KEY, str);
}
- public Color getMotorBorderColor() {
- // TODO: MEDIUM: Motor color (settable?)
- return new Color(0, 0, 0, 200);
- }
-
-
- public Color getMotorFillColor() {
- // TODO: MEDIUM: Motor fill color (settable?)
- return new Color(0, 0, 0, 100);
- }
-
-
public static int getMaxThreadCount() {
return Runtime.getRuntime().availableProcessors();
}
diff --git a/swing/src/net/sf/openrocket/gui/util/UITheme.java b/swing/src/net/sf/openrocket/gui/util/UITheme.java
new file mode 100644
index 000000000..43805d870
--- /dev/null
+++ b/swing/src/net/sf/openrocket/gui/util/UITheme.java
@@ -0,0 +1,572 @@
+package net.sf.openrocket.gui.util;
+
+import com.github.weisj.darklaf.LafManager;
+import com.github.weisj.darklaf.theme.DarculaTheme;
+import net.sf.openrocket.l10n.Translator;
+import net.sf.openrocket.startup.Application;
+import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.swing.BorderFactory;
+import javax.swing.Icon;
+import javax.swing.UIManager;
+import javax.swing.border.Border;
+import java.awt.Color;
+import java.awt.Font;
+import java.io.IOException;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Map;
+
+public class UITheme {
+ private static final Translator trans = Application.getTranslator();
+ private static final Logger log = LoggerFactory.getLogger(UITheme.class);
+
+ public interface Theme {
+ void applyTheme();
+ String name(); // Provided by enum, gives the name of the enum constant
+ String getDisplayName();
+ Color getBackgroundColor();
+ Color getBorderColor();
+ Color getTextColor();
+ Color getDimTextColor();
+ Color getTextSelectionForegroundColor();
+ Color getTextSelectionBackgroundColor();
+ Color getWarningColor();
+ Color getDarkWarningColor();
+ Color getRowBackgroundLighterColor();
+ Color getRowBackgroundDarkerColor();
+ Color getFlightDataTextActiveColor();
+ Color getFlightDataTextInactiveColor();
+
+ // Component colors
+ String getDefaultBodyComponentColor();
+ String getDefaultTubeFinSetColor();
+ String getDefaultFinSetColor();
+ String getDefaultLaunchLugColor();
+ String getDefaultRailButtonColor();
+ String getDefaultInternalComponentColor();
+ String getDefaultMassObjectColor();
+ String getDefaultRecoveryDeviceColor();
+ String getDefaultPodSetColor();
+ String getDefaultParallelStageColor();
+
+ Color getMotorBorderColor();
+ Color getMotorFillColor();
+
+ Color getCGColor();
+ Color getCPColor();
+
+ Color getURLColor();
+
+ Color getComponentTreeBackgroundColor();
+ Color getComponentTreeForegroundColor();
+
+ Color getFinPointGridMajorLineColor();
+ Color getFinPointGridMinorLineColor();
+ Color getFinPointPointColor();
+ Color getFinPointSelectedPointColor();
+ Color getFinPointBodyLineColor();
+
+ Icon getMassOverrideIcon();
+ Icon getMassOverrideSubcomponentIcon();
+ Icon getCGOverrideIcon();
+ Icon getCGOverrideSubcomponentIcon();
+ Icon getCDOverrideIcon();
+ Icon getCDOverrideSubcomponentIcon();
+
+ Border getBorder();
+
+ void formatScriptTextArea(RSyntaxTextArea textArea);
+ }
+
+ public enum Themes implements Theme {
+ LIGHT {
+ private final String displayName = trans.get("UITheme.Light");
+
+ @Override
+ public void applyTheme() {
+ final SwingPreferences prefs = (SwingPreferences) Application.getPreferences();
+
+ GUIUtil.setBestLAF();
+ setGlobalFontSize(prefs.getUIFontSize());
+ }
+
+ @Override
+ public String getDisplayName() {
+ return displayName;
+ }
+
+ @Override
+ public Color getBackgroundColor() {
+ return Color.WHITE;
+ }
+
+ @Override
+ public Color getBorderColor() {
+ return Color.BLACK;
+ }
+
+ @Override
+ public Color getTextColor() {
+ return Color.BLACK;
+ }
+
+ @Override
+ public Color getDimTextColor() {
+ return Color.GRAY;
+ }
+
+ @Override
+ public Color getTextSelectionForegroundColor() {
+ return UIManager.getColor("Tree.selectionForeground");
+ }
+
+ @Override
+ public Color getTextSelectionBackgroundColor() {
+ return UIManager.getColor("Tree.selectionBackground");
+ }
+
+ @Override
+ public Color getWarningColor() {
+ return Color.RED;
+ }
+
+ @Override
+ public Color getDarkWarningColor() {
+ return new Color(200,0,0);
+ }
+
+ @Override
+ public Color getRowBackgroundLighterColor() {
+ return Color.WHITE;
+ }
+
+ @Override
+ public Color getRowBackgroundDarkerColor() {
+ return new Color(245, 245, 245);
+ }
+
+ @Override
+ public Color getFlightDataTextActiveColor() {
+ return new Color(0,0,127);
+ }
+
+ @Override
+ public Color getFlightDataTextInactiveColor() {
+ return new Color(0,0,127,127);
+ }
+
+ @Override
+ public String getDefaultBodyComponentColor() {
+ return "0,0,240";
+ }
+ @Override
+ public String getDefaultTubeFinSetColor() {
+ return "0,0,200";
+ }
+ @Override
+ public String getDefaultFinSetColor() {
+ return "0,0,200";
+ }
+ @Override
+ public String getDefaultLaunchLugColor() {
+ return "0,0,180";
+ }
+ @Override
+ public String getDefaultRailButtonColor() {
+ return "0,0,180";
+ }
+ @Override
+ public String getDefaultInternalComponentColor() {
+ return "170,0,100";
+ }
+ @Override
+ public String getDefaultMassObjectColor() {
+ return "0,0,0";
+ }
+ @Override
+ public String getDefaultRecoveryDeviceColor() {
+ return "255,0,0";
+ }
+ @Override
+ public String getDefaultPodSetColor() {
+ return "160,160,215";
+ }
+ @Override
+ public String getDefaultParallelStageColor() {
+ return "198,163,184";
+ }
+
+ @Override
+ public Color getMotorBorderColor() {
+ return new Color(0, 0, 0, 200);
+ }
+
+ @Override
+ public Color getMotorFillColor() {
+ return new Color(0, 0, 0, 100);
+ }
+
+ @Override
+ public Color getCGColor() {
+ return Color.BLUE;
+ }
+
+ @Override
+ public Color getCPColor() {
+ return Color.RED;
+ }
+
+ @Override
+ public Color getURLColor() {
+ return Color.BLUE;
+ }
+
+ @Override
+ public Color getComponentTreeBackgroundColor() {
+ return UIManager.getColor("Tree.textBackground");
+ }
+
+ @Override
+ public Color getComponentTreeForegroundColor() {
+ return UIManager.getColor("Tree.textForeground");
+ }
+
+ @Override
+ public Color getFinPointGridMajorLineColor() {
+ return new Color( 0, 0, 255, 80);
+ }
+
+ @Override
+ public Color getFinPointGridMinorLineColor() {
+ return new Color( 0, 0, 255, 30);
+ }
+
+ @Override
+ public Color getFinPointPointColor() {
+ return new Color(200, 0, 0, 255);
+ }
+
+ @Override
+ public Color getFinPointSelectedPointColor() {
+ return new Color(200, 0, 0, 255);
+ }
+
+ @Override
+ public Color getFinPointBodyLineColor() {
+ return Color.BLACK;
+ }
+
+ @Override
+ public Icon getMassOverrideIcon() {
+ return Icons.MASS_OVERRIDE_LIGHT;
+ }
+
+ @Override
+ public Icon getMassOverrideSubcomponentIcon() {
+ return Icons.MASS_OVERRIDE_SUBCOMPONENT_LIGHT;
+ }
+
+ @Override
+ public Icon getCGOverrideIcon() {
+ return Icons.CG_OVERRIDE_LIGHT;
+ }
+
+ @Override
+ public Icon getCGOverrideSubcomponentIcon() {
+ return Icons.CG_OVERRIDE_SUBCOMPONENT_LIGHT;
+ }
+
+ @Override
+ public Icon getCDOverrideIcon() {
+ return Icons.CD_OVERRIDE_LIGHT;
+ }
+
+ @Override
+ public Icon getCDOverrideSubcomponentIcon() {
+ return Icons.CD_OVERRIDE_SUBCOMPONENT_LIGHT;
+ }
+
+ @Override
+ public Border getBorder() {
+ return null;
+ }
+
+ @Override
+ public void formatScriptTextArea(RSyntaxTextArea textArea) {
+ try {
+ org.fife.ui.rsyntaxtextarea.Theme theme = org.fife.ui.rsyntaxtextarea.Theme.load(getClass().getResourceAsStream(
+ "/org/fife/ui/rsyntaxtextarea/themes/default.xml"));
+ theme.apply(textArea);
+ textArea.setCurrentLineHighlightColor(new Color(255, 255, 230));
+ } catch (IOException ioe) {
+ log.warn("Unable to load RSyntaxTextArea theme", ioe);
+ }
+ }
+ },
+ DARK {
+ private final String displayName = trans.get("UITheme.Dark");
+ @Override
+ public void applyTheme() {
+ final SwingPreferences prefs = (SwingPreferences) Application.getPreferences();
+
+ LafManager.install(new DarculaTheme());
+ setGlobalFontSize(prefs.getUIFontSize());
+ }
+
+ @Override
+ public String getDisplayName() {
+ return displayName;
+ }
+
+ @Override
+ public Color getBackgroundColor() {
+ return new Color(73, 76, 79);
+ }
+
+ @Override
+ public Color getBorderColor() {
+ return new Color(97, 99, 101);
+ }
+
+ @Override
+ public Color getTextColor() {
+ return UIManager.getColor("Label.foreground");
+ }
+
+ @Override
+ public Color getDimTextColor() {
+ return new Color(162, 162, 162);
+ }
+
+ @Override
+ public Color getTextSelectionForegroundColor() {
+ return Color.WHITE;
+ }
+
+ @Override
+ public Color getTextSelectionBackgroundColor() {
+ return new Color(75, 110, 175);
+ }
+
+ @Override
+ public Color getWarningColor() {
+ return new Color(246, 143, 143);
+ }
+
+ @Override
+ public Color getDarkWarningColor() {
+ return new Color(229, 103, 103);
+ }
+
+ @Override
+ public Color getRowBackgroundLighterColor() {
+ return new Color(65, 69, 71);
+ }
+
+ @Override
+ public Color getRowBackgroundDarkerColor() {
+ return new Color(60, 63, 65);
+ }
+
+ @Override
+ public Color getFlightDataTextActiveColor() {
+ return new Color(145, 183, 231);
+ }
+
+ @Override
+ public Color getFlightDataTextInactiveColor() {
+ return new Color(128, 166, 230, 127);
+ }
+
+ @Override
+ public String getDefaultBodyComponentColor() {
+ return "150,162,255";
+ }
+ @Override
+ public String getDefaultTubeFinSetColor() {
+ return "150,178,255";
+ }
+ @Override
+ public String getDefaultFinSetColor() {
+ return "150,178,255";
+ }
+ @Override
+ public String getDefaultLaunchLugColor() {
+ return "142,153,238";
+ }
+ @Override
+ public String getDefaultRailButtonColor() {
+ return "142,153,238";
+ }
+ @Override
+ public String getDefaultInternalComponentColor() {
+ return "181,128,151";
+ }
+ @Override
+ public String getDefaultMassObjectColor() {
+ return "210,210,210";
+ }
+ @Override
+ public String getDefaultRecoveryDeviceColor() {
+ return "220,90,90";
+ }
+ @Override
+ public String getDefaultPodSetColor() {
+ return "190,190,235";
+ }
+ @Override
+ public String getDefaultParallelStageColor() {
+ return "210,180,195";
+ }
+
+ @Override
+ public Color getMotorBorderColor() {
+ return new Color(0, 0, 0, 100);
+ }
+
+ @Override
+ public Color getMotorFillColor() {
+ return new Color(0, 0, 0, 50);
+ }
+
+ @Override
+ public Color getCGColor() {
+ return new Color(85, 133, 253);
+ }
+
+ @Override
+ public Color getCPColor() {
+ return new Color(255, 72, 106);
+ }
+
+ @Override
+ public Color getURLColor() {
+ return new Color(150, 167, 255);
+ }
+
+ @Override
+ public Color getComponentTreeBackgroundColor() {
+ return getBackgroundColor();
+ }
+
+ @Override
+ public Color getComponentTreeForegroundColor() {
+ return getTextColor();
+ }
+
+ @Override
+ public Color getFinPointGridMajorLineColor() {
+ return new Color(135, 135, 199, 197);
+ }
+
+ @Override
+ public Color getFinPointGridMinorLineColor() {
+ return new Color(121, 121, 189, 69);
+ }
+
+ @Override
+ public Color getFinPointPointColor() {
+ return new Color(217, 108, 108, 255);
+ }
+
+ @Override
+ public Color getFinPointSelectedPointColor() {
+ return new Color(232, 78, 78, 255);
+ }
+
+ @Override
+ public Color getFinPointBodyLineColor() {
+ return Color.WHITE;
+ }
+
+ @Override
+ public Icon getMassOverrideIcon() {
+ return Icons.MASS_OVERRIDE_DARK;
+ }
+
+ @Override
+ public Icon getMassOverrideSubcomponentIcon() {
+ return Icons.MASS_OVERRIDE_SUBCOMPONENT_DARK;
+ }
+
+ @Override
+ public Icon getCGOverrideIcon() {
+ return Icons.CG_OVERRIDE_DARK;
+ }
+
+ @Override
+ public Icon getCGOverrideSubcomponentIcon() {
+ return Icons.CG_OVERRIDE_SUBCOMPONENT_DARK;
+ }
+
+ @Override
+ public Icon getCDOverrideIcon() {
+ return Icons.CD_OVERRIDE_DARK;
+ }
+
+ @Override
+ public Icon getCDOverrideSubcomponentIcon() {
+ return Icons.CD_OVERRIDE_SUBCOMPONENT_DARK;
+ }
+
+ @Override
+ public Border getBorder() {
+ return BorderFactory.createLineBorder(getBorderColor());
+ }
+
+ @Override
+ public void formatScriptTextArea(RSyntaxTextArea textArea) {
+ try {
+ org.fife.ui.rsyntaxtextarea.Theme theme = org.fife.ui.rsyntaxtextarea.Theme.load(getClass().getResourceAsStream(
+ "/org/fife/ui/rsyntaxtextarea/themes/dark.xml"));
+ theme.apply(textArea);
+ } catch (IOException ioe) {
+ log.warn("Unable to load RSyntaxTextArea theme", ioe);
+ }
+ }
+ }
+ }
+
+ private static void setGlobalFontSize(int size) {
+ // Some fonts have different sizes for different components, so we need to adjust them
+ final Map fontOffsets = new HashMap<>();
+ fontOffsets.put("MenuBar.font", 1f);
+ fontOffsets.put("Tree.font", -1f);
+ fontOffsets.put("Slider.font", -2f);
+ fontOffsets.put("TableHeader.font", -2f);
+ fontOffsets.put("ColorChooser.font", -1f);
+ fontOffsets.put("Menu.acceleratorFont", 1f);
+ fontOffsets.put("InternalFrame.optionDialogTitleFont", 1f);
+ fontOffsets.put("InternalFrame.paletteTitleFont", 1f);
+ fontOffsets.put("MenuItem.font", 1f);
+ fontOffsets.put("PopupMenu.font", 1f);
+ fontOffsets.put("MenuItem.acceleratorFont", 1f);
+ fontOffsets.put("RadioButtonMenuItem.font", 1f);
+ fontOffsets.put("Table.font", -1f);
+ //fontOffsets.put("IconButton.font", -2f); // The default doesn't really look nice, we want the normal font size instead
+ fontOffsets.put("InternalFrame.titleFont", 1f);
+ fontOffsets.put("List.font", -1f);
+ fontOffsets.put("RadioButtonMenuItem.acceleratorFont", 1f);
+ fontOffsets.put("CheckBoxMenuItem.acceleratorFont", 1f);
+ fontOffsets.put("Menu.font", 1f);
+ fontOffsets.put("TabbedPane.smallFont", -2f);
+ fontOffsets.put("CheckBoxMenuItem.font", 1f);
+ fontOffsets.put("ToolTip.font", -2f);
+
+ // Iterate over all keys in the UIManager defaults and set the font
+ for (Enumeration