diff --git a/README.md b/README.md index 93b6598..c1aeaf1 100644 --- a/README.md +++ b/README.md @@ -9,9 +9,9 @@ Currently supported features: * Shows tree with project source files * Can open and edit source files from project or file system in multi-tab editor * Build and run project with DUB -* Build log highlight and navigation to place of error or warning by clicking on log line +* Build log highlight and navigation to place of error or warning by clicking on log line (contributed by Extrawurst) * DUB dependencies update -* DUB package configuration selection (implemented by NCrashed) +* DUB package configuration selection (contributed by NCrashed) * Dependency projects are shown in workspace tree Source editor features: diff --git a/src/ddc/lexer/tokenizer.d b/src/ddc/lexer/tokenizer.d index 34bbc27..04a6bf9 100644 --- a/src/ddc/lexer/tokenizer.d +++ b/src/ddc/lexer/tokenizer.d @@ -2596,8 +2596,16 @@ class Tokenizer for (;;) { int i = _pos; int endPos = int.max; + bool lastBackSlash = false; for(; i < _len; i++) { - if (_lineText[i] == delimiter && (i == 0 || _lineText[i - 1] != '\\')) { + dchar ch = _lineText[i]; + if (ch == '\\') { + if (lastBackSlash) + lastBackSlash = false; + else + lastBackSlash = true; + } + if (ch == delimiter && !lastBackSlash) { endPos = i; break; } diff --git a/src/ddebug/windows/mago.d b/src/ddebug/windows/mago.d index 4e5aa1b..48e3aab 100644 --- a/src/ddebug/windows/mago.d +++ b/src/ddebug/windows/mago.d @@ -9,6 +9,11 @@ import std.string; //const GUID CLSID_MAGO = {0xE348A53A, 0x470A, 0x4A70, [0x9B, 0x55, 0x1E, 0x02, 0xF3, 0x52, 0x79, 0x0D]}; const GUID IID_MAGO_NATIVE_ENGINE = {0x97348AC0, 0x2B6B, 0x4B99, [0xA2, 0x45, 0x4C, 0x7E, 0x2C, 0x09, 0xD4, 0x03]}; +//const GUID CLSID_PORT_SUPPLIER = {0x3484EFB2, 0x0A52, 0x4EB2, [0x86, 0x9C, 0x1F, 0x7E, 0x66, 0x8E, 0x1B, 0x87]}; +//const GUID CLSID_PORT_SUPPLIER = {0x3B476D38, 0xA401, 0x11D2, [0xAA, 0xD4, 0x00, 0xC0, 0x4F, 0x99, 0x01, 0x71]}; //3B476D38-A401-11D2-AAD4-00C04F990171 +const GUID CLSID_PORT_SUPPLIER = {0x708C1ECA, 0xFF48, 0x11D2, [0x90, 0x4F, 0x00, 0xC0, 0x4F, 0xA3, 0x02, 0xA1]}; //{708C1ECA-FF48-11D2-904F-00C04FA302A1} +//const GUID CLSID_PORT_SUPPLIER = {0xF561BF8D, 0xBFBA, 0x4FC6, [0xAE, 0xA7, 0x24, 0x45, 0xD0, 0xEA, 0xC1, 0xC5]}; +//const GUID CLSID_PORT_SUPPLIER = {0x708C1ECA, 0xFF48, 0x11D2, [0x90, 0x4F, 0x00, 0x04, 0xA3, 0x2, 0xA1]}; class ComObject : IUnknown { @@ -69,6 +74,46 @@ class DebugCallback : ComObject, IDebugEventCallback2 { } +string formatHResult(HRESULT hr) { + switch(hr) { + case S_OK: return "S_OK"; + case S_FALSE: return "S_FALSE"; + case E_NOTIMPL: return "E_NOTIMPL"; + case E_NOINTERFACE: return "E_NOINTERFACE"; + case E_FAIL: return "E_FAIL"; + case E_HANDLE: return "E_HANDLE"; + case 0x80040154: return "REGDB_E_CLASSNOTREG"; + default: + return format("%08x", hr); + } +} + +IDebugPortSupplier2 createPortSupplier() { + HRESULT hr; + IDebugPortSupplier2 portSupplier = null; + LPOLESTR str; + StringFromCLSID(&CLSID_PORT_SUPPLIER, &str); + hr = CoCreateInstance(&CLSID_PORT_SUPPLIER, //CLSID_MAGO, + null, + CLSCTX_INPROC, //CLSCTX_ALL, + &IID_IDebugPortSupplier2, //IID_MAGO_NATIVE_ENGINE, + cast(void**)&portSupplier); //piUnknown); + if (FAILED(hr) || !portSupplier) { + Log.e("Failed to create port supplier ", formatHResult(hr)); + return null; + } + Log.i("Port supplier is created"); + return portSupplier; +} + +class DebugPortRequest : ComObject, IDebugPortRequest2 { + static const wchar[] portName = "magoDebuggerPort\0"; + HRESULT GetPortName(/+[out]+/ BSTR* pbstrPortName) { + pbstrPortName = cast(BSTR*)portName.ptr; + return S_OK; + } +} + void testMago() { HRESULT hr; IUnknown* piUnknown; @@ -76,11 +121,30 @@ void testMago() { IDebugEngineLaunch2 debugEngineLaunch = null; hr=CoInitialize(null); // Initialize OLE if (FAILED(hr)) { - Log.e("OLE 2 failed to initialize"); + Log.e("OLE 2 failed to initialize", formatHResult(hr)); + return; + } + + IDebugPortSupplier2 portSupplier = createPortSupplier(); + if (!portSupplier) { + Log.e("Failed to create port supplier"); + return; + } + if (portSupplier.CanAddPort() != S_OK) { + Log.e("Cannot add debug port ", portSupplier.CanAddPort()); + return; + } + IDebugPort2 debugPort = null; + DebugPortRequest debugPortRequest = new DebugPortRequest(); + // Add a port + hr = portSupplier.AddPort( + /+[in]+/ debugPortRequest, + /+[out]+/ &debugPort); + if (FAILED(hr) || !debugPort) { + Log.e("Failed to create debub port ", formatHResult(hr)); return; } - IDebugCoreServer2 coreServer = null; //hr = CoCreateInstance(&CLSID_MAGO, null, CLSCTX_ALL, &IID_IDebugEngine2, cast(void**)&piUnknown); hr = CoCreateInstance(&IID_MAGO_NATIVE_ENGINE, //CLSID_MAGO, @@ -92,7 +156,7 @@ void testMago() { Log.d("Debug interface is not null"); } if (FAILED(hr) || !debugEngine) { - Log.e("Failed to create MAGO interface instance ", hr); + Log.e("Failed to create MAGO interface instance ", formatHResult(hr)); return; } @@ -103,7 +167,7 @@ void testMago() { hr = debugEngine.QueryInterface(cast(GUID*)&IID_IDebugEngineLaunch2, cast(void**)&debugEngineLaunch); if (FAILED(hr) || !debugEngineLaunch) { - Log.e("Failed to get IID_IDebugEngineLaunch2 interface ", hr); + Log.e("Failed to get IID_IDebugEngineLaunch2 interface ", formatHResult(hr)); return; } @@ -139,7 +203,7 @@ void testMago() { ); if (FAILED(hr) || !process) { - Log.e("Failed to run process ", format("%08x", hr)); + Log.e("Failed to run process ", formatHResult(hr)); return; } Log.d("LaunchSuspended executed ok"); diff --git a/src/dlangide.d b/src/dlangide.d index 1532c56..dd85e45 100644 --- a/src/dlangide.d +++ b/src/dlangide.d @@ -17,6 +17,8 @@ extern (C) int UIAppMain(string[] args) { // embed non-standard resources listed in views/resources.list into executable embeddedResourceList.addResources(embedResourcesFromList!("resources.list")()); + Platform.instance.uiTheme = "ide_theme_default"; + // you can override default hinting mode here //FontManager.hintingMode = HintingMode.Light; //FontManager.hintingMode = HintingMode.AutoHint; diff --git a/src/dlangide/ui/dsourceedit.d b/src/dlangide/ui/dsourceedit.d index 2f5e824..3813404 100644 --- a/src/dlangide/ui/dsourceedit.d +++ b/src/dlangide/ui/dsourceedit.d @@ -24,7 +24,7 @@ class DSourceEdit : SourceEdit { this(string ID) { super(ID); styleId = null; - backgroundColor = 0xFFFFFF; + backgroundColor = style.customColor("edit_background"); setTokenHightlightColor(TokenCategory.Comment, 0x008000); // green setTokenHightlightColor(TokenCategory.Keyword, 0x0000FF); // blue setTokenHightlightColor(TokenCategory.String, 0xA31515); // brown @@ -42,6 +42,12 @@ class DSourceEdit : SourceEdit { this() { this("SRCEDIT"); } + + /// handle theme change: e.g. reload some themed resources + override void onThemeChanged() { + backgroundColor = style.customColor("edit_background"); + } + protected IDESettings _settings; @property DSourceEdit settings(IDESettings s) { _settings = s; diff --git a/src/dlangide/ui/frame.d b/src/dlangide/ui/frame.d index bbef2d0..d9ce14c 100644 --- a/src/dlangide/ui/frame.d +++ b/src/dlangide/ui/frame.d @@ -13,6 +13,7 @@ import dlangui.widgets.combobox; import dlangui.widgets.popup; import dlangui.dialogs.dialog; import dlangui.dialogs.filedlg; +import dlangui.dialogs.settingsdialog; import dlangui.core.stdaction; import dlangui.core.files; @@ -112,6 +113,7 @@ class IDEFrame : AppFrame { _settings.updateDefaults(); _settings.save(); super.init(); + applySettings(_settings); } /// move focus to editor in currently selected tab @@ -181,7 +183,7 @@ class IDEFrame : AppFrame { TabItem tab = _tabs.tab(filename); tab.objectParam = file; editor.onModifiedStateChangeListener = &onModifiedStateChange; - editor.settings(settings).applySettings(); + applySettings(editor, settings); _tabs.selectTab(index, true); } else { destroy(editor); @@ -427,7 +429,7 @@ class IDEFrame : AppFrame { mainMenuItems.add(helpItem); MainMenu mainMenu = new MainMenu(mainMenuItems); - mainMenu.backgroundColor = 0xd6dbe9; + //mainMenu.backgroundColor = 0xd6dbe9; return mainMenu; } @@ -494,6 +496,8 @@ class IDEFrame : AppFrame { /// override to handle specific actions state (e.g. change enabled state for supported actions) override bool handleActionStateRequest(const Action a) { switch (a.id) { + case IDEActions.EditPreferences: + return true; case IDEActions.FileExit: case IDEActions.FileOpen: case IDEActions.WindowCloseAllDocuments: @@ -624,6 +628,9 @@ class IDEFrame : AppFrame { auto results = _editorTool.getCompletions(currentEditor, currentEditor.getCaretPosition); currentEditor.showCompletionPopup(results); return true; + case IDEActions.EditPreferences: + showPreferences(); + return true; default: return super.handleAction(a); } @@ -631,6 +638,41 @@ class IDEFrame : AppFrame { return false; } + void showPreferences() { + //Log.d("settings before copy:\n", _settings.setting.toJSON(true)); + Setting s = _settings.copySettings(); + //Log.d("settings after copy:\n", s.toJSON(true)); + SettingsDialog dlg = new SettingsDialog(UIString("DlangIDE settings"d), window, s, createSettingsPages()); + dlg.onDialogResult = delegate(Dialog dlg, const Action result) { + if (result.id == ACTION_APPLY.id) { + //Log.d("settings after edit:\n", s.toJSON(true)); + _settings.applySettings(s); + applySettings(_settings); + _settings.save(); + } + }; + dlg.show(); + } + + void applySettings(IDESettings settings) { + for (int i = _tabs.tabCount - 1; i >= 0; i--) { + DSourceEdit ed = cast(DSourceEdit)_tabs.tabBody(i); + if (ed) { + applySettings(ed, settings); + } + } + FontManager.fontGamma = settings.fontGamma; + FontManager.hintingMode = settings.hintingMode; + FontManager.minAnitialiasedFontSize = settings.minAntialiasedFontSize; + Platform.instance.uiLanguage = settings.uiLanguage; + Platform.instance.uiTheme = settings.uiTheme; + requestLayout(); + } + + void applySettings(DSourceEdit editor, IDESettings settings) { + editor.settings(settings).applySettings(); + } + private bool loadProject(Project project) { if (!project.load()) { _logPanel.logLine("Cannot read project " ~ project.filename); diff --git a/src/dlangide/ui/homescreen.d b/src/dlangide/ui/homescreen.d index deaf2f5..ed09043 100644 --- a/src/dlangide/ui/homescreen.d +++ b/src/dlangide/ui/homescreen.d @@ -14,8 +14,9 @@ class HomeScreen : ScrollWidget { protected VerticalLayout _recentItems; this(string ID, IDEFrame frame) { super(ID); - backgroundColor = 0xFFFFFF; + styleId = STYLE_EDIT_BOX; _frame = frame; + uint linkColor = currentTheme.customColor("link_color", 0x2020FF); _content = new HorizontalLayout("HOME_SCREEN_BODY"); _content.layoutWidth(FILL_PARENT).layoutHeight(FILL_PARENT); VerticalLayout _column1 = new VerticalLayout(); @@ -24,11 +25,11 @@ class HomeScreen : ScrollWidget { _column2.layoutWidth(FILL_PARENT).layoutHeight(FILL_PARENT).padding(Rect(20, 20, 20, 20)); _content.addChild(_column1); _content.addChild(_column2); - _column1.addChild((new TextWidget(null, "Dlang IDE"d)).fontSize(32).textColor(0x000080)); + _column1.addChild((new TextWidget(null, "Dlang IDE"d)).fontSize(32).textColor(linkColor)); _column1.addChild((new TextWidget(null, "D language IDE written in D"d)).fontSize(20)); - _column1.addChild((new TextWidget(null, "(c) Vadim Lopatin 2015"d)).fontSize(22).textColor(0x000080)); + _column1.addChild((new TextWidget(null, "(c) Vadim Lopatin 2015"d)).fontSize(22).textColor(linkColor)); _column1.addChild(new VSpacer()); - _column1.addChild((new TextWidget(null, "Start with:"d)).fontSize(20).textColor(0x000040)); + _column1.addChild((new TextWidget(null, "Start with:"d)).fontSize(20).textColor(linkColor)); _startItems = new VerticalLayout(); _recentItems = new VerticalLayout(); _startItems.addChild(new ImageTextButton(ACTION_FILE_OPEN_WORKSPACE)); @@ -36,11 +37,11 @@ class HomeScreen : ScrollWidget { _startItems.addChild(new ImageTextButton(ACTION_FILE_NEW_PROJECT)); _column1.addChild(_startItems); _column1.addChild(new VSpacer()); - _column1.addChild((new TextWidget(null, "Recent:"d)).fontSize(20).textColor(0x000040)); + _column1.addChild((new TextWidget(null, "Recent:"d)).fontSize(20).textColor(linkColor)); _recentItems.addChild((new TextWidget(null, "No recent items"d))); _column1.addChild(_recentItems); _column1.addChild(new VSpacer()); - _column2.addChild((new TextWidget(null, "Useful Links:"d)).fontSize(20).textColor(0x000040)); + _column2.addChild((new TextWidget(null, "Useful Links:"d)).fontSize(20).textColor(linkColor)); _column2.addChild(new UrlImageTextButton(null, "D Programming Language"d, "http://dlang.org/")); _column2.addChild(new UrlImageTextButton(null, "DUB repository"d, "http://code.dlang.org/")); _column2.addChild(new UrlImageTextButton(null, "DLangUI on GitHub"d, "https://github.com/buggins/dlangui")); diff --git a/src/dlangide/ui/settings.d b/src/dlangide/ui/settings.d index 0be6ba1..5c51ffe 100644 --- a/src/dlangide/ui/settings.d +++ b/src/dlangide/ui/settings.d @@ -1,6 +1,10 @@ module dlangide.ui.settings; import dlangui.core.settings; +import dlangui.core.i18n; +import dlangui.graphics.fonts; +import dlangui.widgets.lists; +import dlangui.dialogs.settingsdialog; const AVAILABLE_THEMES = ["theme_default", "theme_dark"]; @@ -21,6 +25,9 @@ class IDESettings : SettingsFile { Setting ui = uiSettings(); ui.setStringDef("theme", "theme_default"); ui.setStringDef("language", "en"); + ui.setIntegerDef("hintingMode", 1); + ui.setIntegerDef("minAntialiasedFontSize", 0); + ui.setFloatingDef("fontGamma", 0.8); } /// override to do something after loading - e.g. set defaults @@ -102,4 +109,75 @@ class IDESettings : SettingsFile { @property bool smartIndentsAfterPaste() { return editorSettings.getBoolean("smartIndentsAfterPaste", true); } /// set smart indents enabled flag @property IDESettings smartIndentsAfterPaste(bool enabled) { editorSettings.setBoolean("smartIndentsAfterPaste", enabled); return this; } + + @property double fontGamma() { + double gamma = uiSettings.getFloating("fontGamma", 1.0); + if (gamma >= 0.5 && gamma <= 2.0) + return gamma; + return 1.0; + } + + @property HintingMode hintingMode() { + long mode = uiSettings.getInteger("hintingMode", HintingMode.Normal); + if (mode >= HintingMode.Normal && mode <= HintingMode.Light) + return cast(HintingMode)mode; + return HintingMode.Normal; + } + + @property int minAntialiasedFontSize() { + long sz = uiSettings.getInteger("minAntialiasedFontSize", 0); + if (sz >= 0) + return cast(int)sz; + return 0; + } + +} + +/// create DlangIDE settings pages tree +SettingsPage createSettingsPages() { + SettingsPage res = new SettingsPage("", UIString(""d)); + SettingsPage ed = res.addChild("editors", UIString("Editors"d)); + SettingsPage texted = ed.addChild("editors/textEditor", UIString("Text Editors"d)); + texted.addNumberEdit("editors/textEditor/tabSize", UIString("Tab size"d), 1, 16, 4); + texted.addCheckbox("editors/textEditor/useSpacesForTabs", UIString("Use spaces for tabs"d)); + texted.addCheckbox("editors/textEditor/smartIndents", UIString("Smart indents"d)); + texted.addCheckbox("editors/textEditor/smartIndentsAfterPaste", UIString("Smart indent after paste"d)); + SettingsPage ui = res.addChild("interface", UIString("Interface"d)); + ui.addStringComboBox("interface/theme", UIString("Theme"d), [ + StringListValue("ide_theme_default", "Default"d), + StringListValue("ide_theme_dark", "Dark"d)]); + ui.addStringComboBox("interface/language", UIString("Language"d), [StringListValue("en", "English"d), StringListValue("ru", "Russian"d)]); + ui.addIntComboBox("interface/hintingMode", UIString("Font hinting mode"d), [StringListValue(0, "Normal"d), StringListValue(1, "Force Auto Hint"d), + StringListValue(2, "Disabled"d), StringListValue(3, "Light"d)]); + ui.addIntComboBox("interface/minAntialiasedFontSize", UIString("Minimum font size for antialiasing"d), + [StringListValue(0, "Always ON"d), + StringListValue(12, "12"d), + StringListValue(14, "14"d), + StringListValue(16, "16"d), + StringListValue(20, "20"d), + StringListValue(24, "24"d), + StringListValue(32, "32"d), + StringListValue(48, "48"d), + StringListValue(255, "Always OFF"d)]); + ui.addFloatComboBox("interface/fontGamma", UIString("Font gamma"d), + [ + StringListValue(500, "0.5 "d), + StringListValue(600, "0.6 "d), + StringListValue(700, "0.7 "d), + StringListValue(800, "0.8 "d), + StringListValue(850, "0.85 "d), + StringListValue(900, "0.9 "d), + StringListValue(950, "0.95 "d), + StringListValue(1000, "1.0 "d), + StringListValue(1050, "1.05 "d), + StringListValue(1100, "1.1 "d), + StringListValue(1150, "1.15 "d), + StringListValue(1200, "1.2 "d), + StringListValue(1250, "1.25 "d), + StringListValue(1300, "1.3 "d), + StringListValue(1400, "1.4 "d), + StringListValue(1500, "1.5 "d), + StringListValue(1700, "1.7 "d), + StringListValue(2000, "2.0 "d)]); + return res; } diff --git a/src/dlangide/workspace/project.d b/src/dlangide/workspace/project.d index 5728c43..5dc712f 100644 --- a/src/dlangide/workspace/project.d +++ b/src/dlangide/workspace/project.d @@ -480,7 +480,7 @@ class Project : WorkspaceItem { if (!selectionsFile.load()) return false; Setting versions = selectionsFile.objectByPath("versions"); - if (!versions) + if (!versions.isObject) return false; string[string] versionMap = versions.strMap; foreach(packageName, packageVersion; versionMap) { diff --git a/views/res/ide_theme_dark.xml b/views/res/ide_theme_dark.xml new file mode 100644 index 0000000..bb43ee9 --- /dev/null +++ b/views/res/ide_theme_dark.xml @@ -0,0 +1,11 @@ + + + + + + diff --git a/views/res/ide_theme_default.xml b/views/res/ide_theme_default.xml new file mode 100644 index 0000000..b3561b8 --- /dev/null +++ b/views/res/ide_theme_default.xml @@ -0,0 +1,11 @@ + + + + + + + diff --git a/views/resources.list b/views/resources.list index 2d7728f..51eb8d2 100644 --- a/views/resources.list +++ b/views/resources.list @@ -1,3 +1,5 @@ +res/ide_theme_default.xml +res/ide_theme_dark.xml res/i18n/en.ini res/i18n/ru.ini res/mdpi/cr3_logo.png