diff --git a/.clang-format b/.clang-format
index e7fd784dccff38f5515e53349eb2c918c770770e..778726d25a4e606f9d50d65846e00c649f3649b9 100644
--- a/.clang-format
+++ b/.clang-format
@@ -62,6 +62,7 @@ CommentPragmas: '^ KEEP pragma:'
 ForEachMacros:
   - 'for_each'
   - 'for_ever'
+  - 'forever'
   - 'parallel_for'
   - 'script_class'
   - 'function_class'
diff --git a/CMakeLists.txt b/CMakeLists.txt
index edb6c4f9740ebf5f5958aaf16fd6b525f8bfea6c..bfa3728ed3e58b52149c4b134404659194c4511f 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -5,6 +5,20 @@ project(Vivy VERSION 0.1 LANGUAGES CXX)
 cmake_policy(SET CMP0100 NEW) # Let cmake use moc and uic for .hh files
 cmake_policy(SET CMP0009 NEW) # Do not follow symlinks with GLOB_RECURSE
 
+# Don't forget for specific things
+if(WIN32)
+    message("You are building on windows, pay attenion to the dependencies")
+endif()
+if(MSVC OR MSYS OR MINGW)
+    message("You are building with a windows compiler")
+endif()
+if(APPLE)
+    message("You are building on MacOS X")
+endif()
+if(UNIX AND NOT APPLE)
+    message("You are building on Linux, FreeBSD or any other toaster OS")
+endif()
+
 # Pass -fPIC
 set(CMAKE_POSITION_INDEPENDENT_CODE ON)
 
@@ -20,11 +34,6 @@ set(THREADS_PREFER_PTHREAD_FLAG ON)
 find_package(QT NAMES Qt6 Qt5      COMPONENTS Widgets REQUIRED)
 find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Widgets REQUIRED)
 
-if(WIN32)
-    message("You are building on windows, pay attenion to the dependencies")
-    # Needed setup for Vivy to compile on Windows goes here
-endif()
-
 # Find others dependencies
 find_library(AVCODEC_LIBRARY    avcodec     4.0 REQUIRED)
 find_library(AVUTIL_LIBRARY     avutil      4.0 REQUIRED)
@@ -41,7 +50,10 @@ add_subdirectory(
 # Grab all files
 file(GLOB_RECURSE Vivy_SRC CONFIGURE_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/src/*.cc")
 file(GLOB_RECURSE Vivy_INC CONFIGURE_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/src/*.hh")
-set(PROJECT_SOURCES ${Vivy_SRC} ${Vivy_INC})
+if(APPLE)
+    file(GLOB_RECURSE Vivy_APPLE_SRC CONFIGURE_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/src/*.mm")
+endif()
+set(PROJECT_SOURCES ${Vivy_SRC} ${Vivy_INC} ${Vivy_APPLE_SRC})
 
 # Add the Vivy executable
 if(${QT_VERSION_MAJOR} GREATER_EQUAL 6)
@@ -90,11 +102,18 @@ target_compile_features(Vivy PRIVATE
 
 # More options and warnings
 target_compile_options(Vivy PRIVATE
-    -Wall -Wextra -Wshadow -pedantic
-    -Wcast-align -Wconversion -Wsign-conversion -Wunused-variable
-    -Wmisleading-indentation -Wnull-dereference -Wdouble-promotion
+    -Wall -Wextra -Wpedantic
+    -Wshadow
+    -Wcast-align
+    -Wconversion
+    -Wsign-conversion
+    -Wunused-variable
+    -Wmisleading-indentation
+    -Wnull-dereference
+    -Wdouble-promotion
     -Wformat=2
-    -Woverloaded-virtual -Wnon-virtual-dtor
+    -Woverloaded-virtual
+    -Wnon-virtual-dtor
     -Wignored-qualifiers
 
     -fopenmp
@@ -117,8 +136,11 @@ if (${CMAKE_CXX_COMPILER_ID} STREQUAL "Clang")
 
         # Disable some things because we want C++20 and don't control some Qt
         # generated files...
-        -Wno-c++98-compat -Wno-c++98-c++11-c++14-c++17-compat-pedantic
+        -Wno-c++98-compat
         -Wno-c++98-compat-pedantic
+        -Wno-c++98-c++11-c++14-c++17-compat-pedantic
+        -Wno-c++20-compat
+
         -Wno-extra-semi-stmt
         -Wno-redundant-parens
         -Wno-padded
diff --git a/PreCompiledHeaders.cmake b/PreCompiledHeaders.cmake
index 4509f913c7712f48d801e1a550e829929b81da57..f1d1768e24d9a3c085dac93f042b46803331ee82 100644
--- a/PreCompiledHeaders.cmake
+++ b/PreCompiledHeaders.cmake
@@ -8,6 +8,7 @@ set(STL_INC
     <stdexcept>
     <sstream>
     <map>
+    <utility>
     <locale>
     <functional>
     <initializer_list>
@@ -22,6 +23,9 @@ set(STL_INC
     <climits>
     <ctype.h>
     <mutex>
+    <iomanip>
+    <filesystem>
+    <bit>
 )
 
 set(EXT_INC PRIVATE
@@ -31,6 +35,9 @@ set(EXT_INC PRIVATE
 
 set(QT_STRUCT_INC
     PRIVATE
+    <qglobal.h>
+    <QThread>
+    <QOperatingSystemVersion>
     <QtGlobal>
     <QObject>
     <QRegularExpression>
@@ -54,7 +61,9 @@ set(QT_STRUCT_INC
     <QList>
     <QVector>
     <QMap>
+    <QSettings>
     <QStringList>
+    <QMetaEnum>
     <QJsonObject>
     <QJsonArray>
     <QJsonDocument>
@@ -66,6 +75,7 @@ set(QT_WIDGET_INC
     <QCoreApplication>
     <QTextDocument>
     <QColor>
+    <QPalette>
     <QWidget>
     <QIcon>
     <QFont>
@@ -133,6 +143,7 @@ set(QT_WIDGET_INC
     <QFileIconProvider>
     <QFontDatabase>
     <QTextCodec>
+    <QListWidget>
 )
 
 set(Vivy_PRECOMPILED_INC PRIVATE
diff --git a/README.md b/README.md
index ab4f429f8d89ec471138afa5dab4be07aa38f61e..5b28b7c4c8e1f2b73a99e65944c48b4bb3f8cc1e 100644
--- a/README.md
+++ b/README.md
@@ -20,21 +20,14 @@ This project depends on cmake as a build system. For dependencies on
 Windows and MacOS, the easier thing to do is grab qtcreator and find a
 way to add the av libraries to it.
 
-#### Ubuntu/debian
-
-Vivy depends on Qt5 and libav. On ubuntu install the following packages:
-`qtbase5-dev qtchooser qt5-qmake qtbase5-dev-tools libavutil-dev
-libavcodec-dev libavformat-dev libmpv-dev cmake clang clang-format
-libomp-dev`.
-
-#### Arch/Manjaro
-
-TODO!
-
-#### FreeBSD and other toasters
-
-You will need cmake, qt and av dev packages. Set your favorite C++
-compiler with `-DCMAKE_CXX_COMPILER` and you're good to go.
+- Ubuntu/debian: Vivy depends on Qt5 and libav. On ubuntu install the
+  following packages: `qtbase5-dev qtchooser qt5-qmake qtbase5-dev-tools
+  libavutil-dev libavcodec-dev libavformat-dev libmpv-dev cmake clang
+  clang-format libomp-dev`.
+- Arch/Manjaro: TODO!
+- FreeBSD and other toasters: You will need cmake, qt and av dev
+  packages. Set your favorite C++ compiler with `-DCMAKE_CXX_COMPILER`
+  and you're good to go.
 
 ## Licence
 
diff --git a/rsc/VivyRessources.qrc b/rsc/VivyRessources.qrc
index f86514d871aa19755d64bb9729a9771dad6b6240..62d7ef146db8b572734d0347b3325016bdea862d 100644
--- a/rsc/VivyRessources.qrc
+++ b/rsc/VivyRessources.qrc
@@ -25,6 +25,15 @@
     <!-- Lua scripts -->
     <file>lua/lib.lua</file>
     <file>lua/module.lua</file>
+
+    <!-- QtCreator's theming thing -->
+    <file>theme/dark.creatortheme</file>
+    <file>theme/default.creatortheme</file>
+    <file>theme/design.creatortheme</file>
+    <file>theme/design-light.creatortheme</file>
+    <file>theme/flat.creatortheme</file>
+    <file>theme/flat-dark.creatortheme</file>
+    <file>theme/flat-light.creatortheme</file>
 </qresource>
 
 <!-- Breeze icons, LGPL-3.0 Licence -->
diff --git a/rsc/lua/lib.lua b/rsc/lua/lib.lua
index fb3f7efe7338c8f7be4aa9da7976dccdb82a1aac..084d70b46074618c191081e8a300d3af42bec12a 100644
--- a/rsc/lua/lib.lua
+++ b/rsc/lua/lib.lua
@@ -83,6 +83,11 @@ function Vivy:newColor (colorString)
     return false
 end
 
+function Vivy:start  () return Vivy.___Fun:start()  end
+function Vivy:finish () return Vivy.___Fun:finish() end
+function Vivy:width  () return Vivy.___Fun:width()  end
+function Vivy:height () return Vivy.___Fun:height() end
+
 -- In case we are able to do the _ENV <- Vivy one day...
 Vivy["Vivy"] = Vivy
 
diff --git a/rsc/theme/dark.creatortheme b/rsc/theme/dark.creatortheme
new file mode 100644
index 0000000000000000000000000000000000000000..4f92228aa3603d878b27b54f103403940f34eaca
--- /dev/null
+++ b/rsc/theme/dark.creatortheme
@@ -0,0 +1,401 @@
+[General]
+ThemeName=Dark
+PreferredStyles=Fusion
+DefaultTextEditorColorScheme=dark.xml
+
+[Palette]
+shadowBackground=ff232323
+text=ffe7e7e7
+textDisabled=7fffffff
+textHighlighted=ffe7e7e7
+hoverBackground=18ffffff
+selectedBackground=aa1f75cc
+selectedBackgroundText=aa1f75cc
+normalBackground=ff333333
+alternateBackground=ff515151
+error=ffd84044
+warning=ffe0b716
+splitterColor=ff313131
+textColorLink=ff007af4
+textColorLinkVisited=ffa57aff
+backgroundColorDisabled=ff444444
+qmlDesignerButtonColor=ff3c3e40
+
+[Colors]
+;DS controls theme START
+DSpanelBackground=ff323232
+
+DSinteraction=ff2aafd3
+DSerrorColor=ffdf3a3a
+DSdisabledColor=ff707070
+
+DScontrolBackground=ff323232
+DScontrolBackgroundInteraction=ff595959
+DScontrolBackgroundDisabled=ff323232
+DScontrolBackgroundGlobalHover=ff474747
+DScontrolBackgroundHover=ff666666
+
+DScontrolOutline=ff1f1f1f
+DScontrolOutlineInteraction=ff2aafd3
+DScontrolOutlineDisabled=ff707070
+
+DStextColor=ffffffff
+DStextColorDisabled=ff707070
+DStextSelectionColor=ff2aafd3
+DStextSelectedTextColor=ff000000
+
+DSplaceholderTextColor=ffffffff
+DSplaceholderTextColorInteraction=ffababab
+
+DSiconColor=ffffffff
+DSiconColorHover=ff262626
+DSiconColorInteraction=ff707070
+DSiconColorDisabled=ff707070
+DSiconColorSelected=ff2aafd3
+
+DSlinkIndicatorColor=ff808080
+DSlinkIndicatorColorHover=ffffffff
+DSlinkIndicatorColorInteraction=ff2aafd3
+DSlinkIndicatorColorDisabled=ff707070
+
+DSpopupBackground=ff474747
+DSpopupOverlayColor=99191919
+
+DSsliderActiveTrack=ff7c7b7b
+DSsliderActiveTrackHover=ff000000
+DSsliderActiveTrackFocus=ffaaaaaa
+DSsliderInactiveTrack=ff595959
+DSsliderInactiveTrackHover=ff505050
+DSsliderInactiveTrackFocus=ff606060
+DSsliderHandle=ff1f1f1f
+DSsliderHandleHover=ff606060
+DSsliderHandleFocus=ff0492c9
+DSsliderHandleInteraction=ff2aafd3
+
+DSscrollBarTrack=ff404040
+DSscrollBarHandle=ff505050
+
+DSsectionHeadBackground=ff1f1f1f
+
+DSstateDefaultHighlight=ffffe400
+DSstateSeparatorColor=ff7c7b7b
+DSstateBackgroundColor=ff383838
+DSstatePreviewOutline=ffaaaaaa
+
+DSchangedStateText=ff99ccff
+
+DS3DAxisXColor=ffd00000
+DS3DAxisYColor=ff009900
+DS3DAxisZColor=ff5050ff
+
+DSactionBinding=ff2aafd3
+DSactionAlias=fff93a3a
+DSactionKeyframe=ffe0e01b
+DSactionJIT=ff2db543
+
+DStableHeaderBackground=ffff0000
+DStableHeaderText=ff00ff00
+
+DSdockContainerBackground=ff323232
+DSdockContainerSplitter=ff323232
+DSdockAreaBackground=ff262728
+
+DSdockWidgetBackground=ff00ff00
+DSdockWidgetSplitter=ff595959
+DSdockWidgetTitleBar=ff1f1f1f
+
+DStitleBarText=ffdadada
+DStitleBarIcon=ffffffff
+DStitleBarButtonHover=40ffffff
+DStitleBarButtonPress=60ffffff
+
+DStabContainerBackground=ff0000ff
+DStabSplitter=ff595959
+
+DStabInactiveBackground=ff1f1f1f
+DStabInactiveText=ffdadada
+DStabInactiveIcon=ffffffff
+DStabInactiveButtonHover=ff1f1f1f
+DStabInactiveButtonPress=ff1f1f1f
+
+DStabActiveBackground=ffdadada
+DStabActiveText=ff111111
+DStabActiveIcon=ff000000
+DStabActiveButtonHover=ffdadada
+DStabActiveButtonPress=ffdadada
+
+DStabFocusBackground=ff2aafd3
+DStabFocusText=ff111111
+DStabFocusIcon=ff000000
+DStabFocusButtonHover=ff2aafd3
+DStabFocusButtonPress=ff2aafd3
+
+DSnavigatorBranch=ff7c7b7b
+DSnavigatorBranchIndicator=ff7c7b7b
+DSnavigatorItemBackground=ff262626
+DSnavigatorItemBackgroundHover=ff666666
+DSnavigatorItemBackgroundSelected=ff1f1f1f
+DSnavigatorText=ffffffff
+DSnavigatorTextHover=ff1f1f1f
+DSnavigatorTextSelected=ff2aafd3
+DSnavigatorIcon=ffffffff
+DSnavigatorIconHover=ff1f1f1f
+DSnavigatorIconSelected=ff7c7b7b
+DSnavigatorAliasIconChecked=ffff0000
+DSnavigatorDropIndicatorBackground=ff2aafd3
+DSnavigatorDropIndicatorOutline=ff2aafd3
+
+DSheaderViewBackground=ff1f1f1f
+DStableViewAlternateBackground=ff00ff00
+
+DStoolTipBackground=ff111111
+DStoolTipOutline=ffdadada
+DStoolTipText=ffdadada
+
+DSUnimportedModuleColor=ffe33c2e
+
+;DS controls theme END
+
+BackgroundColorAlternate=alternateBackground
+BackgroundColorDark=shadowBackground
+BackgroundColorHover=hoverBackground
+BackgroundColorNormal=normalBackground
+BackgroundColorDisabled=backgroundColorDisabled
+BackgroundColorSelected=selectedBackground
+BadgeLabelBackgroundColorChecked=normalBackground
+BadgeLabelBackgroundColorUnchecked=selectedBackground
+BadgeLabelTextColorChecked=text
+BadgeLabelTextColorUnchecked=text
+CanceledSearchTextColor=ff0000
+ComboBoxArrowColor=text
+ComboBoxArrowColorDisabled=text
+ComboBoxTextColor=text
+DetailsButtonBackgroundColorHover=hoverBackground
+DetailsWidgetBackgroundColor=ff4a4a4a
+DockWidgetResizeHandleColor=shadowBackground
+DoubleTabWidget1stSeparatorColor=hoverBackground
+DoubleTabWidget1stTabActiveTextColor=text
+DoubleTabWidget1stTabBackgroundColor=ff4a4a4a
+DoubleTabWidget1stTabInactiveTextColor=textDisabled
+DoubleTabWidget2ndSeparatorColor=hoverBackground
+DoubleTabWidget2ndTabActiveTextColor=text
+DoubleTabWidget2ndTabBackgroundColor=ff434343
+DoubleTabWidget2ndTabInactiveTextColor=textDisabled
+EditorPlaceholderColor=normalBackground
+FancyToolBarSeparatorColor=43ffffff
+FancyTabBarBackgroundColor=shadowBackground
+FancyTabBarSelectedBackgroundColor=88000000
+FancyTabWidgetDisabledSelectedTextColor=textDisabled
+FancyTabWidgetDisabledUnselectedTextColor=textDisabled
+FancyTabWidgetEnabledSelectedTextColor=text
+FancyTabWidgetEnabledUnselectedTextColor=text
+FancyToolButtonHoverColor=35ffffff
+FancyToolButtonSelectedColor=selectedBackground
+FutureProgressBackgroundColor=shadowBackground
+IconsBaseColor=ffdcdcdc
+IconsDisabledColor=textDisabled
+IconsInfoColor=ff3099dc
+IconsInfoToolBarColor=ff3099dc
+IconsWarningColor=warning
+IconsWarningToolBarColor=ffe0b716
+IconsErrorColor=error
+IconsErrorToolBarColor=ffd84044
+IconsRunColor=ff7fc341
+IconsRunToolBarColor=ff7fc341
+IconsStopColor=ffe7353b
+IconsStopToolBarColor=ffe7353b
+IconsInterruptColor=ff7488db
+IconsInterruptToolBarColor=ff7488db
+IconsDebugColor=ffb8c6ff
+IconsNavigationArrowsColor=ffebc322
+IconsBuildHammerHandleColor=ffdd7710
+IconsBuildHammerHeadColor=ff989898
+IconsModeWelcomeActiveColor=ff80c342
+IconsModeEditActiveColor=ff99aaef
+IconsModeDesignActiveColor=ffbb6000
+IconsModeDebugActiveColor=ff99aaef
+IconsModeProjectActiveColor=ff80c342
+IconsModeAnalyzeActiveColor=ff43adee
+IconsModeHelpActiveColor=fff4be04
+IconsCodeModelKeywordColor=ffaaaaaa
+IconsCodeModelClassColor=ffc0b550
+IconsCodeModelStructColor=ff53b053
+IconsCodeModelFunctionColor=ffd34373
+IconsCodeModelVariableColor=ff2bbbcc
+IconsCodeModelEnumColor=ffc0b550
+IconsCodeModelMacroColor=ff477ba0
+IconsCodeModelAttributeColor=ff316511
+IconsCodeModelUniformColor=ff994899
+IconsCodeModelVaryingColor=ffa08833
+IconsCodeModelOverlayBackgroundColor=88000000
+IconsCodeModelOverlayForegroundColor=ffdcdcdc
+InfoBarBackground=ff505000
+InfoBarText=text
+MenuBarEmptyAreaBackgroundColor=shadowBackground
+MenuBarItemBackgroundColor=shadowBackground
+MenuBarItemTextColorDisabled=textDisabled
+MenuBarItemTextColorNormal=text
+MenuItemTextColorDisabled=textDisabled
+MenuItemTextColorNormal=text
+MiniProjectTargetSelectorBackgroundColor=normalBackground
+MiniProjectTargetSelectorBorderColor=shadowBackground
+MiniProjectTargetSelectorSummaryBackgroundColor=shadowBackground
+MiniProjectTargetSelectorTextColor=text
+PanelStatusBarBackgroundColor=shadowBackground
+PanelsWidgetSeparatorLineColor=000000
+PanelTextColorDark=text
+PanelTextColorMid=ffa0a0a0
+PanelTextColorLight=text
+ProgressBarColorError=error
+ProgressBarColorFinished=ff5aaa3c
+ProgressBarColorNormal=ff808080
+ProgressBarTitleColor=text
+ProgressBarBackgroundColor=normalBackground
+SplitterColor=splitterColor
+TextColorDisabled=textDisabled
+TextColorError=ffff4040
+TextColorHighlight=ffff0000
+TextColorHighlightBackground=7a6f1c
+TextColorLink=textColorLink
+TextColorLinkVisited=textColorLinkVisited
+TextColorNormal=text
+ToggleButtonBackgroundColor=shadowBackground
+ToolBarBackgroundColor=shadowBackground
+TreeViewArrowColorNormal=hoverBackground
+TreeViewArrowColorSelected=text
+
+OutputPanes_DebugTextColor=text
+OutputPanes_ErrorMessageTextColor=ffff6c6c
+OutputPanes_MessageOutput=ff008787
+OutputPanes_NormalMessageTextColor=ff008787
+OutputPanes_StdErrTextColor=ffff6666
+OutputPanes_StdOutTextColor=text
+OutputPanes_WarningMessageTextColor=fff3c300
+OutputPanes_TestPassTextColor=ff00b400
+OutputPanes_TestFailTextColor=ffc82828
+OutputPanes_TestXFailTextColor=ff28dc28
+OutputPanes_TestXPassTextColor=ffdc2828
+OutputPanes_TestSkipTextColor=ff828282
+OutputPanes_TestWarnTextColor=ffc8c800
+OutputPanes_TestFatalTextColor=ffb42828
+OutputPanes_TestDebugTextColor=ff329696
+OutputPaneButtonFlashColor=error
+OutputPaneToggleButtonTextColorChecked=textHighlighted
+OutputPaneToggleButtonTextColorUnchecked=text
+
+Debugger_LogWindow_LogInput=ff00acac
+Debugger_LogWindow_LogStatus=ff00875a
+Debugger_LogWindow_LogTime=ffbf0303
+
+Debugger_WatchItem_ValueNormal=text
+Debugger_WatchItem_ValueInvalid=textDisabled
+Debugger_WatchItem_ValueChanged=ffff6666
+
+Debugger_Breakpoint_TextMarkColor=ffff4040
+
+Welcome_TextColor=text
+Welcome_ForegroundPrimaryColor=ff999999
+Welcome_ForegroundSecondaryColor=ff808080
+Welcome_BackgroundColor=normalBackground
+Welcome_ButtonBackgroundColor=normalBackground
+Welcome_DividerColor=ff555555
+Welcome_HoverColor=ff444444
+Welcome_LinkColor=ff78bb39
+Welcome_DisabledLinkColor=textDisabled
+
+Timeline_TextColor=text
+Timeline_BackgroundColor1=normalBackground
+Timeline_BackgroundColor2=ff444444
+Timeline_DividerColor=ff555555
+Timeline_HighlightColor=ff3099dc
+Timeline_PanelBackgroundColor=ff808080
+Timeline_PanelHeaderColor=alternateBackground
+Timeline_HandleColor=alternateBackground
+Timeline_RangeColor=selectedBackground
+
+VcsBase_FileStatusUnknown_TextColor=text
+VcsBase_FileAdded_TextColor=ff00ff00
+VcsBase_FileModified_TextColor=ff8ee0ff
+VcsBase_FileDeleted_TextColor=ffff6c6c
+VcsBase_FileRenamed_TextColor=ffffa500
+VcsBase_FileUnmerged_TextColor=ffff4040
+
+Bookmarks_TextMarkColor=ff8080ff
+
+TextEditor_SearchResult_ScrollBarColor=ff00c000
+TextEditor_CurrentLine_ScrollBarColor=ffffffff
+
+ProjectExplorer_TaskError_TextMarkColor=error
+ProjectExplorer_TaskWarn_TextMarkColor=warning
+
+CodeModel_Error_TextMarkColor=error
+CodeModel_Warning_TextMarkColor=warning
+
+PaletteWindow=normalBackground
+PaletteWindowText=text
+PaletteBase=normalBackground
+PaletteAlternateBase=alternateBackground
+PaletteButton=shadowBackground
+PaletteBrightText=ffff0000
+PaletteText=text
+PaletteButtonText=text
+PaletteButtonTextDisabled=textDisabled
+PaletteToolTipBase=66000000
+PaletteHighlight=selectedBackgroundText
+PaletteDark=shadowBackground
+PaletteHighlightedText=textHighlighted
+PaletteToolTipText=text
+PaletteLink=textColorLink
+PaletteLinkVisited=textColorLinkVisited
+PaletteWindowDisabled=backgroundColorDisabled
+PaletteWindowTextDisabled=textDisabled
+PaletteBaseDisabled=backgroundColorDisabled
+PaletteTextDisabled=textDisabled
+PaletteMid=ffa0a0a0
+PalettePlaceholderText=ff8d8d8d
+
+QmlDesigner_BackgroundColor=qmlDesignerButtonColor
+QmlDesigner_HighlightColor=ff46a2da
+QmlDesigner_FormEditorSelectionColor=ff4ba2ff
+QmlDesigner_FormEditorForegroundColor=ffffffff
+QmlDesigner_BackgroundColorDarkAlternate=ff323232
+QmlDesigner_BackgroundColorDarker=ff151515
+QmlDesigner_BorderColor=splitterColor
+QmlDesigner_ButtonColor=ff505050
+QmlDesigner_TabDark=shadowBackground
+QmlDesigner_TabLight=text
+QmlDesigner_FormeditorBackgroundColor=qmlDesignerButtonColor
+QmlDesigner_AlternateBackgroundColor=qmlDesignerButtonColor
+QmlDesigner_ScrollBarHandleColor=ff505050
+
+[Flags]
+ComboBoxDrawTextShadow=false
+DerivePaletteFromTheme=true
+DrawIndicatorBranch=true
+DrawSearchResultWidgetFrame=false
+DrawTargetSelectorBottom=false
+DrawToolBarHighlights=true
+DrawToolBarBorders=false
+ApplyThemePaletteGlobally=true
+FlatToolBars=true
+FlatSideBarIcons=true
+FlatProjectsMode=true
+FlatMenuBar=true
+ToolBarIconShadow=true
+WindowColorAsBase=false
+DarkUserInterface=true
+
+[Gradients]
+DetailsWidgetHeaderGradient\1\color=00000000
+DetailsWidgetHeaderGradient\1\pos=1
+DetailsWidgetHeaderGradient\size=1
+
+[ImageFiles]
+IconOverlayCSource=:/cppeditor/images/dark_qt_c.png
+IconOverlayCppHeader=:/cppeditor/images/dark_qt_h.png
+IconOverlayCppSource=:/cppeditor/images/dark_qt_cpp.png
+IconOverlayPrf=:/qtsupport/images/dark_qt_project.png
+IconOverlayPri=:/qtsupport/images/dark_qt_project.png
+IconOverlayPro=:/qtsupport/images/dark_qt_project.png
+StandardPixmapFileIcon=:/utils/images/dark_fileicon.png
+StandardPixmapDirIcon=:/utils/images/dark_foldericon.png
diff --git a/rsc/theme/default.creatortheme b/rsc/theme/default.creatortheme
new file mode 100644
index 0000000000000000000000000000000000000000..aeabe8269066d53195bdc256fc5a131372fc0cb7
--- /dev/null
+++ b/rsc/theme/default.creatortheme
@@ -0,0 +1,359 @@
+[General]
+ThemeName=Classic
+PreferredStyles=
+
+[Palette]
+brightText=ffffffff
+darkText=ff000000
+textDisabled=88a0a0a0
+error=ffdf4f4f
+warning=ffecbc1c
+shadowBackground=ff232323
+splitterColor=ff151515
+qmlDesignerButtonColor=ff4c4e50
+
+[Colors]
+;DS controls theme START
+DSpanelBackground=ffeaeaea
+
+DSinteraction=ff2aafd3
+DSerrorColor=ffdf3a3a
+DSdisabledColor=ff8e8e8e
+
+DScontrolBackground=ffeaeaea
+DScontrolBackgroundInteraction=ffc9c9c9
+DScontrolBackgroundDisabled=ffeaeaea
+DScontrolBackgroundGlobalHover=ffe5e5e5
+DScontrolBackgroundHover=ffd1d1d1
+
+DScontrolOutline=ffcecccc
+DScontrolOutlineInteraction=ff2aafd3
+DScontrolOutlineDisabled=ff707070
+
+DStextColor=ff262626
+DStextColorDisabled=ff707070
+DStextSelectionColor=ff2aafd3
+DStextSelectedTextColor=ff000000
+
+DSplaceholderTextColor=ff262626
+DSplaceholderTextColorInteraction=ffababab
+
+DSiconColor=ff262626
+DSiconColorHover=ff191919
+DSiconColorInteraction=ffffffff
+DSiconColorDisabled=ff707070
+DSiconColorSelected=ff2aafd3
+
+DSlinkIndicatorColor=ff808080
+DSlinkIndicatorColorHover=ff1f1f1f
+DSlinkIndicatorColorInteraction=ff2aafd3
+DSlinkIndicatorColorDisabled=ff707070
+
+DSpopupBackground=ffd3d3d3
+DSpopupOverlayColor=99191919
+
+DSsliderActiveTrack=ff7c7b7b
+DSsliderActiveTrackHover=ff000000
+DSsliderActiveTrackFocus=ffaaaaaa
+DSsliderInactiveTrack=ffaaaaaa
+DSsliderInactiveTrackHover=ff505050
+DSsliderInactiveTrackFocus=ff606060
+DSsliderHandle=ff1f1f1f
+DSsliderHandleHover=ff606060
+DSsliderHandleFocus=ff0492c9
+DSsliderHandleInteraction=ff2aafd3
+
+DSscrollBarTrack=ffb5b4b4
+DSscrollBarHandle=ff9b9b9b
+
+DSsectionHeadBackground=ffd8d8d8
+
+DSstateDefaultHighlight=ffffe400
+DSstateSeparatorColor=ffadadad
+DSstateBackgroundColor=ffe0e0e0
+DSstatePreviewOutline=ff363636
+
+DSchangedStateText=ff99ccff
+
+DS3DAxisXColor=ffd00000
+DS3DAxisYColor=ff009900
+DS3DAxisZColor=ff5050ff
+
+DSactionBinding=ff2aafd3
+DSactionAlias=fff93a3a
+DSactionKeyframe=ffe0e01b
+DSactionJIT=ff2db543
+
+DStableHeaderBackground=ffff0000
+DStableHeaderText=ff00ff00
+
+DSdockContainerBackground=ff323232
+DSdockContainerSplitter=ff323232
+DSdockAreaBackground=ff262728
+
+DSdockWidgetBackground=ff00ff00
+DSdockWidgetSplitter=ff595959
+DSdockWidgetTitleBar=ffeaeaea
+
+DStitleBarText=ffdadada
+DStitleBarIcon=ff4f5052
+DStitleBarButtonHover=40ffffff
+DStitleBarButtonPress=60ffffff
+
+DStabContainerBackground=ff0000ff
+DStabSplitter=ff595959
+
+DStabInactiveBackground=ff999999
+DStabInactiveText=ff262626
+DStabInactiveIcon=ffffffff
+DStabInactiveButtonHover=ff1f1f1f
+DStabInactiveButtonPress=ff1f1f1f
+
+DStabActiveBackground=ffdadada
+DStabActiveText=ff111111
+DStabActiveIcon=ff000000
+DStabActiveButtonHover=ffdadada
+DStabActiveButtonPress=ffdadada
+
+DStabFocusBackground=ff2aafd3
+DStabFocusText=ff111111
+DStabFocusIcon=ff000000
+DStabFocusButtonHover=ff2aafd3
+DStabFocusButtonPress=ff2aafd3
+
+DSnavigatorBranch=ff7c7b7b
+DSnavigatorBranchIndicator=ff7c7b7b
+DSnavigatorItemBackground=ffd8d8d8
+DSnavigatorItemBackgroundHover=ffc2c2c2
+DSnavigatorItemBackgroundSelected=ffffffff
+DSnavigatorText=ff262626
+DSnavigatorTextHover=ff1f1f1f
+DSnavigatorTextSelected=ff2aafd3
+DSnavigatorIcon=ff1f1f1f
+DSnavigatorIconHover=ff1f1f1f
+DSnavigatorIconSelected=ff7c7b7b
+DSnavigatorAliasIconChecked=ffff0000
+DSnavigatorDropIndicatorBackground=ff2aafd3
+DSnavigatorDropIndicatorOutline=ff2aafd3
+
+DSheaderViewBackground=ffd8d8d8
+DStableViewAlternateBackground=ff00ff00
+
+DStoolTipBackground=ff111111
+DStoolTipOutline=ffdadada
+DStoolTipText=ffdadada
+
+DSUnimportedModuleColor=ffe33c2e
+
+;DS controls theme END
+
+BackgroundColorAlternate=ff3d3d3d
+BackgroundColorDark=shadowBackground
+BackgroundColorHover=ff515151
+BackgroundColorNormal=ffffffff
+BackgroundColorSelected=ff151515
+BackgroundColorDisabled=ffefebe7
+BadgeLabelBackgroundColorChecked=ffe0e0e0
+BadgeLabelBackgroundColorUnchecked=ff808080
+BadgeLabelTextColorChecked=ff606060
+BadgeLabelTextColorUnchecked=ffffffff
+CanceledSearchTextColor=ffff0000
+ComboBoxArrowColor=ffb8b5b2
+ComboBoxArrowColorDisabled=ffdcdcdc
+ComboBoxTextColor=ffffffff
+DetailsButtonBackgroundColorHover=b4ffffff
+DetailsWidgetBackgroundColor=28ffffff
+DockWidgetResizeHandleColor=ff000000
+DoubleTabWidget1stSeparatorColor=ffff0000
+DoubleTabWidget1stTabActiveTextColor=ff000000
+DoubleTabWidget1stTabBackgroundColor=ffff0000
+DoubleTabWidget1stTabInactiveTextColor=ffffffff
+DoubleTabWidget2ndSeparatorColor=ffff0000
+DoubleTabWidget2ndTabActiveTextColor=ffffffff
+DoubleTabWidget2ndTabBackgroundColor=ffff0000
+DoubleTabWidget2ndTabInactiveTextColor=ff000000
+EditorPlaceholderColor=ffe0dcd8
+FancyToolBarSeparatorColor=60ffffff
+FancyTabBarBackgroundColor=ffff0000
+FancyTabBarSelectedBackgroundColor=ffff0000
+FancyTabWidgetDisabledSelectedTextColor=textDisabled
+FancyTabWidgetDisabledUnselectedTextColor=textDisabled
+FancyTabWidgetEnabledSelectedTextColor=ff3c3c3c
+FancyTabWidgetEnabledUnselectedTextColor=ffffffff
+FancyToolButtonHoverColor=28ffffff
+FancyToolButtonSelectedColor=32000000
+FutureProgressBackgroundColor=ffff0000
+IconsBaseColor=ffdcdcdc
+IconsDisabledColor=textDisabled
+IconsInfoColor=ff3099dc
+IconsInfoToolBarColor=ff71b2db
+IconsWarningColor=warning
+IconsWarningToolBarColor=fff2d76e
+IconsErrorColor=error
+IconsErrorToolBarColor=ffdb6f71
+IconsRunColor=ff6da838
+IconsRunToolBarColor=ffa4d576
+IconsStopColor=ffee6969
+IconsStopToolBarColor=ffff8c8c
+IconsInterruptColor=ff587ff7
+IconsInterruptToolBarColor=ff8f9dda
+IconsDebugColor=ffdcdcdc
+IconsNavigationArrowsColor=ffebc322
+IconsBuildHammerHandleColor=ffdd7710
+IconsBuildHammerHeadColor=ff989898
+IconsModeWelcomeActiveColor=ffffffff
+IconsModeEditActiveColor=ffffffff
+IconsModeDesignActiveColor=ffffffff
+IconsModeDebugActiveColor=ffffffff
+IconsModeProjectActiveColor=ffffffff
+IconsModeAnalyzeActiveColor=ffffffff
+IconsModeHelpActiveColor=ffffffff
+IconsCodeModelKeywordColor=ff777777
+IconsCodeModelClassColor=ffc0b550
+IconsCodeModelStructColor=ff53b053
+IconsCodeModelFunctionColor=fff36393
+IconsCodeModelVariableColor=ff2bbbcc
+IconsCodeModelEnumColor=ffc0b550
+IconsCodeModelMacroColor=ff476ba0
+IconsCodeModelAttributeColor=ff316511
+IconsCodeModelUniformColor=ff994899
+IconsCodeModelVaryingColor=ffa08833
+IconsCodeModelOverlayBackgroundColor=70ffffff
+IconsCodeModelOverlayForegroundColor=ff232425
+InfoBarBackground=ffffffe1
+InfoBarText=ff000000
+MenuBarEmptyAreaBackgroundColor=ffff0000
+MenuBarItemBackgroundColor=ffff0000
+MenuBarItemTextColorDisabled=ffa0a0a4
+MenuBarItemTextColorNormal=ff000000
+MenuItemTextColorDisabled=style
+MenuItemTextColorNormal=style
+MiniProjectTargetSelectorBackgroundColor=ffa0a0a0
+MiniProjectTargetSelectorBorderColor=ff000000
+MiniProjectTargetSelectorSummaryBackgroundColor=ff464646
+MiniProjectTargetSelectorTextColor=a0ffffff
+PanelStatusBarBackgroundColor=ff626262
+PanelsWidgetSeparatorLineColor=ffbfbcb8
+PanelTextColorDark=darkText
+PanelTextColorMid=ff909090
+PanelTextColorLight=brightText
+ProgressBarColorError=error
+ProgressBarColorFinished=ff5aaa3c
+ProgressBarColorNormal=b4ffffff
+ProgressBarTitleColor=ffffffff
+ProgressBarBackgroundColor=18ffffff
+SplitterColor=splitterColor
+TextColorDisabled=55000000
+TextColorError=ffff0000
+TextColorHighlight=ffa0a0a4
+TextColorHighlightBackground=ffef0b
+TextColorLink=ff0057ae
+TextColorLinkVisited=ff644a9b
+TextColorNormal=ff000000
+ToggleButtonBackgroundColor=ffff0000
+ToolBarBackgroundColor=ffff0000
+TreeViewArrowColorNormal=ffff0000
+TreeViewArrowColorSelected=ffff0000
+
+OutputPanes_DebugTextColor=ffaa00aa
+OutputPanes_ErrorMessageTextColor=ffaa0000
+OutputPanes_MessageOutput=ff0000aa
+OutputPanes_NormalMessageTextColor=ff0000aa
+OutputPanes_StdErrTextColor=ffaa0000
+OutputPanes_StdOutTextColor=ff000000
+OutputPanes_WarningMessageTextColor=ff808000
+OutputPanes_TestPassTextColor=ff009900
+OutputPanes_TestFailTextColor=ffa00000
+OutputPanes_TestXFailTextColor=ff28f028
+OutputPanes_TestXPassTextColor=fff02828
+OutputPanes_TestSkipTextColor=ff787878
+OutputPanes_TestWarnTextColor=ffd0bb00
+OutputPanes_TestFatalTextColor=ff640000
+OutputPanes_TestDebugTextColor=ff329696
+OutputPaneButtonFlashColor=ffff0000
+OutputPaneToggleButtonTextColorChecked=ffffffff
+OutputPaneToggleButtonTextColorUnchecked=ff000000
+
+Debugger_LogWindow_LogInput=ff0000ff
+Debugger_LogWindow_LogStatus=ff008000
+Debugger_LogWindow_LogTime=ff800000
+
+Debugger_WatchItem_ValueNormal=ff000000
+Debugger_WatchItem_ValueInvalid=ff8c8c8c
+Debugger_WatchItem_ValueChanged=ffc80000
+
+Debugger_Breakpoint_TextMarkColor=ffff4040
+
+Welcome_TextColor=ff000000
+Welcome_ForegroundPrimaryColor=ff555759
+Welcome_ForegroundSecondaryColor=ff727476
+Welcome_BackgroundColor=fff8f8f8
+Welcome_ButtonBackgroundColor=ffdfdfdf
+Welcome_DividerColor=ffd6d6d6
+Welcome_HoverColor=ffe8e8e8
+Welcome_LinkColor=ff5caa15
+Welcome_DisabledLinkColor=textDisabled
+
+Timeline_TextColor=darkText
+Timeline_BackgroundColor1=ffffffff
+Timeline_BackgroundColor2=fff6f6f6
+Timeline_DividerColor=ffd6d6d6
+Timeline_HighlightColor=ff71b2db
+Timeline_PanelBackgroundColor=ffd6d6d6
+Timeline_PanelHeaderColor=ff3d3d3d
+Timeline_HandleColor=ff3d3d3d
+Timeline_RangeColor=66000000
+
+VcsBase_FileStatusUnknown_TextColor=ff000000
+VcsBase_FileAdded_TextColor=ff00aa00
+VcsBase_FileModified_TextColor=ff0000ee
+VcsBase_FileDeleted_TextColor=ff800000
+VcsBase_FileRenamed_TextColor=ffd77d00
+VcsBase_FileUnmerged_TextColor=ffee0000
+
+Bookmarks_TextMarkColor=ffa0a0ff
+
+TextEditor_SearchResult_ScrollBarColor=ff00c000
+TextEditor_CurrentLine_ScrollBarColor=ff404040
+
+ProjectExplorer_TaskError_TextMarkColor=error
+ProjectExplorer_TaskWarn_TextMarkColor=warning
+
+CodeModel_Error_TextMarkColor=error
+CodeModel_Warning_TextMarkColor=warning
+
+QmlDesigner_BackgroundColor=qmlDesignerButtonColor
+QmlDesigner_HighlightColor=ff46a2da
+QmlDesigner_FormEditorSelectionColor=ff4ba2ff
+QmlDesigner_FormEditorForegroundColor=brightText
+QmlDesigner_BackgroundColorDarkAlternate=ffeaeaea
+QmlDesigner_BackgroundColorDarker=ff4e4e4e
+QmlDesigner_BorderColor=splitterColor
+QmlDesigner_ButtonColor=ff7a7a7a
+QmlDesigner_TabDark=shadowBackground
+QmlDesigner_TabLight=brightText
+QmlDesigner_FormeditorBackgroundColor=qmlDesignerButtonColor
+QmlDesigner_AlternateBackgroundColor=qmlDesignerButtonColor
+QmlDesigner_ScrollBarHandleColor=ff7a7a7a
+
+[Flags]
+ComboBoxDrawTextShadow=true
+DerivePaletteFromTheme=false
+DrawIndicatorBranch=false
+DrawSearchResultWidgetFrame=true
+DrawTargetSelectorBottom=true
+DrawToolBarHighlights=true
+DrawToolBarBorders=false
+ApplyThemePaletteGlobally=false
+FlatToolBars=false
+FlatSideBarIcons=false
+FlatProjectsMode=false
+FlatMenuBar=false
+ToolBarIconShadow=true
+WindowColorAsBase=false
+DarkUserInterface=false
+
+[Gradients]
+DetailsWidgetHeaderGradient\1\color=ffffffff
+DetailsWidgetHeaderGradient\1\pos=1
+DetailsWidgetHeaderGradient\size=1
diff --git a/rsc/theme/design-light.creatortheme b/rsc/theme/design-light.creatortheme
new file mode 100644
index 0000000000000000000000000000000000000000..70ee4462cb6fb7e60ecdb8cb473982116676fc24
--- /dev/null
+++ b/rsc/theme/design-light.creatortheme
@@ -0,0 +1,403 @@
+[General]
+ThemeName=Design Light
+PreferredStyles=Fusion
+
+
+[Palette]
+shadowBackground=ffd1cfcf
+text=ff000000
+textDisabled=55000000
+selectedBackgroundText=aa1f75cc
+toolBarItem=a0010508
+toolBarItemDisabled=38000000
+fancyBarsNormalTextColor=ff000000
+fancyBarsBoldTextColor=a0010508
+hoverBackground=1a000000
+selectedBackground=ffffffff
+normalBackground=ffffffff
+alternateBackground=ff515151
+stop_error=ffec7373
+run_success=ff52c23b
+error=ffdf4f4f
+warning=ffecbc1c
+splitter=ffbdbebf
+qmlDesignerButtonColor=fff8f8f8
+textColorLink=ff007af4
+textColorLinkVisited=ffa57aff
+backgroundColorDisabled=ff8e8e8e
+
+[Colors]
+;DS controls theme START
+DSpanelBackground=ffeaeaea
+
+DSinteraction=ff2aafd3
+DSerrorColor=ffdf3a3a
+DSdisabledColor=ff8e8e8e
+
+DScontrolBackground=ffeaeaea
+DScontrolBackgroundInteraction=ffc9c9c9
+DScontrolBackgroundDisabled=ffeaeaea
+DScontrolBackgroundGlobalHover=ffe5e5e5
+DScontrolBackgroundHover=ffd1d1d1
+
+DScontrolOutline=ffcecccc
+DScontrolOutlineInteraction=ff2aafd3
+DScontrolOutlineDisabled=ff707070
+
+DStextColor=ff262626
+DStextColorDisabled=ff707070
+DStextSelectionColor=ff2aafd3
+DStextSelectedTextColor=ff000000
+
+DSplaceholderTextColor=ff262626
+DSplaceholderTextColorInteraction=ffababab
+
+DSiconColor=ff262626
+DSiconColorHover=ff191919
+DSiconColorInteraction=ffffffff
+DSiconColorDisabled=ff707070
+DSiconColorSelected=ff2aafd3
+
+DSlinkIndicatorColor=ff808080
+DSlinkIndicatorColorHover=ff1f1f1f
+DSlinkIndicatorColorInteraction=ff2aafd3
+DSlinkIndicatorColorDisabled=ff707070
+
+DSpopupBackground=ffd3d3d3
+DSpopupOverlayColor=99191919
+
+DSsliderActiveTrack=ff7c7b7b
+DSsliderActiveTrackHover=ff000000
+DSsliderActiveTrackFocus=ffaaaaaa
+DSsliderInactiveTrack=ffaaaaaa
+DSsliderInactiveTrackHover=ff505050
+DSsliderInactiveTrackFocus=ff606060
+DSsliderHandle=ff1f1f1f
+DSsliderHandleHover=ff606060
+DSsliderHandleFocus=ff0492c9
+DSsliderHandleInteraction=ff2aafd3
+
+DSscrollBarTrack=ffb5b4b4
+DSscrollBarHandle=ff9b9b9b
+
+DSsectionHeadBackground=ffd8d8d8
+
+DSstateDefaultHighlight=ffffe400
+DSstateSeparatorColor=ffadadad
+DSstateBackgroundColor=ffe0e0e0
+DSstatePreviewOutline=ff363636
+
+DSchangedStateText=ff99ccff
+
+DS3DAxisXColor=ffd00000
+DS3DAxisYColor=ff009900
+DS3DAxisZColor=ff5050ff
+
+DSactionBinding=ff2aafd3
+DSactionAlias=fff93a3a
+DSactionKeyframe=ffe0e01b
+DSactionJIT=ff2db543
+
+DStableHeaderBackground=ffff0000
+DStableHeaderText=ff00ff00
+
+DSdockContainerBackground=ff323232
+DSdockContainerSplitter=ff323232
+DSdockAreaBackground=ff262728
+
+DSdockWidgetBackground=ff00ff00
+DSdockWidgetSplitter=ff595959
+DSdockWidgetTitleBar=ffeaeaea
+
+DStitleBarText=ffdadada
+DStitleBarIcon=ff4f5052
+DStitleBarButtonHover=40ffffff
+DStitleBarButtonPress=60ffffff
+
+DStabContainerBackground=ff0000ff
+DStabSplitter=ff595959
+
+DStabInactiveBackground=ff999999
+DStabInactiveText=ff262626
+DStabInactiveIcon=ffffffff
+DStabInactiveButtonHover=ff1f1f1f
+DStabInactiveButtonPress=ff1f1f1f
+
+DStabActiveBackground=ffdadada
+DStabActiveText=ff111111
+DStabActiveIcon=ff000000
+DStabActiveButtonHover=ffdadada
+DStabActiveButtonPress=ffdadada
+
+DStabFocusBackground=ff2aafd3
+DStabFocusText=ff111111
+DStabFocusIcon=ff000000
+DStabFocusButtonHover=ff2aafd3
+DStabFocusButtonPress=ff2aafd3
+
+DSnavigatorBranch=ff7c7b7b
+DSnavigatorBranchIndicator=ff7c7b7b
+DSnavigatorItemBackground=ffd8d8d8
+DSnavigatorItemBackgroundHover=ffc2c2c2
+DSnavigatorItemBackgroundSelected=ffffffff
+DSnavigatorText=ff262626
+DSnavigatorTextHover=ff1f1f1f
+DSnavigatorTextSelected=ff2aafd3
+DSnavigatorIcon=ff1f1f1f
+DSnavigatorIconHover=ff1f1f1f
+DSnavigatorIconSelected=ff7c7b7b
+DSnavigatorAliasIconChecked=ffff0000
+DSnavigatorDropIndicatorBackground=ff2aafd3
+DSnavigatorDropIndicatorOutline=ff2aafd3
+
+DSheaderViewBackground=ffd8d8d8
+DStableViewAlternateBackground=ff00ff00
+
+DStoolTipBackground=ff111111
+DStoolTipOutline=ffdadada
+DStoolTipText=ffdadada
+
+DSUnimportedModuleColor=ffe33c2e
+
+;DS controls theme END
+
+BackgroundColorAlternate=alternateBackground
+BackgroundColorDark=shadowBackground
+BackgroundColorHover=hoverBackground
+BackgroundColorNormal=normalBackground
+BackgroundColorDisabled=ff7a7a7a
+BackgroundColorSelected=selectedBackground
+BadgeLabelBackgroundColorChecked=ffe0e0e0
+BadgeLabelBackgroundColorUnchecked=ff808080
+BadgeLabelTextColorChecked=ff606060
+BadgeLabelTextColorUnchecked=ffffffff
+CanceledSearchTextColor=ff0000
+ComboBoxArrowColor=toolBarItem
+ComboBoxArrowColorDisabled=toolBarItemDisabled
+ComboBoxTextColor=fancyBarsNormalTextColor
+DetailsButtonBackgroundColorHover=b4ffffff
+DetailsWidgetBackgroundColor=28ffffff
+DockWidgetResizeHandleColor=splitter
+DoubleTabWidget1stSeparatorColor=ffff0000
+DoubleTabWidget1stTabActiveTextColor=ff000000
+DoubleTabWidget1stTabBackgroundColor=ffff0000
+DoubleTabWidget1stTabInactiveTextColor=ff555555
+DoubleTabWidget2ndSeparatorColor=ffff0000
+DoubleTabWidget2ndTabActiveTextColor=ffffffff
+DoubleTabWidget2ndTabBackgroundColor=ffff0000
+DoubleTabWidget2ndTabInactiveTextColor=ff000000
+EditorPlaceholderColor=fff4f4f4
+FancyToolBarSeparatorColor=toolBarItemDisabled
+FancyTabBarBackgroundColor=shadowBackground
+FancyTabBarSelectedBackgroundColor=selectedBackground
+FancyTabWidgetDisabledSelectedTextColor=toolBarItemDisabled
+FancyTabWidgetDisabledUnselectedTextColor=toolBarItemDisabled
+FancyTabWidgetEnabledSelectedTextColor=fancyBarsBoldTextColor
+FancyTabWidgetEnabledUnselectedTextColor=fancyBarsBoldTextColor
+FancyToolButtonHoverColor=hoverBackground
+FancyToolButtonSelectedColor=selectedBackground
+FutureProgressBackgroundColor=shadowBackground
+IconsBaseColor=toolBarItem
+IconsDisabledColor=toolBarItemDisabled
+IconsInfoColor=ff3099dc
+IconsInfoToolBarColor=ff3099dc
+IconsWarningColor=warning
+IconsWarningToolBarColor=ffecbc1c
+IconsErrorColor=error
+IconsErrorToolBarColor=ffdf4f4f
+IconsRunColor=run_success
+IconsRunToolBarColor=run_success
+IconsStopColor=stop_error
+IconsStopToolBarColor=stop_error
+IconsInterruptColor=ff587ff7
+IconsInterruptToolBarColor=ff6a7bc3
+IconsDebugColor=toolBarItem
+IconsNavigationArrowsColor=ff3dabe6
+IconsBuildHammerHandleColor=ffc26b14
+IconsBuildHammerHeadColor=ff868687
+IconsModeWelcomeActiveColor=ff5caa15
+IconsModeEditActiveColor=ff6a6add
+IconsModeDesignActiveColor=ffbb6000
+IconsModeDebugActiveColor=ff6a6add
+IconsModeProjectActiveColor=ff5caa15
+IconsModeAnalyzeActiveColor=ff43adee
+IconsModeHelpActiveColor=fffaa838
+IconsCodeModelKeywordColor=ff777777
+IconsCodeModelClassColor=ffc0b550
+IconsCodeModelStructColor=ff53b053
+IconsCodeModelFunctionColor=ffd34373
+IconsCodeModelVariableColor=ff2bbbcc
+IconsCodeModelEnumColor=ffc0b550
+IconsCodeModelMacroColor=ff476ba0
+IconsCodeModelAttributeColor=ff316511
+IconsCodeModelUniformColor=ff994899
+IconsCodeModelVaryingColor=ffa08833
+IconsCodeModelOverlayBackgroundColor=70ffffff
+IconsCodeModelOverlayForegroundColor=ff232425
+InfoBarBackground=ffffffe1
+InfoBarText=text
+MenuBarEmptyAreaBackgroundColor=shadowBackground
+MenuBarItemBackgroundColor=shadowBackground
+MenuBarItemTextColorDisabled=textDisabled
+MenuBarItemTextColorNormal=text
+MenuItemTextColorDisabled=textDisabled
+MenuItemTextColorNormal=text
+MiniProjectTargetSelectorBackgroundColor=shadowBackground
+MiniProjectTargetSelectorBorderColor=shadowBackground
+MiniProjectTargetSelectorSummaryBackgroundColor=shadowBackground
+MiniProjectTargetSelectorTextColor=fancyBarsNormalTextColor
+PanelStatusBarBackgroundColor=shadowBackground
+PanelsWidgetSeparatorLineColor=000000
+PanelTextColorDark=text
+PanelTextColorMid=ff666666
+PanelTextColorLight=fancyBarsNormalTextColor
+ProgressBarColorError=error
+ProgressBarColorFinished=run_success
+ProgressBarColorNormal=ff888888
+ProgressBarTitleColor=fancyBarsBoldTextColor
+ProgressBarBackgroundColor=28000000
+SplitterColor=splitter
+TextColorDisabled=textDisabled
+TextColorError=ffff4040
+TextColorHighlight=ffff0000
+TextColorHighlightBackground=ffef0b
+TextColorLink=ff007af4
+TextColorLinkVisited=ffa57aff
+TextColorNormal=text
+ToggleButtonBackgroundColor=shadowBackground
+ToolBarBackgroundColor=shadowBackground
+TreeViewArrowColorNormal=hoverBackground
+TreeViewArrowColorSelected=text
+
+OutputPanes_DebugTextColor=text
+OutputPanes_ErrorMessageTextColor=ffaa0000
+OutputPanes_MessageOutput=ff0000aa
+OutputPanes_NormalMessageTextColor=ff0000aa
+OutputPanes_StdErrTextColor=ffaa0000
+OutputPanes_StdOutTextColor=ff000000
+OutputPanes_WarningMessageTextColor=ff808000
+OutputPanes_TestPassTextColor=ff009900
+OutputPanes_TestFailTextColor=ffa00000
+OutputPanes_TestXFailTextColor=ff28f028
+OutputPanes_TestXPassTextColor=fff02828
+OutputPanes_TestSkipTextColor=ff787878
+OutputPanes_TestWarnTextColor=ffd0bb00
+OutputPanes_TestFatalTextColor=ff640000
+OutputPanes_TestDebugTextColor=ff329696
+OutputPaneButtonFlashColor=ffff0000
+OutputPaneToggleButtonTextColorChecked=fancyBarsNormalTextColor
+OutputPaneToggleButtonTextColorUnchecked=fancyBarsNormalTextColor
+
+Debugger_LogWindow_LogInput=ff00acac
+Debugger_LogWindow_LogStatus=ff00875a
+Debugger_LogWindow_LogTime=ffbf0303
+
+Debugger_WatchItem_ValueNormal=text
+Debugger_WatchItem_ValueInvalid=textDisabled
+Debugger_WatchItem_ValueChanged=ffbf0303
+
+Debugger_Breakpoint_TextMarkColor=ffff4040
+
+Welcome_TextColor=ff000000
+Welcome_ForegroundPrimaryColor=ff404244
+Welcome_ForegroundSecondaryColor=ff727476
+Welcome_BackgroundColor=normalBackground
+Welcome_ButtonBackgroundColor=normalBackground
+Welcome_DividerColor=ffd6d6d6
+Welcome_HoverColor=fff6f6f6
+Welcome_LinkColor=ff5caa15
+Welcome_DisabledLinkColor=textDisabled
+
+Timeline_TextColor=text
+Timeline_BackgroundColor1=normalBackground
+Timeline_BackgroundColor2=fff6f6f6
+Timeline_DividerColor=ffd6d6d6
+Timeline_HighlightColor=ff3099dc
+Timeline_PanelBackgroundColor=ffd6d6d6
+Timeline_PanelHeaderColor=ff888888
+Timeline_HandleColor=ff888888
+Timeline_RangeColor=selectedBackground
+
+VcsBase_FileStatusUnknown_TextColor=ff000000
+VcsBase_FileAdded_TextColor=ff00aa00
+VcsBase_FileModified_TextColor=ff0000ee
+VcsBase_FileDeleted_TextColor=ff800000
+VcsBase_FileRenamed_TextColor=ffd77d00
+VcsBase_FileUnmerged_TextColor=ffee0000
+
+Bookmarks_TextMarkColor=ffa0a0ff
+
+TextEditor_SearchResult_ScrollBarColor=ff00c000
+TextEditor_CurrentLine_ScrollBarColor=ff404040
+
+ProjectExplorer_TaskError_TextMarkColor=error
+ProjectExplorer_TaskWarn_TextMarkColor=warning
+
+CodeModel_Error_TextMarkColor=error
+CodeModel_Warning_TextMarkColor=warning
+
+;new colors
+QmlDesigner_BackgroundColor=qmlDesignerButtonColor
+QmlDesigner_HighlightColor=ff0492c9
+QmlDesigner_FormEditorSelectionColor=ffd3299a
+QmlDesigner_FormEditorForegroundColor=ffffffff
+QmlDesigner_BackgroundColorDarkAlternate=ffeaeaea
+QmlDesigner_BackgroundColorDarker=fff5f5f5
+QmlDesigner_BorderColor=splitter
+QmlDesigner_ButtonColor=f0f0f0
+QmlDesigner_TabDark=ff63676b
+QmlDesigner_TabLight=ffffffff
+QmlDesigner_FormeditorBackgroundColor=qmlDesignerButtonColor
+QmlDesigner_AlternateBackgroundColor=qmlDesignerButtonColor
+
+;extra_new_colors
+QmlDesigner_FormeditorBackgroundColor=ff000000
+QmlDesigner_AlternateBackgroundColor=ffc14fc1
+QmlDesigner_ScrollBarHandleColor=ff8b8e8f
+
+;palette colors
+
+PaletteWindow=selectedBackground
+PaletteWindowText=text
+PaletteBase=normalBackground
+PaletteAlternateBase=alternateBackground
+PaletteButton=selectedBackground
+PaletteBrightText=selectedBackgroundText
+PaletteText=text
+PaletteButtonText=text
+PaletteButtonTextDisabled=textDisabled
+PaletteToolTipBase=selectedBackground
+
+PaletteHighlight=ff0492c9
+
+PaletteDark=shadowBackground
+PaletteHighlightedText=ffffffff
+PaletteToolTipText=text
+PaletteLink=textColorLink
+PaletteLinkVisited=textColorLinkVisited
+PaletteWindowDisabled=backgroundColorDisabled
+PaletteWindowTextDisabled=textDisabled
+PaletteBaseDisabled=backgroundColorDisabled
+PaletteTextDisabled=textDisabled
+
+[Flags]
+ComboBoxDrawTextShadow=false
+DerivePaletteFromTheme=true
+DrawIndicatorBranch=true
+DrawSearchResultWidgetFrame=false
+DrawTargetSelectorBottom=false
+DrawToolBarHighlights=false
+DrawToolBarBorders=true
+ApplyThemePaletteGlobally=true
+FlatToolBars=true
+FlatSideBarIcons=true
+FlatProjectsMode=false
+FlatMenuBar=true
+ToolBarIconShadow=false
+WindowColorAsBase=false
+DarkUserInterface=false
+
+[Gradients]
+DetailsWidgetHeaderGradient\1\color=00000000
+DetailsWidgetHeaderGradient\1\pos=1
+DetailsWidgetHeaderGradient\size=1
diff --git a/rsc/theme/design.creatortheme b/rsc/theme/design.creatortheme
new file mode 100644
index 0000000000000000000000000000000000000000..daffe924a6ae63149a9e5262c415c9f80e104356
--- /dev/null
+++ b/rsc/theme/design.creatortheme
@@ -0,0 +1,503 @@
+[General]
+ThemeName=Design Dark
+PreferredStyles=Fusion
+DefaultTextEditorColorScheme=creator-dark.xml
+
+[Palette]
+shadowBackground=ff1f1f1f
+text=ffdadada
+textDisabled=60a4a6a8
+selectedBackgroundText=aa1f75cc
+toolBarItem=ffb3b3b3
+toolBarItemDisabled=ff686868
+fancyBarsNormalTextColor=ffd0d0d0
+fancyBarsBoldTextColor=b6fbfdff
+hoverBackground=ff404244
+selectedBackground=ff111111
+normalBackground=ff262728
+alternateBackground=ff353637
+error=ffdf4f4f
+warning=ffecbc1c
+splitter=ff474747
+textColorLink=ff007af4
+textColorLinkVisited=ffa57aff
+backgroundColorDisabled=ff444444
+
+[Colors]
+;DS controls theme START
+DSpanelBackground=ff323232
+
+DSinteraction=ff2aafd3
+DSerrorColor=ffdf3a3a
+DSdisabledColor=ff707070
+
+DScontrolBackground=ff323232
+DScontrolBackgroundInteraction=ff595959
+DScontrolBackgroundDisabled=ff323232
+DScontrolBackgroundGlobalHover=ff474747
+DScontrolBackgroundHover=ff666666
+
+DScontrolOutline=ff1f1f1f
+DScontrolOutlineInteraction=ff2aafd3
+DScontrolOutlineDisabled=ff707070
+
+DStextColor=ffffffff
+DStextColorDisabled=ff707070
+DStextSelectionColor=ff2aafd3
+DStextSelectedTextColor=ff000000
+
+DSplaceholderTextColor=ffffffff
+DSplaceholderTextColorInteraction=ffababab
+
+DSiconColor=ffffffff
+DSiconColorHover=ff262626
+DSiconColorInteraction=ff707070
+DSiconColorDisabled=ff707070
+DSiconColorSelected=ff2aafd3
+
+DSlinkIndicatorColor=ff808080
+DSlinkIndicatorColorHover=ffffffff
+DSlinkIndicatorColorInteraction=ff2aafd3
+DSlinkIndicatorColorDisabled=ff707070
+
+DSpopupBackground=ff474747
+DSpopupOverlayColor=99191919
+
+DSsliderActiveTrack=ff7c7b7b
+DSsliderActiveTrackHover=ff000000
+DSsliderActiveTrackFocus=ffaaaaaa
+DSsliderInactiveTrack=ff595959
+DSsliderInactiveTrackHover=ff505050
+DSsliderInactiveTrackFocus=ff606060
+DSsliderHandle=ff1f1f1f
+DSsliderHandleHover=ff606060
+DSsliderHandleFocus=ff0492c9
+DSsliderHandleInteraction=ff2aafd3
+
+DSscrollBarTrack=ff404040
+DSscrollBarHandle=ff505050
+
+DSsectionHeadBackground=ff1f1f1f
+
+DSstateDefaultHighlight=ffffe400
+DSstateSeparatorColor=ff7c7b7b
+DSstateBackgroundColor=ff383838
+DSstatePreviewOutline=ffaaaaaa
+
+DSchangedStateText=ff99ccff
+
+DS3DAxisXColor=ffd00000
+DS3DAxisYColor=ff009900
+DS3DAxisZColor=ff5050ff
+
+DSactionBinding=ff2aafd3
+DSactionAlias=fff93a3a
+DSactionKeyframe=ffe0e01b
+DSactionJIT=ff2db543
+
+DStableHeaderBackground=ffff0000
+DStableHeaderText=ff00ff00
+
+DSdockContainerBackground=ff323232
+DSdockContainerSplitter=ff323232
+DSdockAreaBackground=ff262728
+
+DSdockWidgetBackground=ff00ff00
+DSdockWidgetSplitter=ff595959
+DSdockWidgetTitleBar=ff1f1f1f
+
+DStitleBarText=ffdadada
+DStitleBarIcon=ffffffff
+DStitleBarButtonHover=40ffffff
+DStitleBarButtonPress=60ffffff
+
+DStabContainerBackground=ff0000ff
+DStabSplitter=ff595959
+
+DStabInactiveBackground=ff1f1f1f
+DStabInactiveText=ffdadada
+DStabInactiveIcon=ffffffff
+DStabInactiveButtonHover=ff1f1f1f
+DStabInactiveButtonPress=ff1f1f1f
+
+DStabActiveBackground=ffdadada
+DStabActiveText=ff111111
+DStabActiveIcon=ff000000
+DStabActiveButtonHover=ffdadada
+DStabActiveButtonPress=ffdadada
+
+DStabFocusBackground=ff2aafd3
+DStabFocusText=ff111111
+DStabFocusIcon=ff000000
+DStabFocusButtonHover=ff2aafd3
+DStabFocusButtonPress=ff2aafd3
+
+DSnavigatorBranch=ff7c7b7b
+DSnavigatorBranchIndicator=ff7c7b7b
+DSnavigatorItemBackground=ff262626
+DSnavigatorItemBackgroundHover=ff666666
+DSnavigatorItemBackgroundSelected=ff1f1f1f
+DSnavigatorText=ffffffff
+DSnavigatorTextHover=ff1f1f1f
+DSnavigatorTextSelected=ff2aafd3
+DSnavigatorIcon=ffffffff
+DSnavigatorIconHover=ff1f1f1f
+DSnavigatorIconSelected=ff7c7b7b
+DSnavigatorAliasIconChecked=ffff0000
+DSnavigatorDropIndicatorBackground=ff2aafd3
+DSnavigatorDropIndicatorOutline=ff2aafd3
+
+DSheaderViewBackground=ff1f1f1f
+DStableViewAlternateBackground=ff00ff00
+
+DStoolTipBackground=ff111111
+DStoolTipOutline=ffdadada
+DStoolTipText=ffdadada
+
+DSUnimportedModuleColor=ffe33c2e
+
+;DS controls theme END
+
+BackgroundColorAlternate=alternateBackground
+BackgroundColorDark=shadowBackground
+BackgroundColorHover=hoverBackground
+BackgroundColorNormal=normalBackground
+BackgroundColorDisabled=backgroundColorDisabled
+BackgroundColorSelected=selectedBackground
+BadgeLabelBackgroundColorChecked=ffe0e0e0
+BadgeLabelBackgroundColorUnchecked=ff808080
+BadgeLabelTextColorChecked=ff606060
+BadgeLabelTextColorUnchecked=ffffffff
+CanceledSearchTextColor=ff0000
+ComboBoxArrowColor=toolBarItem
+ComboBoxArrowColorDisabled=toolBarItemDisabled
+ComboBoxTextColor=fancyBarsNormalTextColor
+DetailsButtonBackgroundColorHover=22ffffff
+DetailsWidgetBackgroundColor=18ffffff
+DockWidgetResizeHandleColor=splitter
+DoubleTabWidget1stSeparatorColor=splitter
+DoubleTabWidget1stTabActiveTextColor=text
+DoubleTabWidget1stTabBackgroundColor=ff505050
+DoubleTabWidget1stTabInactiveTextColor=text
+DoubleTabWidget2ndSeparatorColor=toolBarItemDisabled
+DoubleTabWidget2ndTabActiveTextColor=text
+DoubleTabWidget2ndTabBackgroundColor=selectedBackground
+DoubleTabWidget2ndTabInactiveTextColor=text
+EditorPlaceholderColor=normalBackground
+FancyToolBarSeparatorColor=toolBarItemDisabled
+FancyTabBarBackgroundColor=shadowBackground
+FancyTabBarSelectedBackgroundColor=ff000000
+FancyTabWidgetDisabledSelectedTextColor=toolBarItemDisabled
+FancyTabWidgetDisabledUnselectedTextColor=toolBarItemDisabled
+FancyTabWidgetEnabledSelectedTextColor=fancyBarsBoldTextColor
+FancyTabWidgetEnabledUnselectedTextColor=fancyBarsBoldTextColor
+FancyToolButtonHoverColor=hoverBackground
+FancyToolButtonSelectedColor=selectedBackground
+FutureProgressBackgroundColor=shadowBackground
+IconsBaseColor=toolBarItem
+IconsDisabledColor=toolBarItemDisabled
+IconsInfoColor=ff3099dc
+IconsInfoToolBarColor=ff71b2db
+IconsWarningColor=warning
+IconsWarningToolBarColor=fff2d76e
+IconsErrorColor=error
+IconsErrorToolBarColor=ffdb6f71
+IconsRunColor=ff6da838
+IconsRunToolBarColor=ff93be6c
+IconsStopColor=ffee6969
+IconsStopToolBarColor=ffe27f7f
+IconsInterruptColor=ff587ff7
+IconsInterruptToolBarColor=ff6a7bc3
+IconsDebugColor=toolBarItem
+IconsNavigationArrowsColor=ffebc322
+IconsBuildHammerHandleColor=ffb06112
+IconsBuildHammerHeadColor=ff828384
+IconsModeWelcomeActiveColor=ff1f6c97
+IconsModeEditActiveColor=ff99aaef
+IconsModeDesignActiveColor=ffbb6000
+IconsModeDebugActiveColor=ff99aaef
+IconsModeProjectActiveColor=ff1f6c97
+IconsModeAnalyzeActiveColor=ff43adee
+IconsModeHelpActiveColor=fff4be04
+IconsCodeModelKeywordColor=ff777777
+IconsCodeModelClassColor=ffc0b550
+IconsCodeModelStructColor=ff53b053
+IconsCodeModelFunctionColor=ffd34373
+IconsCodeModelVariableColor=ff2bbbcc
+IconsCodeModelEnumColor=ffc0b550
+IconsCodeModelMacroColor=ff476ba0
+IconsCodeModelAttributeColor=ff316511
+IconsCodeModelUniformColor=ff994899
+IconsCodeModelVaryingColor=ffa08833
+IconsCodeModelOverlayBackgroundColor=70000000
+IconsCodeModelOverlayForegroundColor=ffd0d0d0
+InfoBarBackground=ff505000
+InfoBarText=text
+MenuBarEmptyAreaBackgroundColor=shadowBackground
+MenuBarItemBackgroundColor=shadowBackground
+MenuBarItemTextColorDisabled=textDisabled
+MenuBarItemTextColorNormal=text
+MenuItemTextColorDisabled=textDisabled
+MenuItemTextColorNormal=text
+MiniProjectTargetSelectorBackgroundColor=shadowBackground
+MiniProjectTargetSelectorBorderColor=shadowBackground
+MiniProjectTargetSelectorSummaryBackgroundColor=shadowBackground
+MiniProjectTargetSelectorTextColor=fancyBarsNormalTextColor
+PanelStatusBarBackgroundColor=shadowBackground
+PanelsWidgetSeparatorLineColor=000000
+PanelTextColorDark=text
+PanelTextColorMid=ffa0a0a0
+PanelTextColorLight=text
+ProgressBarColorError=error
+ProgressBarColorFinished=dda4d576
+ProgressBarColorNormal=ff999999
+ProgressBarTitleColor=fancyBarsBoldTextColor
+ProgressBarBackgroundColor=a0606060
+SplitterColor=splitter
+TextColorDisabled=textDisabled
+TextColorError=ffff4040
+TextColorHighlight=ffff0000
+TextColorHighlightBackground=8a7f2c
+TextColorLink=textColorLink
+TextColorLinkVisited=textColorLinkVisited
+TextColorNormal=text
+ToggleButtonBackgroundColor=shadowBackground
+ToolBarBackgroundColor=shadowBackground
+TreeViewArrowColorNormal=hoverBackground
+TreeViewArrowColorSelected=text
+
+OutputPanes_DebugTextColor=text
+OutputPanes_ErrorMessageTextColor=ffff6c6c
+OutputPanes_MessageOutput=ff008787
+OutputPanes_NormalMessageTextColor=ff008787
+OutputPanes_StdErrTextColor=ffff6666
+OutputPanes_StdOutTextColor=text
+OutputPanes_WarningMessageTextColor=fff3c300
+OutputPanes_TestPassTextColor=ff00b400
+OutputPanes_TestFailTextColor=ffcf4848
+OutputPanes_TestXFailTextColor=ff28dc28
+OutputPanes_TestXPassTextColor=ffdc2828
+OutputPanes_TestSkipTextColor=ff828282
+OutputPanes_TestWarnTextColor=ffc8c800
+OutputPanes_TestFatalTextColor=ffc82828
+OutputPanes_TestDebugTextColor=ff329696
+OutputPaneButtonFlashColor=error
+OutputPaneToggleButtonTextColorChecked=fancyBarsNormalTextColor
+OutputPaneToggleButtonTextColorUnchecked=fancyBarsNormalTextColor
+
+Debugger_LogWindow_LogInput=ff00acac
+Debugger_LogWindow_LogStatus=ff00875a
+Debugger_LogWindow_LogTime=ffbf0303
+
+Debugger_WatchItem_ValueNormal=text
+Debugger_WatchItem_ValueInvalid=textDisabled
+Debugger_WatchItem_ValueChanged=ffff6666
+
+Debugger_Breakpoint_TextMarkColor=ffff4040
+
+Welcome_TextColor=text
+Welcome_ForegroundPrimaryColor=ff999999
+Welcome_ForegroundSecondaryColor=ff808080
+Welcome_BackgroundColor=normalBackground
+Welcome_ButtonBackgroundColor=normalBackground
+Welcome_DividerColor=ff555555
+Welcome_HoverColor=ff444444
+Welcome_LinkColor=ff7fc63c
+Welcome_DisabledLinkColor=textDisabled
+
+Timeline_TextColor=text
+Timeline_BackgroundColor1=normalBackground
+Timeline_BackgroundColor2=ff444444
+Timeline_DividerColor=ff555555
+Timeline_HighlightColor=ff3099dc
+Timeline_PanelBackgroundColor=ff808080
+Timeline_PanelHeaderColor=alternateBackground
+Timeline_HandleColor=alternateBackground
+Timeline_RangeColor=selectedBackground
+
+VcsBase_FileStatusUnknown_TextColor=text
+VcsBase_FileAdded_TextColor=ff00ff00
+VcsBase_FileModified_TextColor=ff8ee0ff
+VcsBase_FileDeleted_TextColor=fffff6c6c
+VcsBase_FileRenamed_TextColor=ffffa500
+VcsBase_FileUnmerged_TextColor=ffff4040
+
+Bookmarks_TextMarkColor=ff8080ff
+
+TextEditor_SearchResult_ScrollBarColor=ff00c000
+TextEditor_CurrentLine_ScrollBarColor=ffffffff
+
+ProjectExplorer_TaskError_TextMarkColor=error
+ProjectExplorer_TaskWarn_TextMarkColor=warning
+
+CodeModel_Error_TextMarkColor=error
+CodeModel_Warning_TextMarkColor=warning
+
+
+;Designer Main colors
+
+; Design View panel backgrounds - Library, Form Viewer, Properties, States, Timeline & Connections. - lOOKS LIKE IS NO LONGER USED.
+QmlDesigner_BackgroundColor=ff4c4e50
+;QmlDesigner_BackgroundColor=ffd3299a
+
+
+; Design View selected items - Navigator Selection, Timeline Property Selection, TImeline bar, property text highlighted
+QmlDesigner_HighlightColor=ff0492c9
+
+
+
+; live selection color for the form editors smart guides.
+;QmlDesigner_FormEditorSelectionColor=ff4ba2ff
+QmlDesigner_FormEditorSelectionColor=ffd3299a
+
+
+;color for lable text in form editor
+QmlDesigner_FormEditorForegroundColor=ffdadada
+;QmlDesigner_FormEditorForegroundColor=ffd3299a
+
+
+
+
+;new colors
+
+;background color for main form view, library, navigator, properties, connections
+;QmlDesigner_BackgroundColorDarkAlternate=ff4c4e50
+QmlDesigner_BackgroundColorDarkAlternate=ff323232
+
+;filter outlines, override W/H outlines, properties spinbox background, timeline separators.
+;QmlDesigner_BackgroundColorDarker=ff262728
+QmlDesigner_BackgroundColorDarker=ff191919
+
+;properties outlines, states thumbnail outlines, add state button outlines.
+;QmlDesigner_BorderColor=splitter
+QmlDesigner_BorderColor=ff353535
+
+;background sqaures for components, effects, etc. Handles for scrollbars, type background in properties.
+;QmlDesigner_ButtonColor=ff595b5c
+QmlDesigner_ButtonColor=ff353535
+
+
+;non - selected tabs, text color for selected tabs.
+;QmlDesigner_TabDark=shadowBackground
+QmlDesigner_TabDark=ff111111
+
+
+;active tab backgrounds and non selected tab text.
+QmlDesigner_TabLight=text
+;QmlDesigner_TabLight=ff262626
+
+;extra_new_colors
+
+
+QmlDesigner_FormeditorBackgroundColor=ff000000
+
+QmlDesigner_AlternateBackgroundColor=ffc14fc1
+
+QmlDesigner_ScrollBarHandleColor=ff595b5c
+
+
+
+;palette colors
+
+;outline colors on the combo box, zoom slider, dialog outlines, on loading project the whole screen flashes this color
+;PaletteWindow=normalBackground
+PaletteWindow=ff262626
+
+
+
+; item text for search results panel and application output panel, twirl down triangles in edit mode, text editor.
+PaletteWindowText=text
+;PaletteWindowText=ffd3299a
+
+
+;Search bar background, edit view project list background, style combo box background
+PaletteBase=normalBackground
+;PaletteBase=ff191919
+
+
+
+;can't see where this is used.
+PaletteAlternateBase=alternateBackground
+;PaletteAlternateBase=ffd3299a
+
+
+
+;flow tag backgrounds, import combobox background, edit mode scrollbars
+;PaletteButton=shadowBackground
+PaletteButton=ff262626
+
+
+
+;alternate text in the search and application output panel - NO LONGER SEEMS TO DO ANYTHING
+PaletteBrightText=ffff3333
+;PaletteBrightText=ffd3299a
+
+
+; text inside dropdown combo boxes, styles, connections.
+PaletteText=text
+;PaletteText=ffd3299a
+
+
+
+; text for ticks for tick boxes.
+PaletteButtonText=text
+;PaletteButtonText=ffd3299a
+
+
+PaletteButtonTextDisabled=textDisabled
+
+
+;background color for the tool tip hover background
+PaletteToolTipBase=selectedBackground
+;PaletteToolTipBase=ffd3299a
+
+
+;the selection highlight on the dropdown combo box in the file selection top menu and connections panel and tab mode selector dropdowns
+PaletteHighlight=selectedBackgroundText
+
+
+; outline of warning in editor mode, underline of "open a document" page in the edit mode
+PaletteDark=shadowBackground
+;PaletteDark=ffd3299a
+
+
+;selected text highlight in edit and design studio modes
+PaletteHighlightedText=ffffffff
+;PaletteHighlightedText=ffd3299a
+
+
+; text for for floating tool tips
+PaletteToolTipText=text
+;PaletteToolTipText=ffd3299a
+
+
+PaletteLink=textColorLink
+PaletteLinkVisited=textColorLinkVisited
+PaletteWindowDisabled=backgroundColorDisabled
+PaletteWindowTextDisabled=textDisabled
+PaletteBaseDisabled=backgroundColorDisabled
+PaletteTextDisabled=textDisabled
+PaletteMid=ffafafaf
+PalettePlaceholderText=ff808081
+
+[Flags]
+ComboBoxDrawTextShadow=false
+DerivePaletteFromTheme=true
+DrawIndicatorBranch=true
+DrawSearchResultWidgetFrame=false
+DrawTargetSelectorBottom=false
+DrawToolBarHighlights=false
+DrawToolBarBorders=false
+ApplyThemePaletteGlobally=true
+FlatToolBars=true
+FlatSideBarIcons=true
+FlatProjectsMode=true
+FlatMenuBar=true
+ToolBarIconShadow=true
+WindowColorAsBase=false
+DarkUserInterface=true
+
+[Gradients]
+DetailsWidgetHeaderGradient\1\color=00000000
+DetailsWidgetHeaderGradient\1\pos=1
+DetailsWidgetHeaderGradient\size=1
diff --git a/rsc/theme/flat-dark.creatortheme b/rsc/theme/flat-dark.creatortheme
new file mode 100644
index 0000000000000000000000000000000000000000..6265003f1df32e9495cb6f2ee12b181c3655c540
--- /dev/null
+++ b/rsc/theme/flat-dark.creatortheme
@@ -0,0 +1,395 @@
+[General]
+ThemeName=Flat Dark
+PreferredStyles=Fusion
+DefaultTextEditorColorScheme=creator-dark.xml
+
+[Palette]
+shadowBackground=ff404142
+text=ffd0d0d0
+textDisabled=60a4a6a8
+textHighlighted=fff0f0f0
+toolBarItem=bcfbfdff
+toolBarItemDisabled=56a5a6a7
+fancyBarsNormalTextColor=ffd0d0d0
+fancyBarsBoldTextColor=b6fbfdff
+hoverBackground=28ffffff
+selectedBackground=7a000000
+selectedBackgroundText=ff1d545c
+normalBackground=ff2E2F30
+alternateBackground=ff353637
+error=ffdf4f4f
+warning=ffecbc1c
+splitter=ff06080A
+textColorLink=ff007af4
+textColorLinkVisited=ffa57aff
+backgroundColorDisabled=ff444444
+qmlDesignerButtonColor=ff4c4e50
+
+[Colors]
+;DS controls theme START
+DSpanelBackground=ff323232
+
+DSinteraction=ff2aafd3
+DSerrorColor=ffdf3a3a
+DSdisabledColor=ff707070
+
+DScontrolBackground=ff323232
+DScontrolBackgroundInteraction=ff595959
+DScontrolBackgroundDisabled=ff323232
+DScontrolBackgroundGlobalHover=ff474747
+DScontrolBackgroundHover=ff666666
+
+DScontrolOutline=ff1f1f1f
+DScontrolOutlineInteraction=ff2aafd3
+DScontrolOutlineDisabled=ff707070
+
+DStextColor=ffffffff
+DStextColorDisabled=ff707070
+DStextSelectionColor=ff2aafd3
+DStextSelectedTextColor=ff000000
+
+DSplaceholderTextColor=ffffffff
+DSplaceholderTextColorInteraction=ffababab
+
+DSiconColor=ffffffff
+DSiconColorHover=ff262626
+DSiconColorInteraction=ff707070
+DSiconColorDisabled=ff707070
+DSiconColorSelected=ff2aafd3
+
+DSlinkIndicatorColor=ff808080
+DSlinkIndicatorColorHover=ffffffff
+DSlinkIndicatorColorInteraction=ff2aafd3
+DSlinkIndicatorColorDisabled=ff707070
+
+DSpopupBackground=ff474747
+DSpopupOverlayColor=99191919
+
+DSsliderActiveTrack=ff7c7b7b
+DSsliderActiveTrackHover=ff000000
+DSsliderActiveTrackFocus=ffaaaaaa
+DSsliderInactiveTrack=ff595959
+DSsliderInactiveTrackHover=ff505050
+DSsliderInactiveTrackFocus=ff606060
+DSsliderHandle=ff1f1f1f
+DSsliderHandleHover=ff606060
+DSsliderHandleFocus=ff0492c9
+DSsliderHandleInteraction=ff2aafd3
+
+DSscrollBarTrack=ff404040
+DSscrollBarHandle=ff505050
+
+DSsectionHeadBackground=ff1f1f1f
+
+DSstateDefaultHighlight=ffffe400
+DSstateSeparatorColor=ff7c7b7b
+DSstateBackgroundColor=ff383838
+DSstatePreviewOutline=ffaaaaaa
+
+DSchangedStateText=ff99ccff
+
+DS3DAxisXColor=ffd00000
+DS3DAxisYColor=ff009900
+DS3DAxisZColor=ff5050ff
+
+DSactionBinding=ff2aafd3
+DSactionAlias=fff93a3a
+DSactionKeyframe=ffe0e01b
+DSactionJIT=ff2db543
+
+DStableHeaderBackground=ffff0000
+DStableHeaderText=ff00ff00
+
+DSdockContainerBackground=ff323232
+DSdockContainerSplitter=ff323232
+DSdockAreaBackground=ff262728
+
+DSdockWidgetBackground=ff00ff00
+DSdockWidgetSplitter=ff595959
+DSdockWidgetTitleBar=ff1f1f1f
+
+DStitleBarText=ffdadada
+DStitleBarIcon=ffffffff
+DStitleBarButtonHover=40ffffff
+DStitleBarButtonPress=60ffffff
+
+DStabContainerBackground=ff0000ff
+DStabSplitter=ff595959
+
+DStabInactiveBackground=ff1f1f1f
+DStabInactiveText=ffdadada
+DStabInactiveIcon=ffffffff
+DStabInactiveButtonHover=ff1f1f1f
+DStabInactiveButtonPress=ff1f1f1f
+
+DStabActiveBackground=ffdadada
+DStabActiveText=ff111111
+DStabActiveIcon=ff000000
+DStabActiveButtonHover=ffdadada
+DStabActiveButtonPress=ffdadada
+
+DStabFocusBackground=ff2aafd3
+DStabFocusText=ff111111
+DStabFocusIcon=ff000000
+DStabFocusButtonHover=ff2aafd3
+DStabFocusButtonPress=ff2aafd3
+
+DSnavigatorBranch=ff7c7b7b
+DSnavigatorBranchIndicator=ff7c7b7b
+DSnavigatorItemBackground=ff262626
+DSnavigatorItemBackgroundHover=ff666666
+DSnavigatorItemBackgroundSelected=ff1f1f1f
+DSnavigatorText=ffffffff
+DSnavigatorTextHover=ff1f1f1f
+DSnavigatorTextSelected=ff2aafd3
+DSnavigatorIcon=ffffffff
+DSnavigatorIconHover=ff1f1f1f
+DSnavigatorIconSelected=ff7c7b7b
+DSnavigatorAliasIconChecked=ffff0000
+DSnavigatorDropIndicatorBackground=ff2aafd3
+DSnavigatorDropIndicatorOutline=ff2aafd3
+
+DSheaderViewBackground=ff1f1f1f
+DStableViewAlternateBackground=ff00ff00
+
+DStoolTipBackground=ff111111
+DStoolTipOutline=ffdadada
+DStoolTipText=ffdadada
+
+DSUnimportedModuleColor=ffe33c2e
+
+;DS controls theme END
+
+BackgroundColorAlternate=alternateBackground
+BackgroundColorDark=shadowBackground
+BackgroundColorHover=hoverBackground
+BackgroundColorNormal=normalBackground
+BackgroundColorDisabled=backgroundColorDisabled
+BackgroundColorSelected=selectedBackground
+BadgeLabelBackgroundColorChecked=ffe0e0e0
+BadgeLabelBackgroundColorUnchecked=ff808080
+BadgeLabelTextColorChecked=ff606060
+BadgeLabelTextColorUnchecked=ffffffff
+CanceledSearchTextColor=ff0000
+ComboBoxArrowColor=toolBarItem
+ComboBoxArrowColorDisabled=toolBarItemDisabled
+ComboBoxTextColor=fancyBarsNormalTextColor
+DetailsButtonBackgroundColorHover=22ffffff
+DetailsWidgetBackgroundColor=18ffffff
+DockWidgetResizeHandleColor=splitter
+DoubleTabWidget1stSeparatorColor=splitter
+DoubleTabWidget1stTabActiveTextColor=text
+DoubleTabWidget1stTabBackgroundColor=ff505050
+DoubleTabWidget1stTabInactiveTextColor=text
+DoubleTabWidget2ndSeparatorColor=toolBarItemDisabled
+DoubleTabWidget2ndTabActiveTextColor=text
+DoubleTabWidget2ndTabBackgroundColor=selectedBackground
+DoubleTabWidget2ndTabInactiveTextColor=text
+EditorPlaceholderColor=normalBackground
+FancyToolBarSeparatorColor=toolBarItemDisabled
+FancyTabBarBackgroundColor=shadowBackground
+FancyTabBarSelectedBackgroundColor=selectedBackground
+FancyTabWidgetDisabledSelectedTextColor=toolBarItemDisabled
+FancyTabWidgetDisabledUnselectedTextColor=toolBarItemDisabled
+FancyTabWidgetEnabledSelectedTextColor=fancyBarsBoldTextColor
+FancyTabWidgetEnabledUnselectedTextColor=fancyBarsBoldTextColor
+FancyToolButtonHoverColor=hoverBackground
+FancyToolButtonSelectedColor=selectedBackground
+FutureProgressBackgroundColor=shadowBackground
+IconsBaseColor=toolBarItem
+IconsDisabledColor=toolBarItemDisabled
+IconsInfoColor=ff3099dc
+IconsInfoToolBarColor=ff71b2db
+IconsWarningColor=warning
+IconsWarningToolBarColor=fff2d76e
+IconsErrorColor=error
+IconsErrorToolBarColor=ffdb6f71
+IconsRunColor=ff6da838
+IconsRunToolBarColor=ff93be6c
+IconsStopColor=ffee6969
+IconsStopToolBarColor=ffe27f7f
+IconsInterruptColor=ff587ff7
+IconsInterruptToolBarColor=ff6a7bc3
+IconsDebugColor=toolBarItem
+IconsNavigationArrowsColor=ffebc322
+IconsBuildHammerHandleColor=ffb06112
+IconsBuildHammerHeadColor=ff828384
+IconsModeWelcomeActiveColor=ff80c342
+IconsModeEditActiveColor=ff99aaef
+IconsModeDesignActiveColor=ffbb6000
+IconsModeDebugActiveColor=ff99aaef
+IconsModeProjectActiveColor=ff80c342
+IconsModeAnalyzeActiveColor=ff43adee
+IconsModeHelpActiveColor=fff4be04
+IconsCodeModelKeywordColor=ff777777
+IconsCodeModelClassColor=ffc0b550
+IconsCodeModelStructColor=ff53b053
+IconsCodeModelFunctionColor=ffd34373
+IconsCodeModelVariableColor=ff2bbbcc
+IconsCodeModelEnumColor=ffc0b550
+IconsCodeModelMacroColor=ff476ba0
+IconsCodeModelAttributeColor=ff316511
+IconsCodeModelUniformColor=ff994899
+IconsCodeModelVaryingColor=ffa08833
+IconsCodeModelOverlayBackgroundColor=70000000
+IconsCodeModelOverlayForegroundColor=ffd0d0d0
+InfoBarBackground=ff505000
+InfoBarText=text
+MenuBarEmptyAreaBackgroundColor=shadowBackground
+MenuBarItemBackgroundColor=shadowBackground
+MenuBarItemTextColorDisabled=textDisabled
+MenuBarItemTextColorNormal=text
+MenuItemTextColorDisabled=textDisabled
+MenuItemTextColorNormal=text
+MiniProjectTargetSelectorBackgroundColor=shadowBackground
+MiniProjectTargetSelectorBorderColor=shadowBackground
+MiniProjectTargetSelectorSummaryBackgroundColor=shadowBackground
+MiniProjectTargetSelectorTextColor=fancyBarsNormalTextColor
+PanelStatusBarBackgroundColor=shadowBackground
+PanelsWidgetSeparatorLineColor=000000
+PanelTextColorDark=text
+PanelTextColorMid=ffa0a0a0
+PanelTextColorLight=text
+ProgressBarColorError=error
+ProgressBarColorFinished=dda4d576
+ProgressBarColorNormal=ff999999
+ProgressBarTitleColor=fancyBarsBoldTextColor
+ProgressBarBackgroundColor=a0606060
+SplitterColor=splitter
+TextColorDisabled=textDisabled
+TextColorError=ffff4040
+TextColorHighlight=ffff0000
+TextColorHighlightBackground=7a6f1c
+TextColorLink=textColorLink
+TextColorLinkVisited=textColorLinkVisited
+TextColorNormal=text
+ToggleButtonBackgroundColor=shadowBackground
+ToolBarBackgroundColor=shadowBackground
+TreeViewArrowColorNormal=hoverBackground
+TreeViewArrowColorSelected=text
+
+OutputPanes_DebugTextColor=text
+OutputPanes_ErrorMessageTextColor=ffff6c6c
+OutputPanes_MessageOutput=ff008787
+OutputPanes_NormalMessageTextColor=ff008787
+OutputPanes_StdErrTextColor=ffff6666
+OutputPanes_StdOutTextColor=text
+OutputPanes_WarningMessageTextColor=fff3c300
+OutputPanes_TestPassTextColor=ff00b400
+OutputPanes_TestFailTextColor=ffcf4848
+OutputPanes_TestXFailTextColor=ff28dc28
+OutputPanes_TestXPassTextColor=ffdc2828
+OutputPanes_TestSkipTextColor=ff828282
+OutputPanes_TestWarnTextColor=ffc8c800
+OutputPanes_TestFatalTextColor=ffc82828
+OutputPanes_TestDebugTextColor=ff329696
+OutputPaneButtonFlashColor=error
+OutputPaneToggleButtonTextColorChecked=textHighlighted
+OutputPaneToggleButtonTextColorUnchecked=fancyBarsNormalTextColor
+
+Debugger_LogWindow_LogInput=ff00acac
+Debugger_LogWindow_LogStatus=ff00875a
+Debugger_LogWindow_LogTime=ffbf0303
+
+Debugger_WatchItem_ValueNormal=text
+Debugger_WatchItem_ValueInvalid=textDisabled
+Debugger_WatchItem_ValueChanged=ffff6666
+
+Debugger_Breakpoint_TextMarkColor=ffff4040
+
+Welcome_TextColor=text
+Welcome_ForegroundPrimaryColor=ff999999
+Welcome_ForegroundSecondaryColor=ff808080
+Welcome_BackgroundColor=normalBackground
+Welcome_ButtonBackgroundColor=normalBackground
+Welcome_DividerColor=ff555555
+Welcome_HoverColor=ff444444
+Welcome_LinkColor=ff7fc63c
+Welcome_DisabledLinkColor=textDisabled
+
+Timeline_TextColor=text
+Timeline_BackgroundColor1=normalBackground
+Timeline_BackgroundColor2=ff444444
+Timeline_DividerColor=ff555555
+Timeline_HighlightColor=ff3099dc
+Timeline_PanelBackgroundColor=ff808080
+Timeline_PanelHeaderColor=alternateBackground
+Timeline_HandleColor=alternateBackground
+Timeline_RangeColor=selectedBackground
+
+VcsBase_FileStatusUnknown_TextColor=text
+VcsBase_FileAdded_TextColor=ff00ff00
+VcsBase_FileModified_TextColor=ff8ee0ff
+VcsBase_FileDeleted_TextColor=fffff6c6c
+VcsBase_FileRenamed_TextColor=ffffa500
+VcsBase_FileUnmerged_TextColor=ffff4040
+
+Bookmarks_TextMarkColor=ff8080ff
+
+TextEditor_SearchResult_ScrollBarColor=ff00c000
+TextEditor_CurrentLine_ScrollBarColor=ffffffff
+
+ProjectExplorer_TaskError_TextMarkColor=error
+ProjectExplorer_TaskWarn_TextMarkColor=warning
+
+CodeModel_Error_TextMarkColor=error
+CodeModel_Warning_TextMarkColor=warning
+
+QmlDesigner_BackgroundColor=qmlDesignerButtonColor
+QmlDesigner_HighlightColor=ff1d545c
+QmlDesigner_FormEditorSelectionColor=ff4ba2ff
+QmlDesigner_FormEditorForegroundColor=ffffffff
+QmlDesigner_BackgroundColorDarkAlternate=ff323232
+QmlDesigner_BackgroundColorDarker=ff262728
+QmlDesigner_BorderColor=splitter
+QmlDesigner_ButtonColor=ff595b5c
+QmlDesigner_TabDark=shadowBackground
+QmlDesigner_TabLight=text
+QmlDesigner_FormeditorBackgroundColor=qmlDesignerButtonColor
+QmlDesigner_AlternateBackgroundColor=qmlDesignerButtonColor
+QmlDesigner_ScrollBarHandleColor=ff595b5c
+
+PaletteWindow=normalBackground
+PaletteWindowText=text
+PaletteBase=normalBackground
+PaletteAlternateBase=alternateBackground
+PaletteButton=shadowBackground
+PaletteBrightText=ffff3333
+PaletteText=text
+PaletteButtonText=text
+PaletteButtonTextDisabled=textDisabled
+PaletteToolTipBase=shadowBackground
+PaletteHighlight=selectedBackgroundText
+PaletteDark=shadowBackground
+PaletteHighlightedText=textHighlighted
+PaletteToolTipText=text
+PaletteLink=textColorLink
+PaletteLinkVisited=textColorLinkVisited
+PaletteWindowDisabled=backgroundColorDisabled
+PaletteWindowTextDisabled=textDisabled
+PaletteBaseDisabled=backgroundColorDisabled
+PaletteTextDisabled=textDisabled
+PaletteMid=ffa0a0a0
+PalettePlaceholderText=ff7f7f80
+
+[Flags]
+ComboBoxDrawTextShadow=false
+DerivePaletteFromTheme=true
+DrawIndicatorBranch=true
+DrawSearchResultWidgetFrame=false
+DrawTargetSelectorBottom=false
+DrawToolBarHighlights=false
+DrawToolBarBorders=false
+ApplyThemePaletteGlobally=true
+FlatToolBars=true
+FlatSideBarIcons=true
+FlatProjectsMode=true
+FlatMenuBar=true
+ToolBarIconShadow=true
+WindowColorAsBase=false
+DarkUserInterface=true
+
+[Gradients]
+DetailsWidgetHeaderGradient\1\color=00000000
+DetailsWidgetHeaderGradient\1\pos=1
+DetailsWidgetHeaderGradient\size=1
diff --git a/rsc/theme/flat-light.creatortheme b/rsc/theme/flat-light.creatortheme
new file mode 100644
index 0000000000000000000000000000000000000000..b98478d134db57bd0a0ca4bce03eb676e148fd78
--- /dev/null
+++ b/rsc/theme/flat-light.creatortheme
@@ -0,0 +1,368 @@
+[General]
+ThemeName=Flat Light
+PreferredStyles=
+
+[Palette]
+shadowBackground=ffe4e4e4
+text=ff000000
+textDisabled=55000000
+toolBarItem=a0010508
+toolBarItemDisabled=38000000
+fancyBarsNormalTextColor=ff000000
+fancyBarsBoldTextColor=a0010508
+hoverBackground=1a000000
+selectedBackground=a8ffffff
+normalBackground=ffffffff
+alternateBackground=ff515151
+stop_error=ffec7373
+run_success=ff52c23b
+splitter=ffbdbebf
+error=ffdf4f4f
+warning=ffecbc1c
+qmlDesignerButtonColor=fff8f8f8
+
+[Colors]
+;DS controls theme START
+DSpanelBackground=ffeaeaea
+
+DSinteraction=ff2aafd3
+DSerrorColor=ffdf3a3a
+DSdisabledColor=ff8e8e8e
+
+DScontrolBackground=ffeaeaea
+DScontrolBackgroundInteraction=ffc9c9c9
+DScontrolBackgroundDisabled=ffeaeaea
+DScontrolBackgroundGlobalHover=ffe5e5e5
+DScontrolBackgroundHover=ffd1d1d1
+
+DScontrolOutline=ffcecccc
+DScontrolOutlineInteraction=ff2aafd3
+DScontrolOutlineDisabled=ff707070
+
+DStextColor=ff262626
+DStextColorDisabled=ff707070
+DStextSelectionColor=ff2aafd3
+DStextSelectedTextColor=ff000000
+
+DSplaceholderTextColor=ff262626
+DSplaceholderTextColorInteraction=ffababab
+
+DSiconColor=ff262626
+DSiconColorHover=ff191919
+DSiconColorInteraction=ffffffff
+DSiconColorDisabled=ff707070
+DSiconColorSelected=ff2aafd3
+
+DSlinkIndicatorColor=ff808080
+DSlinkIndicatorColorHover=ff1f1f1f
+DSlinkIndicatorColorInteraction=ff2aafd3
+DSlinkIndicatorColorDisabled=ff707070
+
+DSpopupBackground=ffd3d3d3
+DSpopupOverlayColor=99191919
+
+DSsliderActiveTrack=ff7c7b7b
+DSsliderActiveTrackHover=ff000000
+DSsliderActiveTrackFocus=ffaaaaaa
+DSsliderInactiveTrack=ffaaaaaa
+DSsliderInactiveTrackHover=ff505050
+DSsliderInactiveTrackFocus=ff606060
+DSsliderHandle=ff1f1f1f
+DSsliderHandleHover=ff606060
+DSsliderHandleFocus=ff0492c9
+DSsliderHandleInteraction=ff2aafd3
+
+DSscrollBarTrack=ffb5b4b4
+DSscrollBarHandle=ff9b9b9b
+
+DSsectionHeadBackground=ffd8d8d8
+
+DSstateDefaultHighlight=ffffe400
+DSstateSeparatorColor=ffadadad
+DSstateBackgroundColor=ffe0e0e0
+DSstatePreviewOutline=ff363636
+
+DSchangedStateText=ff99ccff
+
+DS3DAxisXColor=ffd00000
+DS3DAxisYColor=ff009900
+DS3DAxisZColor=ff5050ff
+
+DSactionBinding=ff2aafd3
+DSactionAlias=fff93a3a
+DSactionKeyframe=ffe0e01b
+DSactionJIT=ff2db543
+
+DStableHeaderBackground=ffff0000
+DStableHeaderText=ff00ff00
+
+DSdockContainerBackground=ff323232
+DSdockContainerSplitter=ff323232
+DSdockAreaBackground=ff262728
+
+DSdockWidgetBackground=ff00ff00
+DSdockWidgetSplitter=ff595959
+DSdockWidgetTitleBar=ffeaeaea
+
+DStitleBarText=ffdadada
+DStitleBarIcon=ff4f5052
+DStitleBarButtonHover=40ffffff
+DStitleBarButtonPress=60ffffff
+
+DStabContainerBackground=ff0000ff
+DStabSplitter=ff595959
+
+DStabInactiveBackground=ff999999
+DStabInactiveText=ff262626
+DStabInactiveIcon=ffffffff
+DStabInactiveButtonHover=ff1f1f1f
+DStabInactiveButtonPress=ff1f1f1f
+
+DStabActiveBackground=ffdadada
+DStabActiveText=ff111111
+DStabActiveIcon=ff000000
+DStabActiveButtonHover=ffdadada
+DStabActiveButtonPress=ffdadada
+
+DStabFocusBackground=ff2aafd3
+DStabFocusText=ff111111
+DStabFocusIcon=ff000000
+DStabFocusButtonHover=ff2aafd3
+DStabFocusButtonPress=ff2aafd3
+
+DSnavigatorBranch=ff7c7b7b
+DSnavigatorBranchIndicator=ff7c7b7b
+DSnavigatorItemBackground=ffd8d8d8
+DSnavigatorItemBackgroundHover=ffc2c2c2
+DSnavigatorItemBackgroundSelected=ffffffff
+DSnavigatorText=ff262626
+DSnavigatorTextHover=ff1f1f1f
+DSnavigatorTextSelected=ff2aafd3
+DSnavigatorIcon=ff1f1f1f
+DSnavigatorIconHover=ff1f1f1f
+DSnavigatorIconSelected=ff7c7b7b
+DSnavigatorAliasIconChecked=ffff0000
+DSnavigatorDropIndicatorBackground=ff2aafd3
+DSnavigatorDropIndicatorOutline=ff2aafd3
+
+DSheaderViewBackground=ffd8d8d8
+DStableViewAlternateBackground=ff00ff00
+
+DStoolTipBackground=ff111111
+DStoolTipOutline=ffdadada
+DStoolTipText=ffdadada
+
+DSUnimportedModuleColor=ffe33c2e
+
+;DS controls theme END
+
+BackgroundColorAlternate=alternateBackground
+BackgroundColorDark=shadowBackground
+BackgroundColorHover=hoverBackground
+BackgroundColorNormal=normalBackground
+BackgroundColorDisabled=ff444444
+BackgroundColorSelected=selectedBackground
+BadgeLabelBackgroundColorChecked=ffe0e0e0
+BadgeLabelBackgroundColorUnchecked=ff808080
+BadgeLabelTextColorChecked=ff606060
+BadgeLabelTextColorUnchecked=ffffffff
+CanceledSearchTextColor=ff0000
+ComboBoxArrowColor=toolBarItem
+ComboBoxArrowColorDisabled=toolBarItemDisabled
+ComboBoxTextColor=fancyBarsNormalTextColor
+DetailsButtonBackgroundColorHover=b4ffffff
+DetailsWidgetBackgroundColor=28ffffff
+DockWidgetResizeHandleColor=splitter
+DoubleTabWidget1stSeparatorColor=ffff0000
+DoubleTabWidget1stTabActiveTextColor=ff000000
+DoubleTabWidget1stTabBackgroundColor=ffff0000
+DoubleTabWidget1stTabInactiveTextColor=ff555555
+DoubleTabWidget2ndSeparatorColor=ffff0000
+DoubleTabWidget2ndTabActiveTextColor=ffffffff
+DoubleTabWidget2ndTabBackgroundColor=ffff0000
+DoubleTabWidget2ndTabInactiveTextColor=ff000000
+EditorPlaceholderColor=fff4f4f4
+FancyToolBarSeparatorColor=toolBarItemDisabled
+FancyTabBarBackgroundColor=shadowBackground
+FancyTabBarSelectedBackgroundColor=selectedBackground
+FancyTabWidgetDisabledSelectedTextColor=toolBarItemDisabled
+FancyTabWidgetDisabledUnselectedTextColor=toolBarItemDisabled
+FancyTabWidgetEnabledSelectedTextColor=fancyBarsBoldTextColor
+FancyTabWidgetEnabledUnselectedTextColor=fancyBarsBoldTextColor
+FancyToolButtonHoverColor=hoverBackground
+FancyToolButtonSelectedColor=selectedBackground
+FutureProgressBackgroundColor=shadowBackground
+IconsBaseColor=toolBarItem
+IconsDisabledColor=toolBarItemDisabled
+IconsInfoColor=ff3099dc
+IconsInfoToolBarColor=ff3099dc
+IconsWarningColor=warning
+IconsWarningToolBarColor=ffecbc1c
+IconsErrorColor=error
+IconsErrorToolBarColor=ffdf4f4f
+IconsRunColor=run_success
+IconsRunToolBarColor=run_success
+IconsStopColor=stop_error
+IconsStopToolBarColor=stop_error
+IconsInterruptColor=ff587ff7
+IconsInterruptToolBarColor=ff6a7bc3
+IconsDebugColor=toolBarItem
+IconsNavigationArrowsColor=ff3dabe6
+IconsBuildHammerHandleColor=ffc26b14
+IconsBuildHammerHeadColor=ff868687
+IconsModeWelcomeActiveColor=ff5caa15
+IconsModeEditActiveColor=ff6a6add
+IconsModeDesignActiveColor=ffbb6000
+IconsModeDebugActiveColor=ff6a6add
+IconsModeProjectActiveColor=ff5caa15
+IconsModeAnalyzeActiveColor=ff43adee
+IconsModeHelpActiveColor=fffaa838
+IconsCodeModelKeywordColor=ff777777
+IconsCodeModelClassColor=ffc0b550
+IconsCodeModelStructColor=ff53b053
+IconsCodeModelFunctionColor=ffd34373
+IconsCodeModelVariableColor=ff2bbbcc
+IconsCodeModelEnumColor=ffc0b550
+IconsCodeModelMacroColor=ff476ba0
+IconsCodeModelAttributeColor=ff316511
+IconsCodeModelUniformColor=ff994899
+IconsCodeModelVaryingColor=ffa08833
+IconsCodeModelOverlayBackgroundColor=70ffffff
+IconsCodeModelOverlayForegroundColor=ff232425
+InfoBarBackground=ffffffe1
+InfoBarText=text
+MenuBarEmptyAreaBackgroundColor=shadowBackground
+MenuBarItemBackgroundColor=shadowBackground
+MenuBarItemTextColorDisabled=textDisabled
+MenuBarItemTextColorNormal=text
+MenuItemTextColorDisabled=textDisabled
+MenuItemTextColorNormal=text
+MiniProjectTargetSelectorBackgroundColor=shadowBackground
+MiniProjectTargetSelectorBorderColor=shadowBackground
+MiniProjectTargetSelectorSummaryBackgroundColor=shadowBackground
+MiniProjectTargetSelectorTextColor=fancyBarsNormalTextColor
+PanelStatusBarBackgroundColor=shadowBackground
+PanelsWidgetSeparatorLineColor=000000
+PanelTextColorDark=text
+PanelTextColorMid=ff666666
+PanelTextColorLight=fancyBarsNormalTextColor
+ProgressBarColorError=error
+ProgressBarColorFinished=run_success
+ProgressBarColorNormal=ff888888
+ProgressBarTitleColor=fancyBarsBoldTextColor
+ProgressBarBackgroundColor=28000000
+SplitterColor=splitter
+TextColorDisabled=textDisabled
+TextColorError=ffff4040
+TextColorHighlight=ffff0000
+TextColorHighlightBackground=ffef0b
+TextColorLink=ff007af4
+TextColorLinkVisited=ffa57aff
+TextColorNormal=text
+ToggleButtonBackgroundColor=shadowBackground
+ToolBarBackgroundColor=shadowBackground
+TreeViewArrowColorNormal=hoverBackground
+TreeViewArrowColorSelected=text
+
+OutputPanes_DebugTextColor=text
+OutputPanes_ErrorMessageTextColor=ffaa0000
+OutputPanes_MessageOutput=ff0000aa
+OutputPanes_NormalMessageTextColor=ff0000aa
+OutputPanes_StdErrTextColor=ffaa0000
+OutputPanes_StdOutTextColor=ff000000
+OutputPanes_WarningMessageTextColor=ff808000
+OutputPanes_TestPassTextColor=ff009900
+OutputPanes_TestFailTextColor=ffa00000
+OutputPanes_TestXFailTextColor=ff28f028
+OutputPanes_TestXPassTextColor=fff02828
+OutputPanes_TestSkipTextColor=ff787878
+OutputPanes_TestWarnTextColor=ffd0bb00
+OutputPanes_TestFatalTextColor=ff640000
+OutputPanes_TestDebugTextColor=ff329696
+OutputPaneButtonFlashColor=ffff0000
+OutputPaneToggleButtonTextColorChecked=fancyBarsNormalTextColor
+OutputPaneToggleButtonTextColorUnchecked=fancyBarsNormalTextColor
+
+Debugger_LogWindow_LogInput=ff00acac
+Debugger_LogWindow_LogStatus=ff00875a
+Debugger_LogWindow_LogTime=ffbf0303
+
+Debugger_WatchItem_ValueNormal=text
+Debugger_WatchItem_ValueInvalid=textDisabled
+Debugger_WatchItem_ValueChanged=ffbf0303
+
+Debugger_Breakpoint_TextMarkColor=ffff4040
+
+Welcome_TextColor=ff000000
+Welcome_ForegroundPrimaryColor=ff404244
+Welcome_ForegroundSecondaryColor=ff727476
+Welcome_BackgroundColor=normalBackground
+Welcome_ButtonBackgroundColor=normalBackground
+Welcome_DividerColor=ffd6d6d6
+Welcome_HoverColor=fff6f6f6
+Welcome_LinkColor=ff5caa15
+Welcome_DisabledLinkColor=textDisabled
+
+Timeline_TextColor=text
+Timeline_BackgroundColor1=normalBackground
+Timeline_BackgroundColor2=fff6f6f6
+Timeline_DividerColor=ffd6d6d6
+Timeline_HighlightColor=ff3099dc
+Timeline_PanelBackgroundColor=ffd6d6d6
+Timeline_PanelHeaderColor=ff888888
+Timeline_HandleColor=ff888888
+Timeline_RangeColor=selectedBackground
+
+VcsBase_FileStatusUnknown_TextColor=ff000000
+VcsBase_FileAdded_TextColor=ff00aa00
+VcsBase_FileModified_TextColor=ff0000ee
+VcsBase_FileDeleted_TextColor=ff800000
+VcsBase_FileRenamed_TextColor=ffd77d00
+VcsBase_FileUnmerged_TextColor=ffee0000
+
+Bookmarks_TextMarkColor=ffa0a0ff
+
+TextEditor_SearchResult_ScrollBarColor=ff00c000
+TextEditor_CurrentLine_ScrollBarColor=ff404040
+
+ProjectExplorer_TaskError_TextMarkColor=error
+ProjectExplorer_TaskWarn_TextMarkColor=warning
+
+CodeModel_Error_TextMarkColor=error
+CodeModel_Warning_TextMarkColor=warning
+
+QmlDesigner_BackgroundColor=qmlDesignerButtonColor
+QmlDesigner_HighlightColor=ff46a2da
+QmlDesigner_FormEditorSelectionColor=ff4ba2ff
+QmlDesigner_FormEditorForegroundColor=ffffffff
+QmlDesigner_BackgroundColorDarkAlternate=ffeaeaea
+QmlDesigner_BackgroundColorDarker=fff5f5f5
+QmlDesigner_BorderColor=splitter
+QmlDesigner_ButtonColor=ffcccccc
+QmlDesigner_TabDark=ff585858
+QmlDesigner_TabLight=ffd0d0d0
+QmlDesigner_FormeditorBackgroundColor=qmlDesignerButtonColor
+QmlDesigner_AlternateBackgroundColor=qmlDesignerButtonColor
+QmlDesigner_ScrollBarHandleColor=ffcccccc
+
+[Flags]
+ComboBoxDrawTextShadow=false
+DerivePaletteFromTheme=false
+DrawIndicatorBranch=true
+DrawSearchResultWidgetFrame=false
+DrawTargetSelectorBottom=false
+DrawToolBarHighlights=false
+DrawToolBarBorders=true
+ApplyThemePaletteGlobally=false
+FlatToolBars=true
+FlatSideBarIcons=true
+FlatProjectsMode=false
+FlatMenuBar=false
+ToolBarIconShadow=false
+WindowColorAsBase=false
+DarkUserInterface=false
+
+[Gradients]
+DetailsWidgetHeaderGradient\1\color=00000000
+DetailsWidgetHeaderGradient\1\pos=1
+DetailsWidgetHeaderGradient\size=1
diff --git a/rsc/theme/flat.creatortheme b/rsc/theme/flat.creatortheme
new file mode 100644
index 0000000000000000000000000000000000000000..b6c8f09d9e44b443a434af509481bcfaacda8fee
--- /dev/null
+++ b/rsc/theme/flat.creatortheme
@@ -0,0 +1,366 @@
+[General]
+ThemeName=Flat
+PreferredStyles=
+
+[Palette]
+shadowBackground=ff404142
+text=ff000000
+textDisabled=55000000
+toolBarItem=bcfbfdff
+toolBarItemDisabled=56a5a6a7
+fancyBarsNormalTextColor=ffffffff
+fancyBarsBoldTextColor=b6fbfdff
+hoverBackground=28ffffff
+selectedBackground=7a000000
+normalBackground=ffffffff
+alternateBackground=ff515151
+error=ffdf4f4f
+warning=ffecbc1c
+splitter=ff313131
+qmlDesignerButtonColor=ff4c4e50
+
+[Colors]
+;DS controls theme START
+DSpanelBackground=ff323232
+
+DSinteraction=ff2aafd3
+DSerrorColor=ffdf3a3a
+DSdisabledColor=ff707070
+
+DScontrolBackground=ff323232
+DScontrolBackgroundInteraction=ff595959
+DScontrolBackgroundDisabled=ff323232
+DScontrolBackgroundGlobalHover=ff474747
+DScontrolBackgroundHover=ff666666
+
+DScontrolOutline=ff1f1f1f
+DScontrolOutlineInteraction=ff2aafd3
+DScontrolOutlineDisabled=ff707070
+
+DStextColor=ffffffff
+DStextColorDisabled=ff707070
+DStextSelectionColor=ff2aafd3
+DStextSelectedTextColor=ff000000
+
+DSplaceholderTextColor=ffffffff
+DSplaceholderTextColorInteraction=ffababab
+
+DSiconColor=ffffffff
+DSiconColorHover=ff262626
+DSiconColorInteraction=ff707070
+DSiconColorDisabled=ff707070
+DSiconColorSelected=ff2aafd3
+
+DSlinkIndicatorColor=ff808080
+DSlinkIndicatorColorHover=ffffffff
+DSlinkIndicatorColorInteraction=ff2aafd3
+DSlinkIndicatorColorDisabled=ff707070
+
+DSpopupBackground=ff474747
+DSpopupOverlayColor=99191919
+
+DSsliderActiveTrack=ff7c7b7b
+DSsliderActiveTrackHover=ff000000
+DSsliderActiveTrackFocus=ffaaaaaa
+DSsliderInactiveTrack=ff595959
+DSsliderInactiveTrackHover=ff505050
+DSsliderInactiveTrackFocus=ff606060
+DSsliderHandle=ff1f1f1f
+DSsliderHandleHover=ff606060
+DSsliderHandleFocus=ff0492c9
+DSsliderHandleInteraction=ff2aafd3
+
+DSscrollBarTrack=ff404040
+DSscrollBarHandle=ff505050
+
+DSsectionHeadBackground=ff1f1f1f
+
+DSstateDefaultHighlight=ffffe400
+DSstateSeparatorColor=ff7c7b7b
+DSstateBackgroundColor=ff383838
+DSstatePreviewOutline=ffaaaaaa
+
+DSchangedStateText=ff99ccff
+
+DS3DAxisXColor=ffd00000
+DS3DAxisYColor=ff009900
+DS3DAxisZColor=ff5050ff
+
+DSactionBinding=ff2aafd3
+DSactionAlias=fff93a3a
+DSactionKeyframe=ffe0e01b
+DSactionJIT=ff2db543
+
+DStableHeaderBackground=ffff0000
+DStableHeaderText=ff00ff00
+
+DSdockContainerBackground=ff323232
+DSdockContainerSplitter=ff323232
+DSdockAreaBackground=ff262728
+
+DSdockWidgetBackground=ff00ff00
+DSdockWidgetSplitter=ff595959
+DSdockWidgetTitleBar=ff1f1f1f
+
+DStitleBarText=ffdadada
+DStitleBarIcon=ffffffff
+DStitleBarButtonHover=40ffffff
+DStitleBarButtonPress=60ffffff
+
+DStabContainerBackground=ff0000ff
+DStabSplitter=ff595959
+
+DStabInactiveBackground=ff1f1f1f
+DStabInactiveText=ffdadada
+DStabInactiveIcon=ffffffff
+DStabInactiveButtonHover=ff1f1f1f
+DStabInactiveButtonPress=ff1f1f1f
+
+DStabActiveBackground=ffdadada
+DStabActiveText=ff111111
+DStabActiveIcon=ff000000
+DStabActiveButtonHover=ffdadada
+DStabActiveButtonPress=ffdadada
+
+DStabFocusBackground=ff2aafd3
+DStabFocusText=ff111111
+DStabFocusIcon=ff000000
+DStabFocusButtonHover=ff2aafd3
+DStabFocusButtonPress=ff2aafd3
+
+DSnavigatorBranch=ff7c7b7b
+DSnavigatorBranchIndicator=ff7c7b7b
+DSnavigatorItemBackground=ff262626
+DSnavigatorItemBackgroundHover=ff666666
+DSnavigatorItemBackgroundSelected=ff1f1f1f
+DSnavigatorText=ffffffff
+DSnavigatorTextHover=ff1f1f1f
+DSnavigatorTextSelected=ff2aafd3
+DSnavigatorIcon=ffffffff
+DSnavigatorIconHover=ff1f1f1f
+DSnavigatorIconSelected=ff7c7b7b
+DSnavigatorAliasIconChecked=ffff0000
+DSnavigatorDropIndicatorBackground=ff2aafd3
+DSnavigatorDropIndicatorOutline=ff2aafd3
+
+DSheaderViewBackground=ff1f1f1f
+DStableViewAlternateBackground=ff00ff00
+
+DStoolTipBackground=ff111111
+DStoolTipOutline=ffdadada
+DStoolTipText=ffdadada
+
+DSUnimportedModuleColor=ffe33c2e
+
+;DS controls theme END
+
+BackgroundColorAlternate=alternateBackground
+BackgroundColorDark=shadowBackground
+BackgroundColorHover=hoverBackground
+BackgroundColorNormal=normalBackground
+BackgroundColorDisabled=ff444444
+BackgroundColorSelected=selectedBackground
+BadgeLabelBackgroundColorChecked=ffe0e0e0
+BadgeLabelBackgroundColorUnchecked=ff808080
+BadgeLabelTextColorChecked=ff606060
+BadgeLabelTextColorUnchecked=ffffffff
+CanceledSearchTextColor=ff0000
+ComboBoxArrowColor=toolBarItem
+ComboBoxArrowColorDisabled=toolBarItemDisabled
+ComboBoxTextColor=fancyBarsNormalTextColor
+DetailsButtonBackgroundColorHover=b4ffffff
+DetailsWidgetBackgroundColor=28ffffff
+DockWidgetResizeHandleColor=splitter
+DoubleTabWidget1stSeparatorColor=ffff0000
+DoubleTabWidget1stTabActiveTextColor=ff000000
+DoubleTabWidget1stTabBackgroundColor=ffff0000
+DoubleTabWidget1stTabInactiveTextColor=ffffffff
+DoubleTabWidget2ndSeparatorColor=ffff0000
+DoubleTabWidget2ndTabActiveTextColor=ffffffff
+DoubleTabWidget2ndTabBackgroundColor=ffff0000
+DoubleTabWidget2ndTabInactiveTextColor=ff000000
+EditorPlaceholderColor=ffdddddd
+FancyToolBarSeparatorColor=toolBarItemDisabled
+FancyTabBarBackgroundColor=shadowBackground
+FancyTabBarSelectedBackgroundColor=selectedBackground
+FancyTabWidgetDisabledSelectedTextColor=toolBarItemDisabled
+FancyTabWidgetDisabledUnselectedTextColor=toolBarItemDisabled
+FancyTabWidgetEnabledSelectedTextColor=fancyBarsBoldTextColor
+FancyTabWidgetEnabledUnselectedTextColor=fancyBarsBoldTextColor
+FancyToolButtonHoverColor=hoverBackground
+FancyToolButtonSelectedColor=selectedBackground
+FutureProgressBackgroundColor=shadowBackground
+IconsBaseColor=toolBarItem
+IconsDisabledColor=toolBarItemDisabled
+IconsInfoColor=ff3099dc
+IconsInfoToolBarColor=ff71b2db
+IconsWarningColor=warning
+IconsWarningToolBarColor=fff2d76e
+IconsErrorColor=error
+IconsErrorToolBarColor=ffdb6f71
+IconsRunColor=ff6da838
+IconsRunToolBarColor=ff93be6c
+IconsStopColor=ffee6969
+IconsStopToolBarColor=ffe27f7f
+IconsInterruptColor=ff587ff7
+IconsInterruptToolBarColor=ff6a7bc3
+IconsDebugColor=toolBarItem
+IconsNavigationArrowsColor=ffebc322
+IconsBuildHammerHandleColor=ffc26b14
+IconsBuildHammerHeadColor=ff868687
+IconsModeWelcomeActiveColor=ff80c342
+IconsModeEditActiveColor=ff99aaef
+IconsModeDesignActiveColor=ffbb6000
+IconsModeDebugActiveColor=ff99aaef
+IconsModeProjectActiveColor=ff80c342
+IconsModeAnalyzeActiveColor=ff43adee
+IconsModeHelpActiveColor=fff4be04
+IconsCodeModelKeywordColor=ff777777
+IconsCodeModelClassColor=ffc0b550
+IconsCodeModelStructColor=ff53b053
+IconsCodeModelFunctionColor=ffd34373
+IconsCodeModelVariableColor=ff2bbbcc
+IconsCodeModelEnumColor=ffc0b550
+IconsCodeModelMacroColor=ff476ba0
+IconsCodeModelAttributeColor=ff316511
+IconsCodeModelUniformColor=ff994899
+IconsCodeModelVaryingColor=ffa08833
+IconsCodeModelOverlayBackgroundColor=70ffffff
+IconsCodeModelOverlayForegroundColor=ff232425
+InfoBarBackground=ffffffe1
+InfoBarText=text
+MenuBarEmptyAreaBackgroundColor=shadowBackground
+MenuBarItemBackgroundColor=shadowBackground
+MenuBarItemTextColorDisabled=textDisabled
+MenuBarItemTextColorNormal=text
+MenuItemTextColorDisabled=textDisabled
+MenuItemTextColorNormal=text
+MiniProjectTargetSelectorBackgroundColor=shadowBackground
+MiniProjectTargetSelectorBorderColor=shadowBackground
+MiniProjectTargetSelectorSummaryBackgroundColor=shadowBackground
+MiniProjectTargetSelectorTextColor=fancyBarsNormalTextColor
+PanelStatusBarBackgroundColor=shadowBackground
+PanelsWidgetSeparatorLineColor=000000
+PanelTextColorDark=text
+PanelTextColorMid=ff666666
+PanelTextColorLight=fancyBarsNormalTextColor
+ProgressBarColorError=error
+ProgressBarColorFinished=dda4d576
+ProgressBarColorNormal=ff999999
+ProgressBarTitleColor=fancyBarsBoldTextColor
+ProgressBarBackgroundColor=a0606060
+SplitterColor=splitter
+TextColorDisabled=textDisabled
+TextColorError=ffff4040
+TextColorHighlight=ffff0000
+TextColorHighlightBackground=ffef0b
+TextColorLink=ff007af4
+TextColorLinkVisited=ffa57aff
+TextColorNormal=text
+ToggleButtonBackgroundColor=shadowBackground
+ToolBarBackgroundColor=shadowBackground
+TreeViewArrowColorNormal=hoverBackground
+TreeViewArrowColorSelected=text
+
+OutputPanes_DebugTextColor=text
+OutputPanes_ErrorMessageTextColor=ffaa0000
+OutputPanes_MessageOutput=ff0000aa
+OutputPanes_NormalMessageTextColor=ff0000aa
+OutputPanes_StdErrTextColor=ffaa0000
+OutputPanes_StdOutTextColor=ff000000
+OutputPanes_WarningMessageTextColor=ff808000
+OutputPanes_TestPassTextColor=ff009900
+OutputPanes_TestFailTextColor=ffa00000
+OutputPanes_TestXFailTextColor=ff28f028
+OutputPanes_TestXPassTextColor=fff02828
+OutputPanes_TestSkipTextColor=ff787878
+OutputPanes_TestWarnTextColor=ffd0bb00
+OutputPanes_TestFatalTextColor=ff640000
+OutputPanes_TestDebugTextColor=ff329696
+OutputPaneButtonFlashColor=ffff0000
+OutputPaneToggleButtonTextColorChecked=fancyBarsNormalTextColor
+OutputPaneToggleButtonTextColorUnchecked=fancyBarsNormalTextColor
+
+Debugger_LogWindow_LogInput=ff00acac
+Debugger_LogWindow_LogStatus=ff00875a
+Debugger_LogWindow_LogTime=ffbf0303
+
+Debugger_WatchItem_ValueNormal=text
+Debugger_WatchItem_ValueInvalid=textDisabled
+Debugger_WatchItem_ValueChanged=ffbf0303
+
+Debugger_Breakpoint_TextMarkColor=ffff4040
+
+Welcome_TextColor=ff000000
+Welcome_ForegroundPrimaryColor=shadowBackground
+Welcome_ForegroundSecondaryColor=ff727476
+Welcome_BackgroundColor=normalBackground
+Welcome_ButtonBackgroundColor=normalBackground
+Welcome_DividerColor=ffd6d6d6
+Welcome_HoverColor=fff6f6f6
+Welcome_LinkColor=ff5caa15
+Welcome_DisabledLinkColor=textDisabled
+
+Timeline_TextColor=text
+Timeline_BackgroundColor1=normalBackground
+Timeline_BackgroundColor2=fff6f6f6
+Timeline_DividerColor=ffd6d6d6
+Timeline_HighlightColor=ff71b2db
+Timeline_PanelBackgroundColor=ffd6d6d6
+Timeline_PanelHeaderColor=alternateBackground
+Timeline_HandleColor=alternateBackground
+Timeline_RangeColor=selectedBackground
+
+VcsBase_FileStatusUnknown_TextColor=ff000000
+VcsBase_FileAdded_TextColor=ff00aa00
+VcsBase_FileModified_TextColor=ff0000ee
+VcsBase_FileDeleted_TextColor=ff800000
+VcsBase_FileRenamed_TextColor=ffd77d00
+VcsBase_FileUnmerged_TextColor=ffee0000
+
+Bookmarks_TextMarkColor=ffa0a0ff
+
+TextEditor_SearchResult_ScrollBarColor=ff00c000
+TextEditor_CurrentLine_ScrollBarColor=ff404040
+
+ProjectExplorer_TaskError_TextMarkColor=error
+ProjectExplorer_TaskWarn_TextMarkColor=warning
+
+CodeModel_Error_TextMarkColor=error
+CodeModel_Warning_TextMarkColor=warning
+
+QmlDesigner_BackgroundColor=qmlDesignerButtonColor
+QmlDesigner_HighlightColor=ff46a2da
+QmlDesigner_FormEditorSelectionColor=ff4ba2ff
+QmlDesigner_FormEditorForegroundColor=ffffffff
+QmlDesigner_BackgroundColorDarkAlternate=ff323232
+QmlDesigner_BackgroundColorDarker=ff262728
+QmlDesigner_BorderColor=splitter
+QmlDesigner_ButtonColor=ff595b5c
+QmlDesigner_TabDark=shadowBackground
+QmlDesigner_TabLight=ffffffff
+QmlDesigner_FormeditorBackgroundColor=qmlDesignerButtonColor
+QmlDesigner_AlternateBackgroundColor=qmlDesignerButtonColor
+QmlDesigner_ScrollBarHandleColor=ff595b5c
+
+[Flags]
+ComboBoxDrawTextShadow=false
+DerivePaletteFromTheme=false
+DrawIndicatorBranch=true
+DrawSearchResultWidgetFrame=false
+DrawTargetSelectorBottom=false
+DrawToolBarHighlights=false
+DrawToolBarBorders=false
+ApplyThemePaletteGlobally=false
+FlatToolBars=true
+FlatSideBarIcons=true
+FlatProjectsMode=false
+FlatMenuBar=false
+ToolBarIconShadow=true
+WindowColorAsBase=false
+DarkUserInterface=false
+
+[Gradients]
+DetailsWidgetHeaderGradient\1\color=00000000
+DetailsWidgetHeaderGradient\1\pos=1
+DetailsWidgetHeaderGradient\size=1
diff --git a/src/Lib/AbstractDocument.hh b/src/Lib/AbstractDocument.hh
index 51ed90b0f6c1b4aae910e30d4554d42b32d5442d..c39fb5661a9cdcc3bd841950fe18c011f9214972 100644
--- a/src/Lib/AbstractDocument.hh
+++ b/src/Lib/AbstractDocument.hh
@@ -1,12 +1,13 @@
-#ifndef VIVY_ABSTRACT_DOCUMENT_H
-#define VIVY_ABSTRACT_DOCUMENT_H
+#pragma once
 
 #ifndef __cplusplus
 #error "This is a C++ header"
 #endif
 
+#include "../VivyApplication.hh"
 #include "Utils.hh"
 #include "Uuid.hh"
+#include "Log.hh"
 
 namespace Vivy
 {
@@ -14,6 +15,9 @@ class AbstractDocument : public QObject {
     Q_OBJECT
     VIVY_UNMOVABLE_OBJECT(AbstractDocument)
 
+protected:
+    VIVY_APP_LOGGABLE_OBJECT(AbstractDocument, logger)
+
     void copyOrRenameWith(const QFileInfo &newFile, auto action, auto success)
     {
         const QFileInfo oldFile(getName());
@@ -22,7 +26,7 @@ class AbstractDocument : public QObject {
         QDir dirOp;
         const QString newAbsDirPath = newFile.dir().absolutePath();
         if (!dirOp.exists(newAbsDirPath)) {
-            qInfo() << "Create folder " << newAbsDirPath;
+            logInfo() << "Create folder " << VIVY_LOG_QUOTED(newAbsDirPath);
             dirOp.mkpath(newAbsDirPath);
         }
 
@@ -31,7 +35,7 @@ class AbstractDocument : public QObject {
         }
 
         if (newFile.exists()) {
-            qWarning() << "Deleting the already existing" << newFile;
+            logWarning() << "Deleting the already existing file " << VIVY_LOG_QUOTED(newFile);
             if (!dirOp.remove(newFile.absoluteFilePath()))
                 throw std::runtime_error("Failed to remove " +
                                          newFile.absoluteFilePath().toStdString());
@@ -101,5 +105,3 @@ signals:
 }
 
 bool operator==(const Vivy::AbstractDocument &a, const Vivy::AbstractDocument &b) noexcept;
-
-#endif // VIVY_ABSTRACT_DOCUMENT_H
diff --git a/src/Lib/AbstractMediaContext.hh b/src/Lib/AbstractMediaContext.hh
index f4a09ed6c040a96dcbea04d0b71fe2491430f727..a15609de50345b89f238deae99cbb8722de14456 100644
--- a/src/Lib/AbstractMediaContext.hh
+++ b/src/Lib/AbstractMediaContext.hh
@@ -10,10 +10,11 @@ extern "C" {
 #include <libavformat/avformat.h>
 #include <libswresample/swresample.h>
 #include <libavcodec/avfft.h>
-#include <memory.h>
 }
 
+#include "../VivyApplication.hh"
 #include "Utils.hh"
+#include "Log.hh"
 
 namespace Vivy
 {
@@ -53,6 +54,7 @@ public:
     static inline constexpr AVMediaType avMediaType = static_cast<AVMediaType>(AVMEDIA_TYPE);
 
 protected:
+    VIVY_APP_LOGGABLE_OBJECT(AbstractMediaStream, logger)
     using Super = AbstractMediaStream<AVMEDIA_TYPE>;
 
 protected:
@@ -78,10 +80,10 @@ protected:
         if (avcodec_open2(codecContext.get(), codec, nullptr) < 0)
             throw std::runtime_error("failed to open audio decoder for a stream");
 
-        qDebug() << "[Stream] Codec" << codec->name << "id:" << codecId;
-        qDebug() << "[Stream] sample rate:" << codecParams->sample_rate;
-        qDebug() << "[Stream] bit rate:   " << codecParams->bit_rate;
-        qDebug() << "[Stream] channels:   " << codecParams->channels;
+        VIVY_LOG_CTOR() << "Codec: " << VIVY_LOG_QUOTED(codec->name) << ", id: " << codecId;
+        VIVY_LOG_CTOR() << "Sample rate: " << codecParams->sample_rate;
+        VIVY_LOG_CTOR() << "Bit rate: " << codecParams->bit_rate;
+        VIVY_LOG_CTOR() << "Channels: " << codecParams->channels;
     }
 
 public:
@@ -121,6 +123,7 @@ public:
     using StreamWeakPtr                             = std::weak_ptr<Stream>;
 
 protected:
+    VIVY_APP_LOGGABLE_OBJECT(AbstractMediaContext, logger)
     using Super = AbstractMediaContext<Stream>;
 
 public:
@@ -160,10 +163,10 @@ public:
                                                  -1, // We don't want related streams
                                                  nullptr, 0);
         if (defaultStreamIndex < 0)
-            qCritical() << "Could not find the best stream";
+            logError() << "Could not find the best stream";
 
-        qDebug() << "Opened context for" << path << "with duration" << formatPtr->duration
-                 << "and default stream index" << defaultStreamIndex;
+        logDebug() << "Opened context for " << VIVY_LOG_QUOTED(path) << " with duration "
+                   << formatPtr->duration << " and default stream index " << defaultStreamIndex;
     }
 
     virtual ~AbstractMediaContext() {}
diff --git a/src/Lib/Ass/AssFactory.cc b/src/Lib/Ass/AssFactory.cc
index 08ca4b7e08e93d57f0ca273adb463fb27b2032cf..93f8a92dddfeaee1929db77d0ca3b74dcf58dab2 100644
--- a/src/Lib/Ass/AssFactory.cc
+++ b/src/Lib/Ass/AssFactory.cc
@@ -23,9 +23,10 @@ AssFactory::initFromStorage() noexcept
         // Dectect sections
         else if (line.startsWith("[") && line.endsWith("]")) {
             currentSection = line.mid(1, line.size() - 2);
-            qDebug() << "Parsing section" << currentSection;
+            logDebug() << "Parsing section " << VIVY_LOG_QUOTED(currentSection);
             if (!validSections.contains(currentSection)) {
-                qWarning() << "The current section" << currentSection << "is invalid, ignoring it";
+                logWarning() << "The current section " << VIVY_LOG_QUOTED(currentSection)
+                             << " is invalid, ignoring it";
                 currentSection = "";
             }
         }
@@ -37,12 +38,12 @@ AssFactory::initFromStorage() noexcept
 
             // Easy way to see if the line was invalid
             if (separatorIndex < 0)
-                qWarning() << "Invalid line #" << lineIndex << ":" << line;
+                logWarning() << "Invalid line #" << lineIndex << ": " << line;
 
             // Script's info
             else if (currentSection == sectionScriptInfo) {
                 assInfo.insert(line.mid(0, separatorIndex), line.mid(baseValueIndex));
-                qDebug() << "Got line #" << lineIndex << ":" << line;
+                logDebug() << "Got line #" << lineIndex << ": " << line;
             }
 
             // Skip the headers and the comment lines
@@ -65,10 +66,10 @@ AssFactory::initFromStorage() noexcept
         for (const auto &assLine : eventsContent)
             assLines.push_back(std::make_shared<Line>(this, assLine));
     } catch (const std::runtime_error &e) {
-        qCritical() << "Failed to create ASS style or events with error:" << e.what();
+        logError() << "Failed to create ASS style or events with error: " << e.what();
     }
 
-    qDebug() << "Got" << assLines.size() << "ASS dialog lines";
+    logDebug() << "Got " << assLines.size() << " ASS dialog lines";
 
     return true;
 }
@@ -77,7 +78,7 @@ bool
 AssFactory::checkValidity() const noexcept
 {
     if (assInfo.isEmpty()) {
-        qCritical() << "Empty info section";
+        logError() << "Empty info section";
         return false;
     }
 
@@ -87,7 +88,7 @@ AssFactory::checkValidity() const noexcept
     while (it != end) {
         bool ok = false;
         if (intTypeFields.contains(it.key()) && (static_cast<void>(it.value().toInt(&ok)), !ok)) {
-            qCritical() << it.key() << "is not an integer:" << it.value();
+            logError() << it.key() << " is not an integer: " << it.value();
             return false;
         }
         ++it;
@@ -97,8 +98,8 @@ AssFactory::checkValidity() const noexcept
     for (const auto &fixedValues : checkedValues) {
         if (const auto &first = fixedValues.first;
             assInfo.contains(first) && assInfo[first] != fixedValues.second) {
-            qCritical() << "Invalid" << first << "as it should be equal to" << fixedValues.second
-                        << "but was" << assInfo[first];
+            logError() << "Invalid " << first << " as it should be equal to " << fixedValues.second
+                       << " but was " << assInfo[first];
             return false;
         }
     }
diff --git a/src/Lib/Ass/AssFactory.hh b/src/Lib/Ass/AssFactory.hh
index b220c0f1995a4c633664cb42554505080b494452..98c8f3fea31fd3513f238b77c181f61f69b9e0e1 100644
--- a/src/Lib/Ass/AssFactory.hh
+++ b/src/Lib/Ass/AssFactory.hh
@@ -1,6 +1,7 @@
-#ifndef VIVY_ASS_FACTORY_H
-#define VIVY_ASS_FACTORY_H
+#pragma once
 
+#include "../../VivyApplication.hh"
+#include "../Log.hh"
 #include "../Utils.hh"
 #include "Style.hh"
 #include "Line.hh"
@@ -13,6 +14,7 @@ namespace Vivy::Ass
 {
 class AssFactory final {
     VIVY_UNMOVABLE_OBJECT(AssFactory)
+    VIVY_APP_LOGGABLE_OBJECT(AssFactory, logger)
 
 public:
     enum class Section { ScriptInfo = 1, Styles = 2, Events = 3 };
@@ -55,7 +57,4 @@ public:
     void getStyles(QVector<StylePtr> &) const noexcept;
     void getLines(QVector<LinePtr> &) const noexcept;
 };
-
 }
-
-#endif // VIVY_ASS_FACTORY_H
diff --git a/src/Lib/Ass/Line.cc b/src/Lib/Ass/Line.cc
index fe0b257ab4585b7d0d962f2577177238cd328db9..046e313168aeaaf52a917bafc7e0a00d0f7ea718 100644
--- a/src/Lib/Ass/Line.cc
+++ b/src/Lib/Ass/Line.cc
@@ -65,7 +65,8 @@ Line::initSylFromString(const QString &line) noexcept
     // Matches syllabes like: `{\toto}{\alpha&HFF}content`
     QRegularExpression re("((?:{[^}]*})+[^{]*)");
     if (!re.isValid())
-        qFatal("The regex '%s' is not valid...", re.pattern().toStdString().c_str());
+        logFatal() << "The regex " << VIVY_LOG_QUOTED(re.pattern().toStdString().c_str())
+                   << " is not valid...";
 
     bool once = false;
     try {
@@ -77,9 +78,8 @@ Line::initSylFromString(const QString &line) noexcept
             once |= true;
         }
     } catch (const std::runtime_error &e) {
-        qCritical() << "Failed to init syllabes with line:" << line;
-        qCritical() << "Error was:" << e.what();
-        qCritical() << "Fallback to all line is one syllabe";
+        qCritical() << "Failed to init syllabes with line: " << VIVY_LOG_QUOTED(line)
+                    << ". Error was: " << e.what() << ". Fallback to all line is one syllabe";
         once = false;
     }
 
diff --git a/src/Lib/Ass/Line.hh b/src/Lib/Ass/Line.hh
index 15984da129990d512c3cdc388b67143a4d808d08..11f91de64ed4c0c204b096080b16ec453f97eb63 100644
--- a/src/Lib/Ass/Line.hh
+++ b/src/Lib/Ass/Line.hh
@@ -1,5 +1,7 @@
 #pragma once
 
+#include "../../VivyApplication.hh"
+#include "../Log.hh"
 #include "Syl.hh"
 #include "StyleProperties.hh"
 #include "Style.hh"
@@ -9,7 +11,8 @@ namespace Vivy::Ass
 class AssFactory;
 
 class Line final {
-private:
+    VIVY_APP_LOGGABLE_OBJECT(Line, logger)
+
     quint64 start{ 0 };
     quint64 end{ 0 };
     int layer{ 0 };
diff --git a/src/Lib/Ass/Style.cc b/src/Lib/Ass/Style.cc
index 1a6ce05cc1658ca8169c81a338917cb37f535ba8..2f80c9d1db58c37be84001ba4bd3ca5b3b1072ed 100644
--- a/src/Lib/Ass/Style.cc
+++ b/src/Lib/Ass/Style.cc
@@ -102,7 +102,7 @@ Style::Style(const QString &styleString)
         Utils::decodeLineToInteger(content[StyleIndex::Encoding], "Encoding is not an integer");
 
     if (encoding != 1)
-        qWarning() << "Encoding is not '1' in the ASS Style";
+        logWarning() << "Encoding is not '1' in the ASS Style";
 }
 
 QString
@@ -127,13 +127,8 @@ Color::fromString(const QString &colorString) noexcept
         startIndex++;
 
     // A valid string color is like 'AARRGGBB' for now (skipped 'aH')
-    if (colorString.size() - startIndex != 8) {
-        qCritical()
-            << "Invalid color string: size - index_start =" << (colorString.size() - startIndex)
-            << "| string =" << colorString.mid(startIndex);
-        qCritical() << "Found an invalid color string:" << colorString;
+    if (colorString.size() - startIndex != 8)
         return Color::defaultValue;
-    }
 
     bool ok_alpha = false;
     bool ok_red   = false;
@@ -144,10 +139,8 @@ Color::fromString(const QString &colorString) noexcept
     int green     = colorString.mid(startIndex + 4, 2).toInt(&ok_green, 16);
     int blue      = colorString.mid(startIndex + 6, 2).toInt(&ok_blue, 16);
 
-    if (!(ok_alpha && ok_red && ok_green && ok_blue)) {
-        qCritical() << "Found an invalid color string:" << colorString;
+    if (!(ok_alpha && ok_red && ok_green && ok_blue))
         return Color::defaultValue;
-    }
 
     return QColor(red, green, blue, alpha);
 }
diff --git a/src/Lib/Ass/Style.hh b/src/Lib/Ass/Style.hh
index 696cfc79e4b082a14e8b3ad295e31455611f283c..c6c5f10b91924355bb23f33039a1a34b278a2e2d 100644
--- a/src/Lib/Ass/Style.hh
+++ b/src/Lib/Ass/Style.hh
@@ -1,5 +1,7 @@
 #pragma once
 
+#include "../../VivyApplication.hh"
+#include "../Log.hh"
 #include "AssPrivate.hh"
 
 namespace Vivy::Ass
@@ -15,7 +17,8 @@ private:
 };
 
 class Style final {
-private:
+    VIVY_APP_LOGGABLE_OBJECT(Style, logger)
+
     QString styleName;
     QString fontName;
     int fontSize{};
diff --git a/src/Lib/Ass/Syl.cc b/src/Lib/Ass/Syl.cc
index bac0a4d0363e269ec1d984466a767881c53108a6..65d29a85cd80ce92f5a259edfed905f59f6440f1 100644
--- a/src/Lib/Ass/Syl.cc
+++ b/src/Lib/Ass/Syl.cc
@@ -1,8 +1,6 @@
 #include "Syl.hh"
 #include "Line.hh"
 
-#include <QRegularExpression>
-
 using namespace Vivy::Ass;
 
 Syl::Syl(Line *const line, const QString &lineString, ConstructMode mode) noexcept
@@ -23,9 +21,6 @@ quint64
 Syl::getDurationFromString(const QString &line) noexcept
 {
     QRegularExpression re("\\\\(?:k|K|ko|kf)(\\d+)");
-    if (!re.isValid())
-        qFatal("The regex '%s' is not valid...", re.pattern().toStdString().c_str());
-
     quint64 duration                   = 0;
     QRegularExpressionMatchIterator it = re.globalMatch(line);
 
diff --git a/src/Lib/Ass/Syl.hh b/src/Lib/Ass/Syl.hh
index 084e14e5f93ea11e001260bae8543aeef48c96d9..461746e90d6f8acf4027ddab62a3fcedf58bdb40 100644
--- a/src/Lib/Ass/Syl.hh
+++ b/src/Lib/Ass/Syl.hh
@@ -1,10 +1,6 @@
-#ifndef VIVY_SYL_H
-#define VIVY_SYL_H
+#pragma once
 
 #include "StyleProperties.hh"
-#include <QString>
-#include <QVector>
-#include <QtGlobal>
 
 namespace Vivy::Ass
 {
@@ -37,7 +33,4 @@ public:
 private:
     static quint64 getDurationFromString(const QString &) noexcept;
 };
-
 }
-
-#endif
diff --git a/src/Lib/Audio.cc b/src/Lib/Audio.cc
index 218763c297441fe96745decac1d076922f209f15..fdf79c0fc29e9d58165d701c452447412f6b9655 100644
--- a/src/Lib/Audio.cc
+++ b/src/Lib/Audio.cc
@@ -46,10 +46,10 @@ AudioStream::AudioStream(AVCodec *streamCodec, AVFormatContext *formatPtr, AVStr
 AudioStream::~AudioStream() noexcept
 {
     if (dataPtr) {
-        qDebug() << "Free data ptr";
+        VIVY_LOG_DTOR() << "Free data ptr for audio stream";
         free(dataPtr);
     }
-    qDebug() << "Delete stream object";
+    VIVY_LOG_DTOR() << "Delete audio stream object";
 }
 
 QJsonObject
@@ -68,7 +68,7 @@ AudioStream::decodeData()
 {
     if (isDecoded())
         throw std::logic_error("audio stream is already resampled");
-    qDebug() << "Launch decoding of stream" << streamIndexInContext;
+    logDebug() << "Launch decoding of stream " << streamIndexInContext;
 
     AVPacket packet;
     av_init_packet(&packet);
@@ -137,8 +137,8 @@ AudioStream::decodeData()
         av_packet_unref(&packet);
     }
 
-    qDebug() << "Decoding data finished for stream" << streamIndexInContext
-             << "dataPtr =" << dataPtr << "with dataSize =" << dataSize;
+    logDebug() << "Decoding data finished for stream " << streamIndexInContext << " with dataPtr "
+               << dataPtr << " and dataSize " << dataSize;
 }
 
 // Delete decoded data, clean up thing
diff --git a/src/Lib/CRTPStore.hh b/src/Lib/CRTPStore.hh
index 8324d9c606635812731708f754c7997bbe6c49e2..2aa6da2c7bcb34ae8ed039058049d944b82bf4cf 100644
--- a/src/Lib/CRTPStore.hh
+++ b/src/Lib/CRTPStore.hh
@@ -4,6 +4,8 @@
 #error "This is a C++ header"
 #endif
 
+#include "../VivyApplication.hh"
+#include "Log.hh"
 #include "Utils.hh"
 #include "Uuid.hh"
 
@@ -21,6 +23,8 @@ template <class Store, class Document> class CRTPStore {
     VIVY_UNMOVABLE_OBJECT(CRTPStore)
 
 protected:
+    VIVY_APP_LOGGABLE_OBJECT(CRTPStore, logger)
+
     std::map<Uuid, std::shared_ptr<Document>> documents{};
     uint newDocumentNumber{ 1 };
     static inline const QString newDocumentBaseName = QStringLiteral("Untitled ");
@@ -56,7 +60,7 @@ public:
         auto *self = static_cast<Store *>(this);
 
         if (!self->documents.contains(uuid)) {
-            qCritical() << "Couldn't find the document " << uuid;
+            logError() << "Couldn't find the document " << uuid;
             throw std::runtime_error("Can't find the document");
         }
 
diff --git a/src/Lib/Document/CRTPSubDocument.hh b/src/Lib/Document/CRTPSubDocument.hh
index c89f25c587539613cc5ad1f0c00305629f2eb984..7100a19ab30c7275d4cae86535dc0cd5c9f5c8c0 100644
--- a/src/Lib/Document/CRTPSubDocument.hh
+++ b/src/Lib/Document/CRTPSubDocument.hh
@@ -4,6 +4,8 @@
 #error "This is a C++ header"
 #endif
 
+#include "../../VivyApplication.hh"
+#include "../Log.hh"
 #include "../Utils.hh"
 #include "../Audio.hh"
 #include "../Video.hh"
@@ -15,10 +17,13 @@
 namespace Vivy
 {
 // The Big CRTP class for all common things to all the subdocuments
-template <class CRTPSubDocumentType, class Document, class Context> class CRTPSubDocument {
+template <class CRTPSubDocumentType, class Document, class Context = void> class CRTPSubDocument {
+protected:
+    VIVY_APP_LOGGABLE_OBJECT(CRTPSubDocument, logger)
+    friend std::unique_ptr<Document>;
+
 public:
     using Type = CRTPSubDocumentType;
-    friend std::unique_ptr<Document>;
 
 protected:
     QString filePath;
@@ -35,14 +40,15 @@ protected:
 public:
     static std::unique_ptr<Document> fromFile(const QString &path) noexcept
     {
-        qDebug() << "Init document from file " << path;
-        auto ret      = std::unique_ptr<Document>(new Document());
+        auto ret = std::unique_ptr<Document>(new Document());
+        ret->logDebug() << "Init document from file " << VIVY_LOG_QUOTED(path);
         ret->filePath = path;
 
         try {
             ret->initFromPath(path); // May throw
         } catch (const std::runtime_error &e) {
-            qDebug().nospace() << "Failed to init document from file " << path << ": " << e.what();
+            ret->logDebug() << "Failed to init document from file " << VIVY_LOG_QUOTED(path) << ": "
+                            << e.what();
             ret.reset();
         }
 
@@ -118,7 +124,7 @@ public:
 };
 
 // ASS document
-class AssSubDocument final : public CRTPSubDocument<AssDocumentType, AssSubDocument, void> {
+class AssSubDocument final : public CRTPSubDocument<AssDocumentType, AssSubDocument> {
     static constexpr inline bool hasContext = false;
     const QStringList &suffixList           = Vivy::Utils::assFileSuffix;
 
diff --git a/src/Lib/Document/CRTPSubDocument/AudioSubDocument.cc b/src/Lib/Document/CRTPSubDocument/AudioSubDocument.cc
index c91c3752109af9df968d21d46d32ca433557c1b9..d10fb949520f043cfd3d489f3127ddf23f4229fe 100644
--- a/src/Lib/Document/CRTPSubDocument/AudioSubDocument.cc
+++ b/src/Lib/Document/CRTPSubDocument/AudioSubDocument.cc
@@ -8,10 +8,10 @@ void
 AudioSubDocument::initFromPath(const QString &path)
 {
     if (contextPtr)
-        qDebug() << "Replacing the audio contetx by a new one for file" << path;
+        logDebug() << "Replacing the audio contetx by a new one for file " << VIVY_LOG_QUOTED(path);
     contextPtr.reset(new AudioContext(path)); // May throw
 
-    qDebug() << "Audio OK for" << path;
+    logDebug() << "Audio OK for " << VIVY_LOG_QUOTED(path);
 }
 
 QString
diff --git a/src/Lib/Document/CRTPSubDocument/VideoSubDocument.cc b/src/Lib/Document/CRTPSubDocument/VideoSubDocument.cc
index 6f837e9c219e4747f71100191cad27c9298f102e..516bb8c1bac9e2b2d4cdacb828ae707e68c43b9e 100644
--- a/src/Lib/Document/CRTPSubDocument/VideoSubDocument.cc
+++ b/src/Lib/Document/CRTPSubDocument/VideoSubDocument.cc
@@ -7,11 +7,7 @@ using namespace Vivy;
 void
 VideoSubDocument::initFromPath(const QString &path)
 {
-    if (contextPtr)
-        qDebug() << "Replacing the video contetx by a new one for file" << path;
     contextPtr.reset(new VideoContext(path)); // May throw
-
-    qDebug() << "Video OK for" << path;
 }
 
 QString
diff --git a/src/Lib/Document/VivyDocument.cc b/src/Lib/Document/VivyDocument.cc
index 6749fcc3cce6e5acb44bf584ccb9c345903835cc..6cff0405c0ea45aa91b05ce900c7660d150acbbc 100644
--- a/src/Lib/Document/VivyDocument.cc
+++ b/src/Lib/Document/VivyDocument.cc
@@ -154,18 +154,18 @@ VivyDocument::loadSubDocument(const QString &subName) noexcept
     if (detectDocumentType(file, &ableType)) {
         switch (ableType) {
         case Capabilities::AudioAble:
-            qDebug() << "Auto-detect audio document for" << subName;
+            logDebug() << "Auto-detect audio document for " << subName;
             setAudioSubDocument(file.absoluteFilePath());
             return true;
 
         case Capabilities::VideoAble:
-            qDebug() << "Auto-detect video (and try audio) document for" << subName;
+            logDebug() << "Auto-detect video (and try audio) document for " << subName;
             setVideoSubDocument(file.absoluteFilePath());
             setAudioSubDocument(file.absoluteFilePath());
             return true;
 
         case Capabilities::AssAble:
-            qDebug() << "Auto-detect ASS document for" << subName;
+            logDebug() << "Auto-detect ASS document for " << subName;
             setAssSubDocument(file.absoluteFilePath());
             return true;
         }
@@ -180,24 +180,24 @@ VivyDocument::loadSubDocument(const QString &subName, VivyDocument::Capabilities
     QFileInfo file(subName);
     Capabilities ableType;
     if (!detectDocumentType(file, &ableType)) {
-        qCritical() << "Failed to detect type for file " << subName;
+        logError() << "Failed to detect type for file " << subName;
         return false;
     }
 
     if (ableType == Capabilities::AssAble && asType == Capabilities::AssAble) {
-        qDebug() << "Create an ass subDocument from " << subName;
+        logDebug() << "Create an ass subDocument from " << subName;
         setAssSubDocument(file.absoluteFilePath());
     }
 
     else if (ableType == Capabilities::VideoAble && asType == Capabilities::VideoAble) {
-        qDebug() << "Create a video subDocument from " << subName;
+        logDebug() << "Create a video subDocument from " << subName;
         setVideoSubDocument(file.absoluteFilePath());
     }
 
     else if (const bool requestAudio = (asType == Capabilities::AudioAble);
              (ableType == Capabilities::VideoAble && requestAudio) ||
              (ableType == Capabilities::AudioAble && requestAudio)) {
-        qDebug() << "Create an audio subDocument from " << subName;
+        logDebug() << "Create an audio subDocument from " << subName;
         setAudioSubDocument(file.absoluteFilePath());
     }
 
@@ -237,7 +237,7 @@ VivyDocument::copy(const QString &newName)
 
     // Compute new paths, the document is really on disk initially
     else {
-        qDebug() << "Renaming a real file";
+        logDebug() << "Renaming a real file to " << newName;
         copyWith(newPath, [=, this]() noexcept -> void {
             documentLocation = newPath.dir();
             documentName     = newPath.baseName();
@@ -259,7 +259,7 @@ VivyDocument::rename(const QString &newName)
 
     // Compute new paths, the document is really on disk initially
     else {
-        qDebug() << "Renaming a real file";
+        logDebug() << "Renaming a real file to " << newName;
         renameWith(newPath, [=, this]() noexcept -> void {
             documentLocation = newPath.dir();
             documentName     = newPath.baseName();
@@ -285,8 +285,8 @@ VivyDocument::saveMemoryFile(const QFileInfo &newPath)
     documentLocation = newPath.absoluteDir();
     name             = newPath.absoluteFilePath();
     documentOptions  = static_cast<Options>(documentOptions & (~MemoryDocumentCreation));
-    qDebug().nospace() << "Renaming a memory file => create it on disk with { " << documentLocation
-                       << ", " << documentName << " }";
+    logDebug() << "Renaming a memory file => create it on disk with { "
+               << documentLocation.absolutePath() << ", " << documentName << " }";
     save();
 }
 
@@ -336,7 +336,7 @@ VivyDocument::getDocumentCapabilitiesString() const noexcept
 void
 VivyDocument::setAudioSubDocument(const QString filename) noexcept
 {
-    qDebug() << "[Audio sub-doc] Trying to open file" << filename;
+    logDebug() << "Audio sub-doc: Trying to open file " << filename;
     QFileInfo fileInfo(filename);
     const QString baseName = fileInfo.baseName();
 
@@ -353,7 +353,7 @@ VivyDocument::setAudioSubDocument(const QString filename) noexcept
 void
 VivyDocument::setVideoSubDocument(const QString filename) noexcept
 {
-    qDebug() << "[Video sub-doc] Trying to open file" << filename;
+    logDebug() << "Video sub-doc: Trying to open file " << filename;
     QFileInfo fileInfo(filename);
     const QString baseName = fileInfo.baseName();
 
@@ -370,7 +370,7 @@ VivyDocument::setVideoSubDocument(const QString filename) noexcept
 void
 VivyDocument::setAssSubDocument(const QString filename) noexcept
 {
-    qDebug() << "[ASS sub-doc] Trying to open file" << filename;
+    logDebug() << "ASS sub-doc: Trying to open file " << filename;
     QFileInfo fileInfo(filename);
     const QString baseName = fileInfo.baseName();
 
diff --git a/src/Lib/Document/VivyDocumentStore.cc b/src/Lib/Document/VivyDocumentStore.cc
index 909d9e270367c90526b04c3c5c5f52d75d0fd93a..ebd9a000d18dc7ad68c188be50c861a04ea451ec 100644
--- a/src/Lib/Document/VivyDocumentStore.cc
+++ b/src/Lib/Document/VivyDocumentStore.cc
@@ -9,16 +9,17 @@ VivyDocumentStore::newDocument(VivyDocument::Options opt)
     // Add the memory flag
     opt = static_cast<VivyDocument::Options>(opt | VivyDocument::MemoryDocumentCreation);
 
-    const QString newDocName = newDocumentBaseName + QString::number(newDocumentNumber++);
+    const QString newDocName = newDocumentBaseName + QString::number(newDocumentNumber);
     auto ret                 = std::make_shared<VivyDocument>(newDocName, opt);
+    newDocumentNumber++;
 
     if (ret) {
         const Uuid uuid = ret->getUuid();
         documents[uuid] = ret;
-        qDebug() << "Create new document " << newDocName << "with uuid" << uuid;
+        logDebug() << "Create new document " << newDocName << " with uuid" << uuid;
         return ret;
     } else {
-        qDebug() << "Failed to create new document " << newDocName;
+        logDebug() << "Failed to create new document " << newDocName;
         throw std::runtime_error("Failed to create the document");
     }
 }
diff --git a/src/Lib/HostOsInfo.cc b/src/Lib/HostOsInfo.cc
index 48f5bc6d82b88abad196be8db98ad895555cd467..b6628feaf875fd22881c405140a72662fe10bb9f 100644
--- a/src/Lib/HostOsInfo.cc
+++ b/src/Lib/HostOsInfo.cc
@@ -1,10 +1,13 @@
 #include "HostOsInfo.hh"
 
 #if !defined(QT_NO_OPENGL) && defined(QT_GUI_LIB)
+#define VIVY_OPENGL 1
 #include <QOpenGLContext>
+#else
+#define VIVY_OPENGL 0
 #endif
 
-#ifdef Q_OS_WIN
+#if VIVY_WIN32
 #include <qt_windows.h>
 #endif
 
@@ -13,7 +16,7 @@ using namespace Vivy::Utils;
 Qt::CaseSensitivity HostOsInfo::m_overrideFileNameCaseSensitivity = Qt::CaseSensitive;
 bool HostOsInfo::m_useOverrideFileNameCaseSensitivity             = false;
 
-#ifdef Q_OS_WIN
+#if VIVY_WIN32
 static WORD
 hostProcessorArchitecture()
 {
@@ -26,7 +29,7 @@ hostProcessorArchitecture()
 HostOsInfo::HostArchitecture
 HostOsInfo::hostArchitecture()
 {
-#ifdef Q_OS_WIN
+#if VIVY_WIN32
     static const WORD processorArchitecture = hostProcessorArchitecture();
     switch (processorArchitecture) {
     case PROCESSOR_ARCHITECTURE_AMD64: return HostOsInfo::HostArchitectureAMD64;
@@ -54,16 +57,15 @@ HostOsInfo::unsetOverrideFileNameCaseSensitivity()
 }
 
 bool
-HostOsInfo::canCreateOpenGLContext(QString *errorMessage)
+HostOsInfo::canCreateOpenGLContext([[maybe_unused]] QString *errorMessage)
 {
-#if defined(QT_NO_OPENGL) || !defined(QT_GUI_LIB)
-    Q_UNUSED(errorMessage)
-    return false;
-#else
+#if VIVY_OPENGL
     static const bool canCreate = QOpenGLContext().create();
     if (!canCreate)
         *errorMessage =
             QCoreApplication::translate("Utils::HostOsInfo", "Cannot create OpenGL context.");
     return canCreate;
+#else
+    return false;
 #endif
 }
diff --git a/src/Lib/HostOsInfo.hh b/src/Lib/HostOsInfo.hh
index 97b2889621b1348bfdea6b5c88afd68db1090417..af00e94b2d361aba557445eab4835236b5a45cdb 100644
--- a/src/Lib/HostOsInfo.hh
+++ b/src/Lib/HostOsInfo.hh
@@ -4,27 +4,47 @@
 
 #ifdef Q_OS_WIN
 #define VIVY_HOST_EXE_SUFFIX VIVY_WIN_EXE_SUFFIX
+#define VIVY_WIN32           1
 #else
 #define VIVY_HOST_EXE_SUFFIX ""
+#define VIVY_WIN32           0
 #endif // Q_OS_WIN
 
+// Detect MacOS
+#if defined(Q_OS_DARWIN) || defined(Q_OS_MACOS)
+#define VIVY_MACOS 1
+#else
+#define VIVY_MACOS 0
+#endif
+
+// Detect Linux or UNIX
+#if defined(Q_OS_LINUX)
+#define VIVY_LINUX 1
+#define VIVY_UNIX  1
+#elif defined(Q_OS_UNIX)
+#define VIVY_LINUX 0
+#define VIVY_UNIX  1
+#else
+#define VIVY_LINUX 0
+#define VIVY_UNIX  0
+#endif
+
 namespace Vivy::Utils
 {
 class HostOsInfo {
 public:
     static constexpr OsType hostOs()
     {
-#if defined(Q_OS_WIN)
-        return OsTypeWindows;
-#elif defined(Q_OS_LINUX)
-        return OsTypeLinux;
-#elif defined(Q_OS_MAC)
-        return OsTypeMac;
-#elif defined(Q_OS_UNIX)
-        return OsTypeOtherUnix;
-#else
-        return OsTypeOther;
-#endif
+        if constexpr (VIVY_WIN32)
+            return OsTypeWindows;
+        else if constexpr (VIVY_LINUX)
+            return OsTypeLinux;
+        else if constexpr (VIVY_MACOS)
+            return OsTypeMac;
+        else if constexpr (VIVY_UNIX)
+            return OsTypeOtherUnix;
+        else
+            return OsTypeOther;
     }
 
     enum HostArchitecture {
@@ -39,14 +59,7 @@ public:
     static constexpr bool isWindowsHost() { return hostOs() == OsTypeWindows; }
     static constexpr bool isLinuxHost() { return hostOs() == OsTypeLinux; }
     static constexpr bool isMacHost() { return hostOs() == OsTypeMac; }
-    static constexpr bool isAnyUnixHost()
-    {
-#ifdef Q_OS_UNIX
-        return true;
-#else
-        return false;
-#endif
-    }
+    static constexpr bool isAnyUnixHost() { return VIVY_UNIX; }
 
     static QString withExecutableSuffix(const QString &executable)
     {
diff --git a/src/Lib/Log.cc b/src/Lib/Log.cc
new file mode 100644
index 0000000000000000000000000000000000000000..245a8df13a5bab15490a249bc809fc041333b0e5
--- /dev/null
+++ b/src/Lib/Log.cc
@@ -0,0 +1,311 @@
+#include "Log.hh"
+
+// Vivy::LogLevel utility implementation
+namespace Vivy
+{
+const std::string_view
+LogLevel::toStdStringView(const LogLevel::Level lvl) noexcept
+{
+    return LevelsStringViews[static_cast<Array::size_type>(lvl)];
+}
+}
+
+// LogSinkUpdater implementations
+namespace Vivy
+{
+StlLogSinkUpdater::StlLogSinkUpdater(MutexType *const lock, MessageQueueType *const queue) noexcept
+    : updater(lock, queue)
+{
+    setPriority(QThread::LowestPriority);
+}
+
+void
+StlLogSinkUpdater::run() noexcept
+{
+    forever {
+        if (QThread::currentThread()->isInterruptionRequested())
+            return;
+        if (updater.isMessageAvailable())
+            emit messageAvailable();
+        usleep(chrono::microseconds(10000).count()); // Sleeps for 0.1s
+    }
+}
+}
+
+// Vivy::LogSinkDispatcher and child classes implementation
+namespace Vivy
+{
+LogSinkDispatcher::~LogSinkDispatcher() noexcept {}
+
+const std::string_view
+StderrLogSinkDispatcher::trunkFileName(const char *fileName) noexcept
+{
+    using size_type = std::string_view::size_type;
+
+    static constexpr char basePrefix[]       = "/src/";
+    static constexpr size_type basePrefixLen = (sizeof(basePrefix) / sizeof(char)) - 1;
+
+    const std::string_view fileNameView{ fileName };
+    const size_type basePath = fileNameView.rfind(basePrefix);
+    return std::string_view{ fileNameView.data() + basePath + basePrefixLen,
+                             fileNameView.data() + fileNameView.size() };
+}
+
+std::string
+StderrLogSinkDispatcher::reduceFileName(const std::string_view fileName) noexcept
+{
+    return std::filesystem::path(fileName).lexically_normal();
+}
+
+StderrLogSinkDispatcher::StderrLogSinkDispatcher() noexcept
+    : LogSinkDispatcher(std::string_view{ "stderr" })
+{
+}
+
+void
+StderrLogSinkDispatcher::handleLogMessage(const std::string_view category,
+                                          const LogMessage &msg) noexcept
+{
+    std::cerr << "#(" << reduceFileName(trunkFileName(msg.getHeader().fileName)) << " +"
+              << msg.getHeader().lineNumberInFile << " | "
+              << LogLevel::toStdStringView(msg.getHeader().severity) << " -> " << category << ") "
+              << msg.getTextBuffer() << '\n';
+}
+}
+
+// Vivy::LogSink implementation
+namespace Vivy
+{
+LogSink::LogSink() noexcept
+{
+    workerThread = new StlLogSinkUpdater(&messageQueueLock, &messageQueue);
+    connect(workerThread, &QThread::finished, workerThread, &QObject::deleteLater);
+    connect(
+        workerThread, &StlLogSinkUpdater::messageAvailable, this, [this]() { flush(); },
+        Qt::QueuedConnection);
+
+    workerThread->start();
+}
+
+// Flush all messages before exiting
+LogSink::~LogSink() noexcept
+{
+    workerThread->requestInterruption();
+    workerThread->wait();
+    flush();
+}
+
+// Get the log message from the logger and add it to the sink's queue.
+void
+LogSink::recieveLogMessage(const Logger *const logger, LogMessage &&msg) noexcept
+{
+    const std::lock_guard<std::mutex> messageQueueLockGuard(messageQueueLock);
+    const bool isFatal = msg.getHeader().severity == LogLevel::Critical;
+
+    messageQueue.emplace_back(std::make_tuple<const std::string_view, LogMessage>(
+        logger->getCategoryView(), std::move(msg.sink())));
+
+    if (isFatal) {
+        std::cerr << "-->>Found a fatal log! flush the message queue and abort<<--\n";
+        flush();
+        abort();
+    }
+}
+
+// Flush all LogMessages to all of the LogSinkDispatchers. Also clear the message queue.
+void
+LogSink::flush() noexcept
+{
+    const std::lock_guard<std::mutex> messageQueueLockGuard(messageQueueLock);
+    for (std::shared_ptr<LogSinkDispatcher> dispatcher : logDispatchers) {
+        for (const auto &[category, msg] : messageQueue)
+            dispatcher->handleLogMessage(category, msg);
+    }
+    messageQueue.clear();
+}
+
+std::shared_ptr<LogSink>
+LogSink::newSink() noexcept
+{
+    struct makeSharedEnabler : public LogSink {
+        // NOTE: For make_shared with private CTor
+    };
+    return std::make_shared<makeSharedEnabler>();
+}
+
+void
+LogSink::registerLogDispatcher(std::shared_ptr<LogSinkDispatcher> dispatcher) noexcept
+{
+    logDispatchers.push_back(dispatcher);
+}
+
+void
+LogSink::registerLogger(std::shared_ptr<Logger> ptr) noexcept
+{
+    loggers.push_back(ptr);
+}
+}
+
+// Vivy::Logger implementation
+namespace Vivy
+{
+void
+Logger::sendLogMessage(LogMessage &&msg) const noexcept
+{
+    parentLogSink->recieveLogMessage(this, std::move(msg));
+}
+
+LogMessage
+Logger::logEvent(const char *fileName, const char *functionName, const int lineNumber,
+                 const LogLevel::Level logSeverity) noexcept
+{
+    return LogMessage(this, LogMessage::Header{ .fileName         = fileName,
+                                                .functionName     = functionName,
+                                                .severity         = logSeverity,
+                                                .lineNumberInFile = lineNumber });
+}
+}
+
+// Vivy::LogMessage implementation
+namespace Vivy
+{
+LogMessage::LogMessage(const Logger *const logger, const LogMessage::Header hdr) noexcept
+    : messageHeader(hdr)
+    , parentLogger(logger)
+{
+}
+
+LogMessage::LogMessage(LogMessage &&other) noexcept
+    : messageHeader(other.messageHeader)
+    , parentLogger(other.parentLogger)
+{
+    std::memcpy(this, &other, sizeof(LogMessage));
+    std::memset(reinterpret_cast<void *>(&other), 0, sizeof(LogMessage));
+}
+
+LogMessage &&
+LogMessage::sink() noexcept
+{
+    parentLogger = nullptr;
+    return std::move(*this);
+}
+
+LogMessage &
+LogMessage::operator<<(const std::string &msg) noexcept
+{
+    return (*this << msg.c_str());
+}
+
+LogMessage &
+LogMessage::operator<<(const QVariant &variant) noexcept
+{
+    return (*this << variant.toString());
+}
+
+LogMessage &
+LogMessage::operator<<(const QString &msg) noexcept
+{
+    return (*this << msg.toStdString());
+}
+
+LogMessage &
+LogMessage::operator<<(const QFileInfo &fileInfo) noexcept
+{
+    return (*this << "QFileInfo{ " << fileInfo.absoluteFilePath() << "}");
+}
+
+LogMessage &
+LogMessage::operator<<(const double *ptr) noexcept
+{
+    return (*this << "Pointer{ double, " << pointerToString<double>(ptr) << " }");
+}
+
+LogMessage &
+LogMessage::operator<<(const unsigned char c) noexcept
+{
+    return (*this << static_cast<char>(c));
+}
+
+LogMessage &
+LogMessage::operator<<(const unsigned int i) noexcept
+{
+    return (*this << static_cast<unsigned long>(i));
+}
+
+LogMessage &
+LogMessage::operator<<(const unsigned long i) noexcept
+{
+    return (*this << std::to_string(i));
+}
+
+LogMessage &
+LogMessage::operator<<(const unsigned long long i) noexcept
+{
+    return (*this << std::to_string(i));
+}
+
+LogMessage &
+LogMessage::operator<<(const long long i) noexcept
+{
+    return (*this << std::to_string(i));
+}
+
+LogMessage &
+LogMessage::operator<<(const int i) noexcept
+{
+    return (*this << static_cast<long>(i));
+}
+
+LogMessage &
+LogMessage::operator<<(const long i) noexcept
+{
+    return (*this << std::to_string(i));
+}
+
+LogMessage &
+LogMessage::operator<<(const std::string_view strv) noexcept
+{
+    for (std::size_t i = 0; (i < strv.size()) && (indexInArray < messageBufferLength - 1);
+         ++i, ++indexInArray) {
+        textBuffer[indexInArray] = strv[i];
+    }
+    textBuffer[indexInArray] = '\0';
+    return *this;
+}
+
+LogMessage &
+LogMessage::operator<<(const char *str) noexcept
+{
+    const std::size_t length = strlen(str);
+    for (std::size_t i = 0; (i < length) && (indexInArray < messageBufferLength - 1);
+         ++i, ++indexInArray) {
+        textBuffer[indexInArray] = str[i];
+    }
+    textBuffer[indexInArray] = '\0';
+    return *this;
+}
+
+LogMessage &
+LogMessage::operator<<(const char c) noexcept
+{
+    if (indexInArray < messageBufferLength - 1) {
+        textBuffer[indexInArray] = c;
+        ++indexInArray;
+        textBuffer[indexInArray] = '\0';
+    }
+    return *this;
+}
+
+const std::string_view
+LogMessage::getTextBuffer() const noexcept
+{
+    const char *txt = textBuffer.data();
+    return std::string_view{ txt, strlen(txt) };
+}
+
+LogMessage::~LogMessage() noexcept
+{
+    if (parentLogger)
+        parentLogger->sendLogMessage(std::move(*this));
+}
+}
diff --git a/src/Lib/Log.hh b/src/Lib/Log.hh
new file mode 100644
index 0000000000000000000000000000000000000000..428fb35879cc48849da4d9cd91cea82fcd0e3b60
--- /dev/null
+++ b/src/Lib/Log.hh
@@ -0,0 +1,300 @@
+#pragma once
+
+#include "Utils.hh"
+
+// Create a logger with a category
+#define VIVY_GET_LOGGER(sink, cat)                (sink)->newLogger(std::string_view{ #cat })
+#define VIVY_GET_LOGGER_BY_STORED_NAME(sink, cat) (sink)->newLogger(std::string_view{ cat })
+
+// Log something in a logger
+#define VIVY_LOG_WITH_LEVEL(log, level) (log)->logEvent(__FILE__, __func__, __LINE__, level)
+#define VIVY_LOG_WARN(log)              VIVY_LOG_WITH_LEVEL(log, Vivy::LogLevel::Warning)
+#define VIVY_LOG_DEBUG(log)             VIVY_LOG_WITH_LEVEL(log, Vivy::LogLevel::Debug)
+#define VIVY_LOG_INFO(log)              VIVY_LOG_WITH_LEVEL(log, Vivy::LogLevel::Info)
+#define VIVY_LOG_ERR(log)               VIVY_LOG_WITH_LEVEL(log, Vivy::LogLevel::Error)
+#define VIVY_LOG_FATAL(log)             VIVY_LOG_WITH_LEVEL(log, Vivy::LogLevel::Critical)
+
+// Declare a sink, with the utility function/method `flushLogSink`. This is
+// intended to be used in an object to not polluate namespaces.
+#define VIVY_DCL_LOG_SINK(sink)                          \
+    std::shared_ptr<LogSink> sink{ LogSink::newSink() }; \
+    void flushLogSink() const noexcept { (sink)->flush(); }
+
+// Declare a dispatch for a sink with no arguments in the constructor.
+#define VIVY_DCL_LOG_DISPATCH(sink, name, dispatch) \
+    std::shared_ptr<dispatch> name{ (sink)->newDispatcher<dispatch>() };
+
+// Declare a dispatch for a sink with arguments in the constructor.
+#define VIVY_DCL_LOG_DISPATCH_WITH(sink, name, dispatch, ...) \
+    std::shared_ptr<dispatch> name{ (sink)->newDispatcher<dispatch>(__VA_ARGS__) };
+
+#define VIVY_LOGGABLE_OBJECT_METHODS_ONLY(logger)                            \
+    LogMessage logFatal() const noexcept { return VIVY_LOG_FATAL(logger); }  \
+    LogMessage logError() const noexcept { return VIVY_LOG_ERR(logger); }    \
+    LogMessage logWarning() const noexcept { return VIVY_LOG_WARN(logger); } \
+    LogMessage logInfo() const noexcept { return VIVY_LOG_INFO(logger); }    \
+    LogMessage logDebug() const noexcept { return VIVY_LOG_DEBUG(logger); }
+
+// Install logger for the object.
+#define VIVY_LOGGABLE_OBJECT(sink, name, logger)                   \
+    std::shared_ptr<Logger> logger{ VIVY_GET_LOGGER(sink, name) }; \
+    VIVY_LOGGABLE_OBJECT_METHODS_ONLY(logger)
+
+// Install logger for the object. Here `name` must be a variable that can be
+// used to create a std::stding_view.
+#define VIVY_LOGGABLE_OBJECT_BY_STORED_NAME(sink, name, logger)                   \
+    std::shared_ptr<Logger> logger{ VIVY_GET_LOGGER_BY_STORED_NAME(sink, name) }; \
+    VIVY_LOGGABLE_OBJECT_METHODS_ONLY(logger)
+
+#define VIVY_LOG_CTOR() logDebug() << "<<CTOR>> "
+#define VIVY_LOG_DTOR() logDebug() << "<<DTOR>> "
+
+#define VIVY_LOG_QUOTED(something) '\'' << (something) << '\''
+
+namespace Vivy
+{
+class LogSinkDispatcher;
+class LogSink;
+class Logger;
+class LogMessage;
+
+// The severity of an event. Critical will cause the LogSink to flush all
+// messages to its emeters and then abort.
+struct LogLevel final {
+    // NOTE: The order matters:
+    enum Level : int {
+        None, // In option setup to disable logs
+        Debug,
+        Info,
+        Warning,
+        Error,
+        Critical, // Will trigger qFatal
+        ___MaxAndUnused
+    };
+
+    static const std::string_view toStdStringView(const LogLevel::Level) noexcept;
+
+private:
+    using Array = std::array<const std::string_view, ___MaxAndUnused>;
+
+    // WARN: The order matters!
+    static inline constexpr Array LevelsStringViews = { "None",   "Debug", "Info",
+                                                        "Warnin", "Error", "Critical" };
+
+    LogLevel() {}
+};
+
+// A LogSinkDispatcher will excavate LogMessages from the Sink and do something
+// with them (save to a file, display in stderr/Vivy console, etc).
+class LogSinkDispatcher {
+    VIVY_UNMOVABLE_OBJECT(LogSinkDispatcher)
+
+    const std::string dispatcherName;
+
+protected:
+    explicit LogSinkDispatcher(const std::string_view name) noexcept
+        : dispatcherName(name)
+    {
+    }
+
+public:
+    const std::string_view getDispatcherName() const noexcept { return dispatcherName; }
+    virtual ~LogSinkDispatcher() noexcept;
+
+    virtual void handleLogMessage(const std::string_view, const LogMessage &) noexcept = 0;
+};
+
+// The stderr dispatcher for logs
+class StderrLogSinkDispatcher : public LogSinkDispatcher {
+    VIVY_UNMOVABLE_OBJECT(StderrLogSinkDispatcher)
+
+    static const std::string_view trunkFileName(const char *) noexcept;
+    static std::string reduceFileName(const std::string_view) noexcept;
+
+public:
+    explicit StderrLogSinkDispatcher() noexcept;
+    void handleLogMessage(const std::string_view, const LogMessage &) noexcept override;
+};
+
+// Class used to check the messageQueue and send needFlush or something like
+// that messages. It needs to be moved to its own thread and connected with the
+// queue version of the QObject::connect method.
+template <class Mutex, class MessageQueue>
+requires MutexType<Mutex>
+class LogSinkUpdater final {
+    VIVY_UNMOVABLE_OBJECT(LogSinkUpdater)
+
+    Mutex *const messageQueueLock;
+    MessageQueue *const messageQueue;
+
+    using size_type = typename MessageQueue::size_type;
+
+public:
+    explicit LogSinkUpdater(Mutex *const lock, MessageQueue *const queue)
+        : messageQueueLock(lock)
+        , messageQueue(queue)
+    {
+        if (lock == nullptr || queue == nullptr)
+            throw std::logic_error("Can't pass null pointers for the queue and its lock");
+    }
+
+    bool isMessageAvailable() const noexcept
+    {
+        std::lock_guard<Mutex> lockGuard(*messageQueueLock);
+        size_type size = messageQueue->size();
+        return size != 0;
+    }
+};
+
+// LogSinkUpdater controller, needed because templated Q_OBJECT are not
+// supported by Qt. This controller is specialized for a std implementation of
+// the message queue. For a Qt version use the following types:
+//      using MutexType        = QMutex;
+//      using MessageQueueType = QVector<QTuple<QStringView, LogMessage>>;
+class StlLogSinkUpdater final : public QThread {
+    Q_OBJECT
+    VIVY_UNMOVABLE_OBJECT(StlLogSinkUpdater)
+
+public:
+    using MutexType        = std::mutex;
+    using MessageQueueType = std::vector<std::tuple<const std::string_view, LogMessage>>;
+
+    explicit StlLogSinkUpdater(MutexType *const, MessageQueueType *const) noexcept;
+
+    void run() noexcept override;
+
+private:
+    LogSinkUpdater<MutexType, MessageQueueType> updater;
+
+signals:
+    void messageAvailable();
+};
+
+// LogSink is the parent logger. It will recieve messages and display them to a
+// console or to std::cerr or to a file, etc. The save to over file, etc will
+// be donne by the LogSinkDispatcher.
+class LogSink : QObject {
+    Q_OBJECT
+    VIVY_UNMOVABLE_OBJECT(LogSink)
+
+    explicit LogSink() noexcept;
+
+public:
+    static std::shared_ptr<LogSink> newSink() noexcept;
+    ~LogSink() noexcept override;
+
+    void registerLogDispatcher(std::shared_ptr<LogSinkDispatcher>) noexcept;
+    void registerLogger(std::shared_ptr<Logger>) noexcept;
+    void recieveLogMessage(const Logger *const, LogMessage &&) noexcept;
+    void flush() noexcept;
+
+    template <Derived<LogSinkDispatcher> DispatcherType, typename... Args>
+    std::shared_ptr<DispatcherType> newDispatcher(Args &&...args) noexcept
+    {
+        std::shared_ptr<DispatcherType> dispatch =
+            std::make_shared<DispatcherType>(std::forward<Args>(args)...);
+        registerLogDispatcher(dispatch);
+        return dispatch;
+    }
+
+    std::shared_ptr<Logger> newLogger(const StringType auto &category) noexcept
+    {
+        std::shared_ptr<Logger> logger = std::make_shared<Logger>(this, category);
+        registerLogger(logger);
+        return logger;
+    }
+
+private:
+    StlLogSinkUpdater *workerThread;
+    std::mutex messageQueueLock{};
+    std::vector<std::tuple<const std::string_view, LogMessage>> messageQueue;
+    std::vector<std::shared_ptr<LogSinkDispatcher>> logDispatchers;
+    std::vector<std::shared_ptr<Logger>> loggers;
+};
+
+// Message to be logged, constructed by a logger then send
+class LogMessage final {
+public:
+    using TimeStamp = chrono::milliseconds;
+
+    // The header of the LogMessage
+    struct Header final {
+        const char *fileName;
+        const char *functionName;
+        const LogLevel::Level severity;
+        const int lineNumberInFile;
+    };
+
+private:
+    const Header messageHeader;
+    const TimeStamp timeStamp{ duration_cast<TimeStamp>(
+        chrono::system_clock::now().time_since_epoch()) };
+
+    static constexpr inline size_t messageBufferLength = 2048;
+    std::array<char, messageBufferLength> textBuffer{};
+    std::size_t indexInArray{ 0 };
+    const Logger *parentLogger{ nullptr };
+
+    static std::string pointerToString(const auto *ptr) noexcept
+    {
+        std::stringstream stream;
+        stream << "0x" << std::setfill('0') << std::setw(sizeof(std::intptr_t) * 2) << std::hex
+               << reinterpret_cast<const std::intptr_t>(ptr);
+        return stream.str();
+    }
+
+public:
+    VIVY_DISABLE_COPY_CTOR(LogMessage)
+    VIVY_DISABLE_ASSIGN_OPERATORS(LogMessage)
+
+    explicit LogMessage(const Logger *const, const Header) noexcept;
+    explicit LogMessage(LogMessage &&) noexcept;
+    ~LogMessage() noexcept; // The message will be send on destruction
+
+    Header const &getHeader() const noexcept { return messageHeader; }
+    const std::string_view getTextBuffer() const noexcept;
+    const TimeStamp getTimeStamp() const noexcept { return timeStamp; }
+
+    LogMessage &&sink() noexcept;
+
+    LogMessage &operator<<(const std::string &) noexcept;
+    LogMessage &operator<<(const std::string_view) noexcept;
+    LogMessage &operator<<(const QString &) noexcept;
+    LogMessage &operator<<(const QVariant &) noexcept;
+    LogMessage &operator<<(const QFileInfo &) noexcept;
+    LogMessage &operator<<(const char *) noexcept;
+    LogMessage &operator<<(const double *) noexcept;
+    LogMessage &operator<<(const char) noexcept;
+    LogMessage &operator<<(const int) noexcept;
+    LogMessage &operator<<(const long) noexcept;
+    LogMessage &operator<<(const long long) noexcept;
+    LogMessage &operator<<(const unsigned char) noexcept;
+    LogMessage &operator<<(const unsigned int) noexcept;
+    LogMessage &operator<<(const unsigned long) noexcept;
+    LogMessage &operator<<(const unsigned long long) noexcept;
+};
+
+// A logger class, a client to LogSink. Will generate and send instances of
+// LogMessage.
+class Logger final : public std::enable_shared_from_this<Logger> {
+    VIVY_UNMOVABLE_OBJECT(Logger)
+
+    LogSink *const parentLogSink;
+    const std::string logCategory;
+
+public:
+    // Templated constructor, construct from anything that can be considered as
+    // a string.
+    explicit Logger(LogSink *const sink, const StringType auto category) noexcept
+        : parentLogSink(sink)
+        , logCategory(Utils::anyStringToStdString(category))
+    {
+    }
+
+    const std::string_view getCategoryView() const noexcept { return logCategory; }
+
+    void sendLogMessage(LogMessage &&) const noexcept;
+    LogMessage logEvent(const char *fileName, const char *functionName, const int lineNumber,
+                        const LogLevel::Level) noexcept;
+};
+}
diff --git a/src/Lib/Script/CRTPLuaScriptObject.hh b/src/Lib/Script/CRTPLuaScriptObject.hh
index 3a5a51e7477de51cd507d6afe569d9f0e9f172b9..6772bb5a779fd79e8354e9c37499a71a47494b7f 100644
--- a/src/Lib/Script/CRTPLuaScriptObject.hh
+++ b/src/Lib/Script/CRTPLuaScriptObject.hh
@@ -1,5 +1,7 @@
 #pragma once
 
+#include "../../VivyApplication.hh"
+#include "../../Lib/Log.hh"
 #include "LuaContext.hh"
 #include "ScriptOption.hh"
 
@@ -15,25 +17,34 @@ namespace Vivy::Script
 #define LUA_RETURN_NOTHING(L)                       { lua_settop(L, 0); return 0; }
 // clang-format on
 
+#define method_list static constexpr inline auto
 #define script_class(theClassName) \
     class theClassName final : public CRTPLuaScriptObject<theClassName>
 
-#define method_list static constexpr inline auto
+#define LUA_DECL_GETTER(type, luaName, value)             \
+    static int luaName(lua_State *const L) noexcept       \
+    {                                                     \
+        return luaPushValue<type>(L, CHECK(L, 1)->value); \
+    }
+
+#define LUA_SCRIPTABLE_CLASS_MANUAL_CTOR(theClassName) \
+    VIVY_UNMOVABLE_OBJECT(theClassName)                \
+    static constexpr char className[] = #theClassName; \
+    friend CRTPLuaScriptObject<theClassName>;          \
+    ~theClassName() noexcept {}
 
 #define LUA_SCRIPTABLE_CLASS(theClassName)                               \
-    VIVY_UNMOVABLE_OBJECT(theClassName)                                  \
-    static constexpr char className[] = #theClassName;                   \
-    friend CRTPLuaScriptObject<theClassName>;                            \
+    LUA_SCRIPTABLE_CLASS_MANUAL_CTOR(theClassName)                       \
     theClassName(lua_State *L) noexcept                                  \
     {                                                                    \
         err(LuaContext::getContext(L))                                   \
             << "Create instance of " << theClassName::className << "\n"; \
-    }                                                                    \
-    ~theClassName() noexcept {}
+    }
 
 // The type of the thing that a job iterate over
 enum class JobIteratorType { Line = 1, Syllabe = 2 };
-static inline JobIteratorType
+
+[[maybe_unused]] static inline JobIteratorType
 getJobIteratorTypeFromString(const std::string_view it) noexcept
 {
     if (it == "LINE")
@@ -46,6 +57,8 @@ getJobIteratorTypeFromString(const std::string_view it) noexcept
 // CRTP to expose objects to Lua
 template <class Object> class CRTPLuaScriptObject {
 protected:
+    VIVY_APP_LOGGABLE_OBJECT_BY_STORED_NAME(Object::className, logger)
+
     [[noreturn]] static void luaGlobalError(lua_State *const L, const std::string &str) noexcept
     {
         const auto *const context = LuaContext::getContext(L);
@@ -54,6 +67,30 @@ protected:
         exit(EXIT_FAILURE); // lua_error should not return anything
     }
 
+    // Push something onto the stack, must be specialized
+    template <typename T> static int luaPushValue(lua_State *const L, const T &thing) noexcept;
+    using std_str  = std::string;
+    using std_strv = std::string_view;
+
+    template <> static int luaPushValue<int>(lua_State *const L, const int &thing) noexcept
+    {
+        lua_pushinteger(L, thing);
+        return 1;
+    }
+
+    template <>
+    static int luaPushValue<std_strv>(lua_State *const L, const std_strv &thing) noexcept
+    {
+        lua_pushlstring(L, thing.data(), thing.size());
+        return 1;
+    }
+
+    template <> static int luaPushValue<std_str>(lua_State *const L, const std_str &thing) noexcept
+    {
+        lua_pushstring(L, thing.c_str());
+        return 1;
+    }
+
     static bool PushFunctionFromRegistry(lua_State *const L, const int key) noexcept
     {
         if (key >= 0) {
@@ -90,7 +127,7 @@ protected:
     {
         const Object *const obj = reinterpret_cast<const Object *>(lua_topointer(L, 1));
         auto *context           = LuaContext::getContext(L);
-        err(context) << "Call destructor for " << Object::className << "\n";
+        out(context) << "Call destructor for " << Object::className;
         obj->~Object();
         return 0;
     }
@@ -321,17 +358,28 @@ public:
 
 // Holds all the free functions (well, not really free functions here...)
 script_class (FreeFunctions) {
-    LUA_SCRIPTABLE_CLASS(FreeFunctions)
+    LUA_SCRIPTABLE_CLASS_MANUAL_CTOR(FreeFunctions)
+
+    FreeFunctions(lua_State *const) noexcept;
 
     static int print(lua_State *const) noexcept;
     static int getModule(lua_State *const) noexcept;
-    static int start(lua_State *const) noexcept;
-    static int finish(lua_State *const) noexcept;
+
+    LUA_DECL_GETTER(int, start, audioStart)
+    LUA_DECL_GETTER(int, finish, audioFinish)
+    LUA_DECL_GETTER(int, width, videoWidth)
+    LUA_DECL_GETTER(int, height, videoHeight)
 
     method_list metaMethods = { luaRegDefaultGC };
     method_list methods     = { LUA_DECL_METHOD(FreeFunctions, getModule),
                             LUA_DECL_METHOD(FreeFunctions, start),
                             LUA_DECL_METHOD(FreeFunctions, finish),
-                            LUA_DECL_METHOD(FreeFunctions, print), LUA_DECL_CREATE(FreeFunctions) };
+                            LUA_DECL_METHOD(FreeFunctions, width),
+                            LUA_DECL_METHOD(FreeFunctions, height),
+                            LUA_DECL_METHOD(FreeFunctions, print),
+                            LUA_DECL_CREATE(FreeFunctions) };
+
+    int videoWidth{ 0 }, videoHeight{ 0 };
+    int audioStart{ 0 }, audioFinish{ 0 };
 };
 }
diff --git a/src/Lib/Script/CRTPLuaScriptObject/FreeFunctions.cc b/src/Lib/Script/CRTPLuaScriptObject/FreeFunctions.cc
index 981491023738cdd7b2b0a3f03ce238b9497dd2ad..36c9cee70d84ed5ad0de6ccae9d6c000d0358bfe 100644
--- a/src/Lib/Script/CRTPLuaScriptObject/FreeFunctions.cc
+++ b/src/Lib/Script/CRTPLuaScriptObject/FreeFunctions.cc
@@ -1,12 +1,48 @@
 #include "../CRTPLuaScriptObject.hh"
+#include "../LuaContext.hh"
+#include "../../Document/CRTPSubDocument.hh"
+#include "../../Document/VivyDocument.hh"
+#include "../../Audio.hh"
+#include "../../Video.hh"
+#include "../../../VivyApplication.hh"
 
 using namespace Vivy::Script;
 
+FreeFunctions::FreeFunctions(lua_State *const L) noexcept
+{
+    const auto *const context = LuaContext::getContext(L);
+    out(context) << "Creating the FreeFunctions lua object!";
+
+    std::shared_ptr<VivyDocument> vivyDoc = context->getAttachedVivyDocument();
+    if (vivyDoc) {
+        if (!(vivyDoc->checkDocumentCapabilities(VivyDocument::AudioAble) &&
+              vivyDoc->checkDocumentCapabilities(VivyDocument::AssAble) &&
+              vivyDoc->checkDocumentCapabilities(VivyDocument::AudioAble))) {
+            err(context) << "The VivyDocument is not a complete document, "
+                         << "can't load it for scripting";
+            luaGlobalError(L, "Attached VivyDocument is not complete");
+        }
+
+        out(context) << "Initializing the FreeFunctions lua object with the VivyDocument";
+        std::shared_ptr<VideoStream> video = vivyDoc->getVideoSubDocument()->getDefaultStream();
+        [[maybe_unused]] std::shared_ptr<AudioStream> audio =
+            vivyDoc->getAudioSubDocument()->getDefaultStream();
+
+        videoWidth  = video->getWidth();
+        videoHeight = video->getWidth();
+        TODO(Set getters values for audio)
+    }
+
+    else {
+        out(context) << "No attached Vivy document in the Lua context";
+    }
+}
+
 int
 FreeFunctions::print(lua_State *const L) noexcept
 {
     const std::string_view arg = CHECK_STRING_VIEW(L, 1);
-    out(LuaContext::getContext(L)) << "OUT: " << arg << "\n";
+    out(LuaContext::getContext(L)) << "OUT: " << arg;
     LUA_RETURN_NOTHING(L);
 }
 
@@ -18,19 +54,3 @@ FreeFunctions::getModule(lua_State *const L) noexcept
     [[maybe_unused]] const ModuleDeclaration *const mod = context->getModule(modName);
     return 1;
 }
-
-int
-FreeFunctions::start(lua_State *const L) noexcept
-{
-    lua_settop(L, 0);
-    lua_pushinteger(L, 0);
-    return 1;
-}
-
-int
-FreeFunctions::finish(lua_State *const L) noexcept
-{
-    lua_settop(L, 0);
-    lua_pushinteger(L, 0);
-    return 1;
-}
diff --git a/src/Lib/Script/CRTPLuaScriptObject/FunctionDeclaration.cc b/src/Lib/Script/CRTPLuaScriptObject/FunctionDeclaration.cc
index 13fd29aa064784ebb97fd2eb404bb7fb40737d1d..e773a13fc394db4ac8afbb0f238dcc2964dadee4 100644
--- a/src/Lib/Script/CRTPLuaScriptObject/FunctionDeclaration.cc
+++ b/src/Lib/Script/CRTPLuaScriptObject/FunctionDeclaration.cc
@@ -40,10 +40,10 @@ FunctionDeclaration::pushToRuntime(lua_State *const L) noexcept
 
     if (self->isImported)
         err(context) << "The function \"" << self->parentScript << "::" << self->name
-                     << "\" is an imported function\n";
+                     << "\" is an imported function";
 
     else if (context->registerDeclaration(self) == LuaContext::Code::Error)
-        context->setFailed("Failed to register function " + self->name);
+        context->failedWith("Failed to register function " + self->name);
 
     LUA_RETURN_NOTHING(L);
 }
diff --git a/src/Lib/Script/CRTPLuaScriptObject/JobDeclaration.cc b/src/Lib/Script/CRTPLuaScriptObject/JobDeclaration.cc
index f55a170b30fb15fab2b424ddfd0da5f94394673c..c3761f3acc2e0b0484bcb25a55cd7f7dda24d885 100644
--- a/src/Lib/Script/CRTPLuaScriptObject/JobDeclaration.cc
+++ b/src/Lib/Script/CRTPLuaScriptObject/JobDeclaration.cc
@@ -34,14 +34,14 @@ JobDeclaration::setOptions(lua_State *L) noexcept
 {
     auto *const context       = LuaContext::getContext(L);
     JobDeclaration *const job = JobDeclaration::CHECK(L, 1);
-    err(context) << "Set options for job '" << job->name << "'\n";
+    err(context) << "Set options for job '" << job->name << "'";
 
     IterateOverArray(L, 2, [&context, job, L]() noexcept -> bool {
         OptionDeclaration *opt = OptionDeclaration::CHECK(L, -1);
 
         for (const auto &[name, value] : opt->options) {
             err(context) << "\tregister option \"" << name << "\" with type \""
-                         << value.getSignature() << "\"\n";
+                         << value.getSignature() << "\"";
         }
 
         job->options.push_back(opt);
@@ -70,11 +70,11 @@ JobDeclaration::pushToRuntime(lua_State *const L) noexcept
     lua_settop(L, 1);
 
     if (self->isImported)
-        err(context) << "The job \"" << self->parentScript << "::" << self->name
-                     << "\" is an imported job\n";
+        err(context)
+            << "The job \"" << self->parentScript << "::" << self->name << "\" is an imported job";
 
     else if (context->registerDeclaration(self) == LuaContext::Code::Error)
-        context->setFailed("Failed to register job " + self->name);
+        context->failedWith("Failed to register job " + self->name);
 
     LUA_RETURN_NOTHING(L);
 }
diff --git a/src/Lib/Script/CRTPLuaScriptObject/ModuleDeclaration.cc b/src/Lib/Script/CRTPLuaScriptObject/ModuleDeclaration.cc
index 5d535ea67c07b15911b7efff0225ea5dca6a3bc9..5de28117202757c0e20870b0a2204d6987b20308 100644
--- a/src/Lib/Script/CRTPLuaScriptObject/ModuleDeclaration.cc
+++ b/src/Lib/Script/CRTPLuaScriptObject/ModuleDeclaration.cc
@@ -7,12 +7,11 @@ ModuleDeclaration::resolvModules(LuaContext *const context) noexcept
 {
     for (const auto &str : importNames) {
         const ModuleDeclaration *const mod = context->getModule(str);
-        if (mod) {
+        if (mod)
             importedModules.emplace_back(mod);
-        } else {
-            context->setFailed("Failed to find needed module: " + str);
-            return false;
-        }
+
+        else
+            context->failedWith("Failed to find needed module: " + str);
     }
     return true;
 }
@@ -77,7 +76,7 @@ ModuleDeclaration::exportFunction(lua_State *const L, const int tableIndex,
 {
     auto *const context             = LuaContext::getContext(L);
     FunctionDeclaration *const func = context->getFunction(moduleName, name);
-    out(context) << "Export function " << moduleName << "::" << name << '\n';
+    out(context) << "Export function " << moduleName << "::" << name;
 
     lua_pushinteger(L, 2);
     lua_gettable(L, tableIndex);
@@ -100,7 +99,7 @@ ModuleDeclaration::exportJob(lua_State *const L, const int tableIndex, const std
     }
 
     else {
-        out(context) << "Export job " << moduleName << "::" << name << "{" << itTypeStr << "}\n";
+        out(context) << "Export job " << moduleName << "::" << name << "{" << itTypeStr << "}";
 
         lua_pushinteger(L, 3);
         lua_gettable(L, tableIndex);
@@ -212,11 +211,11 @@ ModuleDeclaration::pushToRuntime(lua_State *const L) noexcept
     Utils::uniqAndSort<std::string>(self->importNames); // Better for all std algorithms
 
     self->validateModule(L); // May abort the LUA context
-    err(context) << "Register module \"" << self->moduleName << "\" in the runtime!\n";
+    err(context) << "Register module " << VIVY_LOG_QUOTED(self->moduleName) << " in the runtime!";
     lua_settop(L, 1);
 
     if (context->registerDeclaration(self) == LuaContext::Code::Error)
-        context->setFailed("Failed to register module " + self->moduleName);
+        context->failedWith("Failed to register module " + self->moduleName);
 
     TODO(Import needed modules here !)
 
diff --git a/src/Lib/Script/LuaContext.cc b/src/Lib/Script/LuaContext.cc
index 6d5f32e54fa19cc88720710353817d6813c7e6ea..52c00dd7d2ded0365192014bbc7feea9c9fd518c 100644
--- a/src/Lib/Script/LuaContext.cc
+++ b/src/Lib/Script/LuaContext.cc
@@ -1,14 +1,13 @@
 #include "CRTPLuaScriptObject.hh"
 #include "ScriptDocument.hh"
 #include "LuaContext.hh"
+#include "../Document/VivyDocument.hh"
 
 // LuaContext implementation
 using namespace Vivy::Script;
 
-LuaContext::LuaContext(LoggerType &out, LoggerType &err) noexcept
+LuaContext::LuaContext() noexcept
     : L(luaL_newstate())
-    , streamOut(out)
-    , streamErr(err)
 {
     luaL_openlibs(L);
 
@@ -50,25 +49,34 @@ LuaContext::getContext(const lua_State *const state) noexcept
     return contextList.contains(state) ? contextList.at(state) : nullptr;
 }
 
+LuaContext::Code
+LuaContext::loadDocument(std::shared_ptr<Vivy::VivyDocument> doc) noexcept
+{
+    if (attachedVivyDocument != nullptr)
+        return Error;
+    attachedVivyDocument = doc;
+    return Success;
+}
+
 LuaContext::Code
 LuaContext::loadDocument(std::shared_ptr<ScriptDocument> doc) noexcept
 {
     const QFileInfo fileCheck(doc->getName());
     if (!fileCheck.exists()) {
         err(this) << "File " << doc->getName().toStdString() << " for document "
-                  << doc->getUuid().toString().toStdString() << " doesn't exists\n";
+                  << doc->getUuid().toString().toStdString() << " doesn't exists";
         return Code::Error;
     }
 
     lastLoadedModule = "";
     const auto rc    = loadFile(fileCheck.absoluteFilePath().toStdString().c_str());
     if (rc != Code::Success) {
-        setFailed("Failed to load the module file");
+        setFailedWith("Failed to load the module file");
         return rc;
     }
 
     if (lastLoadedModule.empty()) {
-        setFailed("No module was loaded");
+        setFailedWith("No module was loaded");
         return Code::Error;
     }
 
@@ -85,7 +93,7 @@ LuaContext::Code
 LuaContext::exec() noexcept
 {
     if (lua_pcall(L, 0, 0, 0) != LUA_OK) {
-        err(this) << getLastLuaError().toStdString().c_str() << "\n";
+        err(this) << getLastLuaError().toStdString().c_str();
         return Code::Error;
     }
     return Code::Success;
@@ -95,7 +103,7 @@ LuaContext::Code
 LuaContext::loadString(const char *str) noexcept
 {
     if (luaL_loadstring(L, str) != LUA_OK) {
-        err(this) << "Error loading string: " << getLastLuaError().toStdString() << "\n";
+        err(this) << "Error loading string: " << getLastLuaError();
         return Code::Error;
     }
     return exec();
@@ -106,8 +114,7 @@ LuaContext::loadFile(const char *file) noexcept
 {
     currentFile = file;
     if (luaL_loadfile(L, file) != LUA_OK) {
-        err(this)
-            << "Error loading file " << file << ": " << getLastLuaError().toStdString() << "\n";
+        err(this) << "Error loading file " << file << ": " << getLastLuaError();
         currentFile = "";
         return Code::Error;
     }
@@ -129,14 +136,46 @@ LuaContext::getLastLuaError() noexcept
     return ret;
 }
 
+void
+LuaContext::failedWith(const std::string &errorMessage) noexcept
+{
+    failedWith(errorMessage.c_str());
+}
+
+void
+LuaContext::failedWith(const char *errorMessage) noexcept
+{
+    setFailedWith(errorMessage);
+    lua_error(L);
+    exit(EXIT_FAILURE); // Should not be there!
+}
+
+void
+LuaContext::setFailedWith(const char *errorMessage) noexcept
+{
+    lua_pushstring(L, errorMessage);
+}
+
+void
+LuaContext::setFailedWith(const std::string &errorMessage) noexcept
+{
+    setFailedWith(errorMessage.c_str());
+}
+
 lua_State *
 LuaContext::getState() noexcept
 {
     return L;
 }
 
+std::shared_ptr<Vivy::VivyDocument>
+LuaContext::getAttachedVivyDocument() const noexcept
+{
+    return attachedVivyDocument;
+}
+
 bool
-LuaContext::moduleExists(string_view mod) const noexcept
+LuaContext::moduleExists(const std::string_view mod) const noexcept
 {
     return modules.count(mod) >= 1;
 }
@@ -156,14 +195,14 @@ LuaContext::registerDeclaration(const JobDeclaration *const job) noexcept
 }
 
 LuaContext::Code
-LuaContext::registerToMapTuple(
-    string_view module, string_view name,
-    std::map<std::tuple<string_view, string_view>, const int> *const map) noexcept
+LuaContext::registerToMapTuple(const std::string_view module, const std::string_view name,
+                               std::map<std::tuple<const std::string_view, const std::string_view>,
+                                        const int> *const map) noexcept
 {
     if (map->count({ module, name }) >= 1)
         return Error;
 
-    streamErr << "Register \"" << module << "::" << name << "\"\n";
+    logInfo() << "Register " << module << "::" << name;
 
     // WARN: The object needs to be already checked!
     const int r = luaL_ref(L, LUA_REGISTRYINDEX);
@@ -175,11 +214,12 @@ LuaContext::Code
 LuaContext::registerDeclaration(const ModuleDeclaration *const mod) noexcept
 {
     if (moduleExists(mod->moduleName)) {
-        streamErr << "The module \"" << mod->moduleName << "\" is already registered!";
+        logError()
+            << "The module " << VIVY_LOG_QUOTED(mod->moduleName) << " is already registered!";
         return Code::Error;
     }
 
-    streamErr << "Register module \"" << mod->moduleName << "\"\n";
+    logInfo() << "Register module " << VIVY_LOG_QUOTED(mod->moduleName);
 
     ModuleDeclaration::CHECK(L, -1);
     const int r = luaL_ref(L, LUA_REGISTRYINDEX);
@@ -247,25 +287,6 @@ LuaContext::getAllModuleName() const noexcept
     return moduleNames;
 }
 
-void
-LuaContext::setFailed(const std::string &msg) noexcept
-{
-    failureString = msg;
-    streamErr << "LuaContext is now in failure state: " << msg << "\n";
-}
-
-bool
-LuaContext::isFailed() const noexcept
-{
-    return failureString.size() != 0;
-}
-
-const std::string &
-LuaContext::getFailureDescription() const noexcept
-{
-    return failureString;
-}
-
 const std::string &
 LuaContext::getCurrentLuaFile() const
 {
diff --git a/src/Lib/Script/LuaContext.hh b/src/Lib/Script/LuaContext.hh
index afc55e82b59d60d58631d393aa54d16bb6b5e5fb..318751abaeb7ad0e7cbf53616a20b7aad56a8a0b 100644
--- a/src/Lib/Script/LuaContext.hh
+++ b/src/Lib/Script/LuaContext.hh
@@ -1,5 +1,7 @@
 #pragma once
 
+#include "../../VivyApplication.hh"
+#include "../Log.hh"
 #include "../Utils.hh"
 
 struct lua_State;
@@ -7,6 +9,7 @@ struct lua_State;
 namespace Vivy
 {
 class ScriptDocument;
+class VivyDocument;
 }
 
 namespace Vivy::Script
@@ -22,42 +25,47 @@ namespace Vivy::Script
 // New Lua script instance
 class LuaContext final {
     VIVY_UNMOVABLE_OBJECT(LuaContext)
-
-public:
-    using LoggerType = std::stringstream;
-
-private:
-    using string_view = const std::string_view;
+    VIVY_APP_LOGGABLE_OBJECT(LuaContext, logger)
 
     lua_State *L{ nullptr };
+    std::shared_ptr<Vivy::VivyDocument> attachedVivyDocument{ nullptr };
 
     std::string failureString{};
     std::string currentFile{};
     std::string lastLoadedModule{};
 
-    std::map<string_view, const int> modules                            = {};
-    std::map<std::tuple<string_view, string_view>, const int> functions = {};
-    std::map<std::tuple<string_view, string_view>, const int> jobs      = {};
+    std::map<const std::string_view, const int> modules                                       = {};
+    std::map<std::tuple<const std::string_view, const std::string_view>, const int> functions = {};
+    std::map<std::tuple<const std::string_view, const std::string_view>, const int> jobs      = {};
 
     static inline std::map<const lua_State *const, LuaContext *const> contextList = {};
 
-    LoggerType &streamOut;
-    LoggerType &streamErr;
-
 public:
     enum class Code { Success, Error };
     static constexpr inline Code Success = Code::Success;
     static constexpr inline Code Error   = Code::Error;
 
-    LuaContext(LoggerType &out, LoggerType &err) noexcept;
+    LuaContext() noexcept;
     ~LuaContext() noexcept;
 
     Code loadDocument(std::shared_ptr<ScriptDocument>) noexcept;
+    Code loadDocument(std::shared_ptr<Vivy::VivyDocument>) noexcept;
+
     QString getLastLuaError() noexcept;
+
+    // Fail and abort the LuaContext
+    [[noreturn]] void failedWith(const std::string &) noexcept;
+    [[noreturn]] void failedWith(const char *) noexcept;
+
+    // Set the error message for the lua context and continue
+    void setFailedWith(const char *) noexcept;
+    void setFailedWith(const std::string &) noexcept;
+
     lua_State *getState() noexcept;
+    std::shared_ptr<Vivy::VivyDocument> getAttachedVivyDocument() const noexcept;
 
-    auto &getOutputStream() noexcept { return streamOut; }
-    auto &getErrorStream() noexcept { return streamErr; }
+    decltype(auto) getOutputStream() const noexcept { return logInfo(); }
+    decltype(auto) getErrorStream() const noexcept { return logError(); }
 
     operator lua_State *() noexcept { return L; }
     static LuaContext *getContext(const lua_State *const) noexcept;
@@ -74,12 +82,6 @@ public:
     JobDeclaration *getJob(const std::string_view, const std::string_view) const noexcept;
     const std::vector<std::string_view> getAllModuleName() const noexcept;
 
-    // Some verifications gone wrong, this is not a Lua error, this is a script
-    // runtime error.
-    void setFailed(const std::string &) noexcept;
-    bool isFailed() const noexcept;
-    const std::string &getFailureDescription() const noexcept;
-
     // Makes only sens when called from within loadFile and loadPackagedFile
     const std::string &getCurrentLuaFile() const;
     void setLastLoadedModule(const std::string &) noexcept;
@@ -92,23 +94,23 @@ private:
     Code loadFile(const char *) noexcept;
     void loadPackagedFile(const QString &) noexcept;
 
-    Code registerToMapTuple(
-        const std::string_view module, const std::string_view name,
-        std::map<std::tuple<string_view, string_view>, const int> *const mapPtr) noexcept;
-    bool moduleExists(string_view) const noexcept;
+    Code registerToMapTuple(const std::string_view module, const std::string_view name,
+                            std::map<std::tuple<const std::string_view, const std::string_view>,
+                                     const int> *const mapPtr) noexcept;
+    bool moduleExists(const std::string_view) const noexcept;
 
     // Swap the env, needed before executing a user script!
     void swapEnv() noexcept;
 };
 
-static inline auto &
-out(LuaContext *const context) noexcept
+[[maybe_unused]] static inline decltype(auto)
+out(const LuaContext *const context) noexcept
 {
     return context->getOutputStream();
 }
 
-static inline auto &
-err(LuaContext *const context) noexcept
+[[maybe_unused]] static inline decltype(auto)
+err(const LuaContext *const context) noexcept
 {
     return context->getErrorStream();
 }
diff --git a/src/Lib/Script/ScriptStore.cc b/src/Lib/Script/ScriptStore.cc
index 593809f7bb314fe5692c92be568b9d05f7c552da..44b8e5d16652b9ac658debbdd7ec40c7ea1bfeba 100644
--- a/src/Lib/Script/ScriptStore.cc
+++ b/src/Lib/Script/ScriptStore.cc
@@ -41,35 +41,26 @@ ScriptStore::getLoadedScripts() const noexcept
 void
 ScriptStore::resetLoadedScripts() noexcept
 {
-    luaContext.reset(new Script::LuaContext(streamOut, streamErr));
+    luaContext.reset(new Script::LuaContext());
 }
 
-std::optional<std::tuple<int, std::string>>
+bool
 ScriptStore::executeScript(Uuid id) noexcept
 {
-    const auto script = documents.at(id);
-    const bool ok     = luaContext->loadDocument(script) == Code::Success;
-    QString errorString;
-    std::string buffer;
-
-    std::cout << "STDOUT:\n";
-    while (std::getline(streamOut, buffer, '\n'))
-        std::cout << '\t' << buffer << '\n';
+    const auto &script = documents.at(id);
+    return luaContext->loadDocument(script) == Code::Success;
+}
 
-    if (!ok) {
-        // /home/.../sample-spec.module:31: bad argument #1 to 'export'...
-        std::cout << "STDERR:\n";
-        QRegExp errorDetect(QStringLiteral("^([^\\:]*)\\:(\\d+)\\:(.*)\\n?$"));
-        while (std::getline(streamErr, buffer, '\n')) {
-            std::cout << '\t' << buffer << '\n';
-            if (errorDetect.indexIn(QString::fromUtf8(buffer.c_str())) != -1) {
-                std::string errorDesc = errorDetect.cap(3).toStdString();
-                return std::tuple{ QString(errorDetect.cap(2)).toInt(), Utils::trim(errorDesc) };
-            }
-        }
+bool
+ScriptStore::executeScript(Uuid id, std::shared_ptr<Vivy::VivyDocument> doc) noexcept
+{
+    if (doc == nullptr)
+        return false;
+    if (luaContext->loadDocument(doc) != Script::LuaContext::Success) {
+        luaContext->setFailedWith("A VivyDocument is already loaded, can't attach a new one");
+        return false;
     }
-
-    return std::nullopt;
+    return executeScript(id);
 }
 
 const std::vector<std::string_view>
@@ -83,3 +74,9 @@ ScriptStore::getModule(const std::string_view str) const noexcept
 {
     return luaContext->getModule(str);
 }
+
+QString
+ScriptStore::getLastLuaError() noexcept
+{
+    return luaContext ? luaContext->getLastLuaError() : QString();
+}
diff --git a/src/Lib/Script/ScriptStore.hh b/src/Lib/Script/ScriptStore.hh
index 21d0b98309535e90ec5a3b183b79af5783bc28dd..0a1968a6d3b2579843e248605191eca7e534690c 100644
--- a/src/Lib/Script/ScriptStore.hh
+++ b/src/Lib/Script/ScriptStore.hh
@@ -1,6 +1,7 @@
 #pragma once
 
 #include "../CRTPStore.hh"
+#include "../Document/VivyDocument.hh"
 #include "ScriptDocument.hh"
 #include "LuaContext.hh"
 
@@ -18,8 +19,7 @@ class ScriptStore final : public CRTPStore<ScriptStore, ScriptDocument> {
     static constexpr inline int outBufferSize = 1024;
 
 public:
-    using Item       = std::tuple<Uuid, QString>;
-    using LoggerType = Script::LuaContext::LoggerType;
+    using Item = std::tuple<Uuid, QString>;
 
     explicit ScriptStore() noexcept;
 
@@ -27,7 +27,10 @@ public:
     void resetLoadedScripts() noexcept;
     void loadScriptFolder(const QString &folderPath);
     std::vector<Item> getLoadedScripts() const noexcept;
-    std::optional<std::tuple<int, std::string>> executeScript(Uuid) noexcept;
+    bool executeScript(Uuid) noexcept;
+    bool executeScript(Uuid, std::shared_ptr<Vivy::VivyDocument>) noexcept;
+
+    QString getLastLuaError() noexcept;
 
     // Get modules from scripts
     const std::vector<std::string_view> getLoadedModules() const noexcept;
@@ -35,7 +38,5 @@ public:
 
 private:
     std::unique_ptr<Script::LuaContext> luaContext{ nullptr };
-    LoggerType streamOut;
-    LoggerType streamErr;
 };
 }
diff --git a/src/Lib/Utils.cc b/src/Lib/Utils.cc
index d023dd7bd8ae39c87b06f71242bab94c8e2373f3..9679378c3dbed92d0da9c39d53a8c5c12a3ca1e0 100644
--- a/src/Lib/Utils.cc
+++ b/src/Lib/Utils.cc
@@ -295,9 +295,5 @@ Utils::getAnyTopLevelDocumentFileSuffixFilter() noexcept
 void
 Utils::writeAssertLocation(const char *msg)
 {
-    static bool goBoom = qEnvironmentVariableIsSet("QTC_FATAL_ASSERTS");
-    if (goBoom)
-        qFatal("SOFT ASSERT made fatal: %s", msg);
-    else
-        qDebug("SOFT ASSERT: %s", msg);
+    qFatal("ASSERT: %s", msg);
 }
diff --git a/src/Lib/Utils.hh b/src/Lib/Utils.hh
index eca3e66396085bfef587c7bca936e44898774dda..39a6bc79895b010ea85cf826c7c4a4604c1d63e9 100644
--- a/src/Lib/Utils.hh
+++ b/src/Lib/Utils.hh
@@ -4,8 +4,6 @@
 #error "This is a C++ header"
 #endif
 
-#include <qglobal.h>
-
 #define VIVY_PRAGMA(x) _Pragma(#x)
 #define TODO(x)        VIVY_PRAGMA(message("\"TODO: " #x "\""))
 
@@ -21,22 +19,43 @@
 #define VIVY_EXPORT      __attribute__((visibility("default")))
 #define VIVY_NO_EXPORT   __attribute__((visibility("hidden")))
 #define VIVY_DEPRECATED  __attribute__((__deprecated__))
+#define VIVY_CONSTRUCTOR __attribute__((constructor))
+#define VIVY_DESTRUCTOR  __attribute__((destructor))
+#define VIVY_ASSERT(cond)              \
+    {                                  \
+        if (!cond)                     \
+            VIVY_ASSERT_STRING(#cond); \
+    }
 
 // Use chrono instead of std::chrono...
 namespace chrono = std::chrono;
+using chrono::duration_cast;
 
 // Prety define for OpenMP's parallel for loop with indentation not fucked up
 // by clang-format.
 #define parallel_for \
     _Pragma("omp parallel for") for
 
-// Don't move this object, create it in one place and never move it again.
-#define VIVY_UNMOVABLE_OBJECT(classname)                                \
-    classname(const classname &) = delete;            /* Copy */        \
-    classname(classname &&)      = delete;            /* Move */        \
+// Disable copy and move assign operators
+#define VIVY_DISABLE_ASSIGN_OPERATORS(classname)                        \
     classname &operator=(const classname &) = delete; /* Copy assign */ \
     classname &operator=(classname &&) = delete;      /* Move assign */
 
+#define VIVY_DISABLE_COPY_CTOR(classname) classname(const classname &) = delete; /* Copy */
+#define VIVY_DISABLE_MOVE_CTOR(classname) classname(classname &&) = delete;      /* Move */
+
+// Don't move this object, create it in one place and never move it again.
+#define VIVY_UNMOVABLE_OBJECT(classname) \
+    VIVY_DISABLE_COPY_CTOR(classname)    \
+    VIVY_DISABLE_MOVE_CTOR(classname)    \
+    VIVY_DISABLE_ASSIGN_OPERATORS(classname)
+
+#define VIVY_DEFAULT_COPY_CTOR(classname) \
+    explicit classname(const classname &) noexcept = default; /* Copy */
+
+#define VIVY_DEFAULT_MOVE_CTOR(classname) \
+    explicit classname(classname &&) noexcept = default; /* Move  */
+
 // QStringLiteral but for regexes
 #define QRegExpLiteral(str) QRegExp(QStringLiteral(str))
 
@@ -52,8 +71,40 @@ concept PropertyConstViewable = requires(T element)
     // clang-format on
 };
 
+// Concept for mutexes
+template <typename T>
+concept MutexType = requires(T mtx)
+{
+    // clang-format off
+    { mtx.lock() }     -> std::same_as<void>;
+    { mtx.try_lock() } -> std::same_as<bool>;
+    { mtx.unlock() }   -> std::same_as<void>;
+    // clang-format on
+};
+
+// Concept with a better name for inheritence between types
 template <class T, class U>
 concept Derived = std::is_base_of<U, T>::value;
+
+// Concept for string things
+template <typename T>
+concept StringType = requires(T str)
+{
+    // clang-format off
+    typename T::iterator;   // Can iterate
+    typename T::value_type; // The `Char`
+    { str.size() }  -> std::convertible_to<std::size_t>;
+    { str.begin() } -> std::same_as<typename T::iterator>;
+    { str.end() }   -> std::same_as<typename T::iterator>;
+    { str[0] }      -> std::same_as<typename T::const_reference>;
+    // clang-format on
+};
+
+// Theme type
+enum class ThemeType { QtCreator, System, QssFile };
+
+// Prefered collor for a given type
+enum class VivyThemeType { Dark, Light, ___COUNT };
 }
 
 namespace Vivy::Utils
@@ -90,7 +141,7 @@ uniqAndSort(std::vector<T> &vec) noexcept
 }
 
 // WARN: The V1 and V2 vectors need to be sorted before calling this function.
-template <typename T> inline std::vector<T>
+template <typename T> static inline std::vector<T>
 sortedSetDifference(const std::vector<T> &v1, const std::vector<T> &v2)
 {
     std::vector<T> res{};
@@ -103,6 +154,19 @@ sortedSetDifference(const std::vector<T> &v1, const std::vector<T> &v2)
     return res;
 }
 
+static inline std::string
+anyStringToStdString(StringType auto const &str) noexcept
+{
+    std::string ret;
+    std::size_t i;
+    const std::size_t size = str.size();
+    ret.resize(size);
+    for (i = 0; i < size && str[i]; ++i)
+        ret[i] = str[i];
+    ret.resize(i);
+    return ret;
+}
+
 enum class DocumentType : quint64 {
     /* Basic types */
     Vivy = (1 << 1),
@@ -162,8 +226,7 @@ int decodeLineToInteger(const QString &item, const QString &error);
 float decodeLineToFloating(const QString &item, const QString &error);
 
 QString getBaseName(const QString &) noexcept;
-
-void writeAssertLocation(const char *msg);
+[[noreturn]] void writeAssertLocation(const char *msg);
 
 struct OsSpecificAspects final {
 private:
@@ -206,16 +269,4 @@ enum class VideoDocumentType : quint64 {
 
 // Ass document types
 enum class AssDocumentType : quint64 { ASS = Utils::toUnderlying(Utils::DocumentType::ASS) };
-
-}
-
-class QMenu;
-class QAction;
-class QTabWidget;
-class QGraphicsPixmapItem;
-
-namespace Vivy
-{
-class VivyApplication;
-class MainWindow;
 }
diff --git a/src/Lib/Uuid.cc b/src/Lib/Uuid.cc
new file mode 100644
index 0000000000000000000000000000000000000000..7062680471fe5fa08e2aeb965807eac2b0dabd52
--- /dev/null
+++ b/src/Lib/Uuid.cc
@@ -0,0 +1,13 @@
+#include "Uuid.hh"
+#include "Log.hh"
+
+Vivy::Uuid::Uuid()
+    : QUuid(QUuid::createUuid())
+{
+}
+
+Vivy::LogMessage &
+Vivy::operator<<(Vivy::LogMessage &msg, Vivy::Uuid uuid) noexcept
+{
+    return msg << "UUID{ " << uuid.toString() << " }";
+}
diff --git a/src/Lib/Uuid.hh b/src/Lib/Uuid.hh
index 068277deeac9f8b33673fa2243a0c5e30e9c2223..083e001f7192acd29c20d6ee447ebd68b8fcb8f6 100644
--- a/src/Lib/Uuid.hh
+++ b/src/Lib/Uuid.hh
@@ -2,13 +2,11 @@
 
 namespace Vivy
 {
-class Uuid : public QUuid {
-public:
-    explicit Uuid()
-        : QUuid(QUuid::createUuid())
-    {
-    }
-
+struct Uuid : QUuid {
+    explicit Uuid();
     QString toString() const noexcept { return QUuid::toString(Uuid::WithoutBraces); }
 };
+
+class LogMessage;
+LogMessage &operator<<(LogMessage &, Uuid) noexcept;
 }
diff --git a/src/Main.cc b/src/Main.cc
index 7fb3b3ff18a661594cab1def4edf71ebc839162a..1fcc24a12a4beb33e5f79e3135b0eeb6894b3ebb 100644
--- a/src/Main.cc
+++ b/src/Main.cc
@@ -1,10 +1,7 @@
 #include "VivyApplication.hh"
-#include "VivyCli.hh"
-#include "Lib/Script/LuaContext.hh"
 
 int
 main(int argc, char **argv) noexcept
 {
-    return (argc >= 2) ? (Vivy::VivyCli(argc, argv).exec())
-                       : (Vivy::VivyApplication(argc, argv).exec());
+    return Vivy::VivyApplication(argc, argv).exec();
 }
diff --git a/src/UI/AboutWindow.cc b/src/UI/AboutWindow.cc
index 420dae7bfbd3ef45a4273245400656a912d64962..2faabee9bd54aac84530089fcaabed12e1b612e2 100644
--- a/src/UI/AboutWindow.cc
+++ b/src/UI/AboutWindow.cc
@@ -55,6 +55,7 @@ AboutWindow::LicenceLabel::LicenceLabel(QWidget *parent, const QString &url,
                                         const Qt::TextFormat format)
     : QTextEdit(parent)
 {
+    VIVY_LOG_CTOR() << "Creating label for licence " << VIVY_LOG_QUOTED(url);
     QFile content(url);
     if (!content.open(QIODevice::ReadOnly | QIODevice::Text))
         throw std::runtime_error("Failed to open file that should be accessible");
@@ -74,7 +75,7 @@ AboutWindow::LicenceLabel::LicenceLabel(QWidget *parent, const QString &url,
     case Qt::PlainText:
     case Qt::RichText: setText(QString::fromUtf8(content.readAll())); break;
     case Qt::MarkdownText: setMarkdown(QString::fromUtf8(content.readAll())); break;
-    case Qt::AutoText: qCritical() << "Invalid text format for LicenceLabel" << format;
+    case Qt::AutoText: logError() << "Invalid text format for LicenceLabel" << format;
     }
 }
 
@@ -83,6 +84,7 @@ AboutWindow::AboutWindow(QWidget *parent) noexcept
     : QMainWindow(parent, Qt::Dialog)
     , panels(new QTabWidget)
 {
+    VIVY_LOG_CTOR() << "Creating the 'Vivy - About' window";
     setWindowIcon(QIcon(VIVY_ICON_APP));
     setWindowTitle("Vivy - About");
 
diff --git a/src/UI/AboutWindow.hh b/src/UI/AboutWindow.hh
index 9bff80cfce81f53374d4e7658698224387bb150d..73c8e640012f897620192da41318e23f3433113b 100644
--- a/src/UI/AboutWindow.hh
+++ b/src/UI/AboutWindow.hh
@@ -1,13 +1,14 @@
 #pragma once
 
-#ifndef __cplusplus
-#error "This is a C++ header"
-#endif
+#include "../VivyApplication.hh"
+#include "../Lib/Utils.hh"
+#include "../Lib/Log.hh"
 
 namespace Vivy
 {
 class AboutWindow final : public QMainWindow {
     Q_OBJECT
+    VIVY_APP_LOGGABLE_OBJECT(AboutWindow, logger)
 
     QTabWidget *panels;
 
@@ -20,6 +21,7 @@ class AboutWindow final : public QMainWindow {
 
     // Simple QLabel for licences
     class LicenceLabel final : public QTextEdit {
+        VIVY_APP_LOGGABLE_OBJECT(LicenceLabel, logger)
     public:
         explicit LicenceLabel(QWidget *parent, const QString &url, const Qt::TextFormat format);
         ~LicenceLabel() noexcept override;
diff --git a/src/UI/DockWidgetTitleBar.cc b/src/UI/DockWidgetTitleBar.cc
index 2e1cdaa099d9246e785e46919a317dd51cbb6785..46bc79c28f393361aef522ff04c94fee9394b6bf 100644
--- a/src/UI/DockWidgetTitleBar.cc
+++ b/src/UI/DockWidgetTitleBar.cc
@@ -7,18 +7,20 @@ DockWidgetTitleBar::DockWidgetTitleBar(QDockWidget *parent) noexcept
     , attachedDock(parent)
 {
     if (parent == nullptr)
-        qFatal("Can't pass a nullptr as a parent widget pointer");
+        logFatal() << "Can't pass a nullptr as a parent widget pointer";
 
-    auto *box = new QHBoxLayout(this);
-    box->addWidget(new QLabel(parent->windowTitle(), this));
+    const QString name = parent->windowTitle();
+    auto *const box    = new QHBoxLayout(this);
+    box->addWidget(new QLabel(name, this));
     qobject_cast<QHBoxLayout *>(layout())->setStretch(0, 1);
+    VIVY_LOG_CTOR() << "TitleBar added to dock " << VIVY_LOG_QUOTED(name);
 }
 
 void
 DockWidgetTitleBar::addToDock(QDockWidget *const dock) noexcept
 {
     DockWidgetTitleBar *const titleBar = new DockWidgetTitleBar(dock);
-    qDebug() << "Adding" << dock->windowTitle() << "to dock...";
+    titleBar->logDebug() << "Adding " << VIVY_LOG_QUOTED(dock->windowTitle()) << " to dock...";
     Utils::setTransparentBackgroundForWidget(titleBar);
     dock->setTitleBarWidget(titleBar);
 }
diff --git a/src/UI/DockWidgetTitleBar.hh b/src/UI/DockWidgetTitleBar.hh
index 620c9a947e3537b566ee45a69be521cf63b97005..f3eace6b0c63d4194878a6a9dc23b5cf6f041034 100644
--- a/src/UI/DockWidgetTitleBar.hh
+++ b/src/UI/DockWidgetTitleBar.hh
@@ -1,5 +1,7 @@
 #pragma once
 
+#include "../VivyApplication.hh"
+#include "../Lib/Log.hh"
 #include "Utils.hh"
 
 namespace Vivy
@@ -7,6 +9,7 @@ namespace Vivy
 class DockWidgetTitleBar final : public QWidget {
     Q_OBJECT
     VIVY_UNMOVABLE_OBJECT(DockWidgetTitleBar)
+    VIVY_APP_LOGGABLE_OBJECT(DockWidgetTitleBar, logger)
 
     QDockWidget *attachedDock{ nullptr };
     explicit DockWidgetTitleBar(QDockWidget *parent) noexcept;
diff --git a/src/UI/DocumentViews/AudioVisualizer.cc b/src/UI/DocumentViews/AudioVisualizer.cc
index a0215bda855b3e744f4cfe23ad6cec2b06f8ebaa..bab53a25f3755ba476fc24e7723e3dd70c699d6f 100644
--- a/src/UI/DocumentViews/AudioVisualizer.cc
+++ b/src/UI/DocumentViews/AudioVisualizer.cc
@@ -9,7 +9,6 @@ AudioVisualizer::AudioVisualizer(AudioContext::StreamPtr stream, QWidget *parent
     : QWidget(parent)
 {
     if (!stream->isDecoded()) {
-        qDebug() << "Need to decode data for stream" << stream->getStreamIndex();
         stream->decodeData();
     }
 
diff --git a/src/UI/DocumentViews/MpvContainer.cc b/src/UI/DocumentViews/MpvContainer.cc
index 425346ed36a1ef646bb5644b76034ccd307f773b..b689349e5cbb3c4f2aef696e9fc789155a1a7c75 100644
--- a/src/UI/DocumentViews/MpvContainer.cc
+++ b/src/UI/DocumentViews/MpvContainer.cc
@@ -47,7 +47,7 @@ void
 MpvContainer::registerMpvTimeCallback(std::function<void(double)> callback) noexcept
 {
     if (mpvTimeCallback)
-        qWarning() << "Override a previous mpv callback!";
+        logWarning() << "Override a previous mpv callback!";
     mpvTimeCallback = callback;
 }
 
@@ -55,7 +55,7 @@ void
 MpvContainer::registerMpvDurationCallback(std::function<void(double)> callback) noexcept
 {
     if (mpvDurationCallback)
-        qWarning() << "Override a previous mpv callback!";
+        logWarning() << "Override a previous mpv callback!";
     mpvDurationCallback = callback;
 }
 
@@ -63,7 +63,7 @@ void
 MpvContainer::closeMpv() noexcept
 {
     if (mpv) {
-        qDebug() << "Closing the MPV context";
+        logDebug() << "Closing the MPV context";
         asyncCommand(AsyncCmdType::None, { "quit", nullptr });
         registerMpvTimeCallback(nullptr);
         registerMpvDurationCallback(nullptr);
@@ -98,8 +98,7 @@ MpvContainer::handleMpvEvent(const mpv_event *const event) noexcept
         msg     = reinterpret_cast<mpv_event_log_message *>(event->data);
         msgText = QString::fromUtf8(msg->text);
         msgText.replace('\n', "");
-        qDebug().nospace().noquote()
-            << "MPV - MSG [" << msg->prefix << "] " << msg->level << ": " << msgText;
+        logDebug() << "MPV - MSG [" << msg->prefix << "] " << msg->level << ": " << msgText;
         break;
 
     case MPV_EVENT_PROPERTY_CHANGE:
@@ -117,7 +116,7 @@ MpvContainer::handleMpvEvent(const mpv_event *const event) noexcept
         else if (checkProp(prop, "pause", MPV_FORMAT_FLAG)) {
             isPlaybackPaused = *reinterpret_cast<bool *>(prop->data);
             emit mpvPlaybackToggled(!isPlaybackPaused);
-            qDebug() << "MPV -> set to" << (isPlaybackPaused ? "pause" : "play");
+            logDebug() << "MPV -> set to" << (isPlaybackPaused ? "pause" : "play");
         }
 
         break;
@@ -125,20 +124,20 @@ MpvContainer::handleMpvEvent(const mpv_event *const event) noexcept
     case MPV_EVENT_PAUSE:
         isPlaybackPaused = true;
         emit mpvPlaybackToggled(!isPlaybackPaused);
-        qDebug() << "MPV -> set to pause";
+        logDebug() << "MPV -> set to pause";
         break;
 
     case MPV_EVENT_UNPAUSE:
         isPlaybackPaused = false;
         emit mpvPlaybackToggled(!isPlaybackPaused);
-        qDebug() << "MPV -> set to play";
+        logDebug() << "MPV -> set to play";
         break;
 
-    case MPV_EVENT_START_FILE: qDebug() << "MPV: Begin of file"; break;
-    case MPV_EVENT_END_FILE: qDebug() << "MPV: Reached end of file!"; break;
+    case MPV_EVENT_START_FILE: logDebug() << "MPV: Begin of file"; break;
+    case MPV_EVENT_END_FILE: logDebug() << "MPV: Reached end of file!"; break;
 
     case MPV_EVENT_COMMAND_REPLY:
-        qDebug() << "Got return of" << event->reply_userdata;
+        logDebug() << "Got return of" << event->reply_userdata;
         handleMpvEventCommandReply(static_cast<AsyncCmdType>(event->reply_userdata));
         break;
 
@@ -182,27 +181,26 @@ MpvContainer::handleMpvEventCommandReply(const AsyncCmdType type) noexcept
     case AsyncCmdType::LoadAssFile:
     case AsyncCmdType::ReloadAss:
         sid = getAssSid();
-        qDebug() << "Load ASS file with id:" << sid;
+        logDebug() << "Load ASS file with id:" << sid;
         break;
 
     case AsyncCmdType::UnloadAss:
         sid = getAssSid();
-        qDebug().nospace() << "Unload Ass, rc = " << sid;
+        logDebug() << "Unload Ass, rc = " << sid;
         if (sid != -1)
             unloadAssFile();
         break;
 
     case AsyncCmdType::LoadFile:
         sid = getAssSid();
-        qDebug() << "MPV - CMD: File loaded by mpv, sid =" << sid;
+        logDebug() << "MPV - CMD: File loaded by mpv, sid =" << sid;
         isPlaybackPaused = false;
         mpvPause();
         unloadAssFile();
         break;
 
-    case AsyncCmdType::SeekTime: qDebug() << "MPV - CMD: Seeked playback"; break;
-
-    case AsyncCmdType::TogglePlayback: qDebug() << "MPV - CMD: Playback was toggled"; break;
+    case AsyncCmdType::SeekTime: logDebug() << "MPV - CMD: Seeked playback"; break;
+    case AsyncCmdType::TogglePlayback: logDebug() << "MPV - CMD: Playback was toggled"; break;
     }
 }
 
@@ -217,14 +215,14 @@ MpvContainer::onMpvEvent() noexcept
     }
 
     if (mpv == nullptr) {
-        qDebug() << "MPV was closed while in the event loop!";
+        logDebug() << "MPV was closed while in the event loop!";
     }
 }
 
 void
 MpvContainer::loadFile(const QString &filename) noexcept
 {
-    qDebug() << "Loading file" << filename;
+    logDebug() << "Loading file" << filename;
     if (filename.size() == 0)
         return;
 
@@ -254,7 +252,7 @@ MpvContainer::printMpvError(int rc) const noexcept
     if (rc == MPV_ERROR_SUCCESS)
         return;
 
-    qCritical() << "MPV error:" << mpv_error_string(rc);
+    logError() << "MPV error:" << mpv_error_string(rc);
 }
 
 void
@@ -274,7 +272,7 @@ MpvContainer::mpvPause() noexcept
 void
 MpvContainer::mpvTogglePlayback() noexcept
 {
-    qDebug() << "MPV: Toggling the playback";
+    logDebug() << "MPV: Toggling the playback";
     asyncCommand(AsyncCmdType::TogglePlayback, { "cycle", "pause", "up", nullptr });
 }
 
diff --git a/src/UI/DocumentViews/MpvContainer.hh b/src/UI/DocumentViews/MpvContainer.hh
index 760d5e4a32c75fc5ef6ab4d04abe4033bd4cb184..70804554d68da8cb45e9a96b17e3c177103d3bea 100644
--- a/src/UI/DocumentViews/MpvContainer.hh
+++ b/src/UI/DocumentViews/MpvContainer.hh
@@ -4,7 +4,9 @@
 #error "This is a C++ header"
 #endif
 
+#include "../../VivyApplication.hh"
 #include "../../Lib/Utils.hh"
+#include "../../Lib/Log.hh"
 
 extern "C" {
 struct mpv_handle;
@@ -16,6 +18,7 @@ namespace Vivy
 class MpvContainer final : public QWidget {
     Q_OBJECT
     VIVY_UNMOVABLE_OBJECT(MpvContainer)
+    VIVY_APP_LOGGABLE_OBJECT(MpvContainer, logger)
 
     enum AsyncCmdType : uint64_t {
         None,
diff --git a/src/UI/DocumentViews/MpvControls.cc b/src/UI/DocumentViews/MpvControls.cc
index 931239ac66d8dd4ed04255ac40f46ca73699c50c..453c87c5ff116415c0ee121c1bbd2329bfe93ca7 100644
--- a/src/UI/DocumentViews/MpvControls.cc
+++ b/src/UI/DocumentViews/MpvControls.cc
@@ -49,7 +49,6 @@ MpvControls::MpvControls(MpvContainer *passedContainer, QWidget *parent) noexcep
             });
 
     connect(progressBar, &QAbstractSlider::sliderReleased, this, [this]() noexcept -> void {
-        qDebug() << "Slider set to" << askedSliderPosition << "max was" << timeDuration.count();
         timePosition = chrono::seconds(askedSliderPosition);
         mpv->seekInFile(timePosition);
     });
diff --git a/src/UI/DocumentViews/MpvControls.hh b/src/UI/DocumentViews/MpvControls.hh
index 51b1e127450c31a2fd291c46db08b5663aa05f38..5c792dc72af07ec2866f5bc07e7bd6eaec077a7a 100644
--- a/src/UI/DocumentViews/MpvControls.hh
+++ b/src/UI/DocumentViews/MpvControls.hh
@@ -4,8 +4,8 @@
 #error "This is a C++ header"
 #endif
 
-#include "../../Lib/Utils.hh"
 #include "../../VivyApplication.hh"
+#include "../../Lib/Utils.hh"
 
 namespace Vivy
 {
diff --git a/src/UI/FakeVim/FakeVimActions.cc b/src/UI/FakeVim/FakeVimActions.cc
index 7042af0745e7a304c5e9534e61439ffce665f387..045fd3a244612bf2ee605aead272079aabd0c3b6 100644
--- a/src/UI/FakeVim/FakeVimActions.cc
+++ b/src/UI/FakeVim/FakeVimActions.cc
@@ -3,7 +3,7 @@
 
 #include "../../Lib/Utils.hh"
 
-namespace FakeVim::Internal
+namespace Vivy
 {
 #ifdef FAKEVIM_STANDALONE
 FvBaseAspect::FvBaseAspect() {}
@@ -181,4 +181,4 @@ fakeVimSettings()
     return &s;
 }
 
-} // namespace FakeVim::Internal
+}
diff --git a/src/UI/FakeVim/FakeVimActions.hh b/src/UI/FakeVim/FakeVimActions.hh
index 13895c395ead827054ff3c7dc76ea6ceb4f8b7c8..69a9a01ecaf6892ed8fd30b433c5ca9d8367c413 100644
--- a/src/UI/FakeVim/FakeVimActions.hh
+++ b/src/UI/FakeVim/FakeVimActions.hh
@@ -2,7 +2,7 @@
 
 #define FAKEVIM_STANDALONE
 
-namespace FakeVim::Internal
+namespace Vivy
 {
 class FvBaseAspect {
 public:
@@ -118,4 +118,4 @@ private:
 };
 
 FakeVimSettings *fakeVimSettings();
-} // namespace FakeVim::Internal
+}
diff --git a/src/UI/FakeVim/FakeVimHandler.cc b/src/UI/FakeVim/FakeVimHandler.cc
index 3d455d8b3b63e5900169c59ab00a53fd737fdb12..2cede1aa033ea1c6ababdef39987a71fbbf1be16 100644
--- a/src/UI/FakeVim/FakeVimHandler.cc
+++ b/src/UI/FakeVim/FakeVimHandler.cc
@@ -35,19 +35,19 @@
 
 //#define DEBUG_KEY  1
 #if defined(DEBUG_KEY) && DEBUG_KEY
-#define KEY_DEBUG(s) qDebug() << s
+#define KEY_DEBUG(s) logDebug() << s
 #else
 #define KEY_DEBUG(s)
 #endif
 
 //#define DEBUG_UNDO  1
 #if defined(DEBUG_UNDO) && DEBUG_UNDO
-#define UNDO_DEBUG(s) qDebug() << "REV" << revision() << s
+#define UNDO_DEBUG(s) logDebug() << "REV" << revision() << s
 #else
 #define UNDO_DEBUG(s)
 #endif
 
-namespace FakeVim::Internal
+namespace Vivy
 {
 ///////////////////////////////////////////////////////////////////////
 //
@@ -196,12 +196,6 @@ struct CursorPosition {
     int column = -1; // Position on line.
 };
 
-VIVY_UNUSED static QDebug
-operator<<(QDebug ts, const CursorPosition &pos)
-{
-    return ts << "(line: " << pos.line << ", column: " << pos.column << ")";
-}
-
 // vi style configuration
 
 class Mark {
@@ -281,12 +275,6 @@ struct Column {
     int logical;  // Column on screen.
 };
 
-VIVY_UNUSED static QDebug
-operator<<(QDebug ts, const Column &col)
-{
-    return ts << "(p: " << col.physical << ", l: " << col.logical << ")";
-}
-
 struct Register {
     Register() = default;
     Register(const QString &c)
@@ -302,12 +290,6 @@ struct Register {
     RangeMode rangemode = RangeCharMode;
 };
 
-VIVY_UNUSED static QDebug
-operator<<(QDebug ts, const Register &reg)
-{
-    return ts << reg.contents;
-}
-
 struct SearchData {
     QString needle;
     bool forward          = true;
@@ -950,12 +932,6 @@ Range::isValid() const
     return beginPos >= 0 && endPos >= 0;
 }
 
-static QDebug
-operator<<(QDebug ts, const Range &range)
-{
-    return ts << '[' << range.beginPos << ',' << range.endPos << ']';
-}
-
 ExCommand::ExCommand(const QString &c, const QString &a, const Range &r)
     : cmd(c)
     , args(a)
@@ -969,20 +945,6 @@ ExCommand::matches(const QString &min, const QString &full) const
     return cmd.startsWith(min) && full.startsWith(cmd);
 }
 
-VIVY_UNUSED static QDebug
-operator<<(QDebug ts, const ExCommand &cmd)
-{
-    return ts << cmd.cmd << ' ' << cmd.args << ' ' << cmd.range;
-}
-
-VIVY_UNUSED static QDebug
-operator<<(QDebug ts, const QList<QTextEdit::ExtraSelection> &sels)
-{
-    foreach(const QTextEdit::ExtraSelection &sel, sels) ts << "SEL: " << sel.cursor.anchor()
-                                                           << sel.cursor.position();
-    return ts;
-}
-
 static QString
 quoteUnprintable(const QString &ba)
 {
@@ -1004,7 +966,6 @@ static bool
 startsWithWhitespace(const QString &str, int col)
 {
     if (col > str.size()) {
-        qWarning("Wrong column");
         return false;
     }
     for (int i = 0; i < col; ++i) {
@@ -1200,11 +1161,6 @@ public:
         return key;
     }
 
-    QDebug dump(QDebug ts) const
-    {
-        return ts << m_key << '-' << m_modifiers << '-' << quoteUnprintable(m_text);
-    }
-
 private:
     int m_key                         = 0;
     int m_xkey                        = 0;
@@ -1289,12 +1245,6 @@ dotCommandFromSubMode(SubMode submode)
     return QString();
 }
 
-VIVY_UNUSED static QDebug
-operator<<(QDebug ts, const Input &input)
-{
-    return input.dump(ts);
-}
-
 class Inputs : public QVector<Input> {
 public:
     Inputs() = default;
@@ -1734,6 +1684,8 @@ struct MappingState {
 };
 
 class FakeVimHandler::Private : public QObject {
+    VIVY_APP_LOGGABLE_OBJECT(FakeVimHandler::Private, logger)
+
 public:
     Private(FakeVimHandler *parent, QWidget *widget);
     ~Private() override;
@@ -1963,7 +1915,7 @@ public:
     void movePageUp(int count = 1) { movePageDown(-count); }
     void dump(const char *msg) const
     {
-        qDebug() << msg << "POS: " << anchor() << position() << "VISUAL: " << g.visualMode;
+        logDebug() << msg << "POS: " << anchor() << position() << "VISUAL: " << g.visualMode;
     }
     void moveRight(int n = 1)
     {
@@ -2519,8 +2471,7 @@ void
 FakeVimHandler::Private::enterFakeVim()
 {
     if (m_inFakeVim) {
-        qWarning("enterFakeVim() shouldn't be called recursively!");
-        return;
+        qFatal("enterFakeVim() shouldn't be called recursively!");
     }
 
     if (!m_buffer->currentHandler)
@@ -2541,8 +2492,7 @@ void
 FakeVimHandler::Private::leaveFakeVim(bool needUpdate)
 {
     if (!m_inFakeVim) {
-        qWarning("enterFakeVim() not called before leaveFakeVim()!");
-        return;
+        qFatal("enterFakeVim() not called before leaveFakeVim()!");
     }
 
     // The command might have destroyed the editor.
@@ -2627,10 +2577,6 @@ FakeVimHandler::Private::handleEvent(QKeyEvent *ev)
     if (g.passing) {
         passShortcuts(false);
         KEY_DEBUG("PASSING PLAIN KEY..." << ev->key() << ev->text());
-        //if (input.is(',')) { // use ',,' to leave, too.
-        //    qDebug() << "FINISHED...";
-        //    return EventHandled;
-        //}
         KEY_DEBUG("   PASS TO CORE");
         return EventPassedToCore;
     }
@@ -3801,7 +3747,6 @@ FakeVimHandler::Private::updateSelection()
             selections.append(sel);
         }
     }
-    //qDebug() << "SELECTION: " << selections;
     q->selectionChanged(selections);
 }
 
@@ -3903,7 +3848,6 @@ FakeVimHandler::Private::updateMiniBuffer()
 void
 FakeVimHandler::Private::showMessage(MessageLevel level, const QString &msg)
 {
-    //qDebug() << "MSG: " << msg;
     g.currentMessage      = msg;
     g.currentMessageLevel = level;
 }
@@ -3911,7 +3855,7 @@ FakeVimHandler::Private::showMessage(MessageLevel level, const QString &msg)
 void
 FakeVimHandler::Private::notImplementedYet()
 {
-    qDebug() << "Not implemented in FakeVim";
+    logDebug() << "Not implemented in FakeVim";
     showMessage(MessageError, Tr::tr("Not implemented in FakeVim."));
 }
 
@@ -4448,9 +4392,6 @@ FakeVimHandler::Private::handleCommandMode(const Input &input)
         saveLastVisualMode();
     } else {
         leaveCurrentMode();
-        //qDebug() << "IGNORED IN COMMAND MODE: " << key << text
-        //    << " VISUAL: " << g.visualMode;
-
         // if a key which produces text was pressed, don't mark it as unhandled
         // - otherwise the text would be inserted while being in command mode
         if (input.text().isEmpty())
@@ -4493,8 +4434,6 @@ FakeVimHandler::Private::handleNoSubMode(const Input &input)
     } else if (input.is(',')) {
         passShortcuts(true);
     } else if (input.is('.')) {
-        //qDebug() << "REPEATING" << quoteUnprintable(g.dotCommand) << count()
-        //    << input;
         dotCommand.clear();
         QString savedCommand = g.dotCommand;
         g.dotCommand.clear();
@@ -5673,7 +5612,7 @@ FakeVimHandler::Private::handleExMode(const Input &input)
         handleExCommand(g.commandBuffer.contents());
         g.commandBuffer.clear();
     } else if (!g.commandBuffer.handleInput(input)) {
-        qDebug() << "IGNORED IN EX-MODE: " << input.key() << input.text();
+        logDebug() << "IGNORED IN EX-MODE: " << input.key() << input.text();
         return EventUnhandled;
     }
 
@@ -5720,7 +5659,6 @@ FakeVimHandler::Private::handleSearchSubSubMode(const Input &input)
     } else if (input.isKey(Key_Tab)) {
         g.searchBuffer.insertChar(QChar(9));
     } else if (!g.searchBuffer.handleInput(input)) {
-        //qDebug() << "IGNORED IN SEARCH MODE: " << input.key() << input.text();
         return EventUnhandled;
     }
 
@@ -5738,7 +5676,6 @@ FakeVimHandler::Private::handleSearchSubSubMode(const Input &input)
 int
 FakeVimHandler::Private::parseLineAddress(QString *cmd)
 {
-    //qDebug() << "CMD: " << cmd;
     if (cmd->isEmpty())
         return -1;
 
@@ -6156,8 +6093,7 @@ FakeVimHandler::Private::handleExMapCommand(const ExCommand &cmd0) // :map
     QString args = cmd0.args;
     bool silent  = false;
     bool unique  = false;
-    forever
-    {
+    forever {
         if (eatString("<silent>", &args)) {
             silent = true;
         } else if (eatString("<unique>", &args)) {
@@ -6186,7 +6122,6 @@ FakeVimHandler::Private::handleExMapCommand(const ExCommand &cmd0) // :map
     }
 
     Inputs key(lhs);
-    //qDebug() << "MAPPING: " << modes << lhs << rhs;
     switch (type) {
     case Unmap: foreach(char c, modes) MappingsIterator(&g.mappings, c, key).remove(); break;
     case Map: Q_FALLTHROUGH();
@@ -6306,7 +6241,6 @@ FakeVimHandler::Private::handleExNormalCommand(const ExCommand &cmd)
     // :norm[al]
     if (!cmd.matches("norm", "normal"))
         return false;
-    //qDebug() << "REPLAY NORMAL: " << quoteUnprintable(reNormal.cap(3));
     replay(cmd.args);
     return true;
 }
@@ -6468,12 +6402,8 @@ FakeVimHandler::Private::handleExWriteCommand(const ExCommand &cmd)
         beginLine = 0;
     if (endLine == -1)
         endLine = linesInDocument();
-    //qDebug() << "LINES: " << beginLine << endLine;
-    //QString prefix = cmd.args;
     const bool forced = cmd.hasBang;
-    //const bool quit = prefix.contains('q') || prefix.contains('x');
-    //const bool quitAll = quit && prefix.contains('a');
-    QString fileName = replaceTildeWithHome(cmd.args);
+    QString fileName  = replaceTildeWithHome(cmd.args);
     if (fileName.isEmpty())
         fileName = m_currentFileName;
     QFile file1(fileName);
@@ -6564,7 +6494,6 @@ FakeVimHandler::Private::handleExBangCommand(const ExCommand &cmd) // :!
         setPosition(targetPosition);
         endEditBlock();
         leaveVisualMode();
-        //qDebug() << "FILTER: " << command;
         showMessage(MessageInfo, Tr::tr("%n lines filtered.", nullptr, input.count('\n')));
     } else if (!result.isEmpty()) {
         q->extraInformationChanged(result);
@@ -6713,12 +6642,10 @@ FakeVimHandler::Private::handleExSourceCommand(const ExCommand &cmd)
         }
 
         if (line.startsWith("function")) {
-            //qDebug() << "IGNORING FUNCTION" << line;
             inFunction = true;
         } else if (inFunction && line.startsWith("endfunction")) {
             inFunction = false;
         } else if (!line.isEmpty() && !inFunction) {
-            //qDebug() << "EXECUTING: " << line;
             ExCommand cmdLocal;
             QString commandLine = QString::fromLocal8Bit(line);
             while (parseExCommand(&commandLine, &cmdLocal)) {
@@ -6756,8 +6683,6 @@ FakeVimHandler::Private::handleExCommand(const QString &line0)
         return;
     }
 
-    //qDebug() << "CMD: " << cmd;
-
     enterCommandMode(g.returnToMode);
 
     beginLargeEditBlock();
@@ -6803,7 +6728,6 @@ FakeVimHandler::Private::handleExPluginCommand(const ExCommand &cmd)
     int pos      = m_cursor.position();
     commitCursor();
     q->handleExCommandRequested(&handled, cmd);
-    //qDebug() << "HANDLER REQUEST: " << cmd.cmd << handled;
     if (handled && (m_textedit || m_plaintextedit)) {
         pullCursor();
         if (m_cursor.position() != pos)
@@ -7084,15 +7008,12 @@ void
 FakeVimHandler::Private::moveToTargetColumn()
 {
     const QTextBlock &bl = block();
-    //Column column = cursorColumn();
-    //int logical = logical
-    const int pos = lastPositionInLine(bl.blockNumber() + 1, false);
+    const int pos        = lastPositionInLine(bl.blockNumber() + 1, false);
     if (m_targetColumn == -1) {
         setPosition(pos);
         return;
     }
     const int physical = bl.position() + logicalToPhysicalColumn(m_targetColumn, bl.text());
-    //qDebug() << "CORRECTING COLUMN FROM: " << logical << "TO" << m_targetColumn;
     setPosition(qMin(pos, physical));
 }
 
@@ -7751,7 +7672,7 @@ void
 FakeVimHandler::Private::insertText(const Register &reg)
 {
     if (reg.rangemode != RangeCharMode) {
-        qWarning() << "WRONG INSERT MODE: " << reg.rangemode;
+        logWarning() << "WRONG INSERT MODE: " << reg.rangemode;
         return;
     }
     setAnchor();
@@ -8418,7 +8339,7 @@ FakeVimHandler::Private::endEditBlock()
 {
     UNDO_DEBUG("END EDIT BLOCK" << m_buffer->editBlockLevel);
     if (m_buffer->editBlockLevel <= 0) {
-        qWarning("beginEditBlock() not called before endEditBlock()!");
+        logWarning() << "beginEditBlock() not called before endEditBlock()!";
         return;
     }
     --m_buffer->editBlockLevel;
@@ -8683,7 +8604,7 @@ void
 FakeVimHandler::Private::enterInsertOrReplaceMode(Mode mode)
 {
     if (mode != InsertMode && mode != ReplaceMode) {
-        qWarning("Unexpected mode");
+        logWarning() << "Unexpected mode";
         return;
     }
     if (g.mode == mode)
@@ -8898,7 +8819,6 @@ FakeVimHandler::Private::replay(const QString &command, int repeat)
     if (repeat <= 0)
         return;
 
-    //qDebug() << "REPLAY: " << quoteUnprintable(command);
     clearCurrentMode();
     const Inputs inputs(command);
     for (int i = 0; i < repeat; ++i) {
@@ -9191,7 +9111,7 @@ FakeVimHandler::Private::changeNumberTextObject(int count)
     else
         value = num.toLongLong(&ok, base);
     if (!ok) {
-        qWarning() << "Cannot parse number:" << num << "base:" << base;
+        logWarning() << "Cannot parse number:" << num << "base:" << base;
         return false;
     }
 
@@ -9731,6 +9651,6 @@ FakeVimHandler::jumpToLocalMark(QChar mark, bool backTickMode)
     return d->jumpToMark(mark, backTickMode);
 }
 
-} // namespace FakeVim::Internal
+}
 
-Q_DECLARE_METATYPE(FakeVim::Internal::FakeVimHandler::Private::BufferDataPtr)
+Q_DECLARE_METATYPE(Vivy::FakeVimHandler::Private::BufferDataPtr)
diff --git a/src/UI/FakeVim/FakeVimHandler.hh b/src/UI/FakeVim/FakeVimHandler.hh
index a84a97ee71e5033f7976aec554c2f929d7d9a209..eb8fea4eb9cc07118b09c9d9a309cfa8161803f1 100644
--- a/src/UI/FakeVim/FakeVimHandler.hh
+++ b/src/UI/FakeVim/FakeVimHandler.hh
@@ -1,8 +1,12 @@
 #pragma once
 
+#include "../../VivyApplication.hh"
+#include "../../Lib/Log.hh"
+#include "../../Lib/Utils.hh"
+
 #define FAKEVIM_STANDALONE
 
-namespace FakeVim::Internal
+namespace Vivy
 {
 enum RangeMode {
     // Reordering first three enum items here will break
@@ -76,6 +80,8 @@ private:
 
 class FakeVimHandler : public QObject {
     Q_OBJECT
+    VIVY_UNMOVABLE_OBJECT(FakeVimHandler)
+    VIVY_APP_LOGGABLE_OBJECT(FakeVimHandler, logger)
 
 public:
     explicit FakeVimHandler(QWidget *widget, QObject *parent = nullptr);
@@ -160,6 +166,6 @@ private:
     Private *d;
 };
 
-} // namespace FakeVim::Internal
+}
 
-Q_DECLARE_METATYPE(FakeVim::Internal::ExCommand)
+Q_DECLARE_METATYPE(Vivy::ExCommand)
diff --git a/src/UI/FakeVim/FakeVimTr.hh b/src/UI/FakeVim/FakeVimTr.hh
index d2cf884ba9c40e54daabeb5f603100fa59bbf450..4f2b78e9800816bdc5c24c99ab849a5da3e8e70f 100644
--- a/src/UI/FakeVim/FakeVimTr.hh
+++ b/src/UI/FakeVim/FakeVimTr.hh
@@ -1,9 +1,9 @@
 #pragma once
 
-namespace FakeVim
+namespace Vivy
 {
 struct Tr {
     Q_DECLARE_TR_FUNCTIONS(FakeVim)
 };
 
-} // namespace FakeVim
+}
diff --git a/src/UI/LogConsole.cc b/src/UI/LogConsole.cc
new file mode 100644
index 0000000000000000000000000000000000000000..544392d5d937c1c2f9d9b78e3a5883ad935d4624
--- /dev/null
+++ b/src/UI/LogConsole.cc
@@ -0,0 +1,92 @@
+#include "LogConsole.hh"
+
+using namespace Vivy;
+
+ConsoleLogSinkDispatcher::ConsoleLogSinkDispatcher() noexcept
+    : LogSinkDispatcher(std::string_view{ "console" })
+{
+}
+
+void
+ConsoleLogSinkDispatcher::handleLogMessage(const std::string_view strv,
+                                           const LogMessage &msg) noexcept
+{
+    if (console)
+        console->handleLogMessage(strv, msg);
+}
+
+void
+ConsoleLogSinkDispatcher::attachLogConsole(LogConsole *widget) noexcept
+{
+    console = widget;
+    if (widget)
+        widget->setDispatcher(shared_from_this());
+}
+
+LogConsole::LogConsole(QWidget *parent) noexcept
+    : QListWidget(parent)
+{
+    setSortingEnabled(false);
+    setSelectionMode(QAbstractItemView::NoSelection);
+
+    const QString style = QStringLiteral("* { font-family: \"FiraCode\"; font-size: 8pt; }");
+    setStyleSheet(style);
+}
+
+LogConsole::~LogConsole() noexcept
+{
+    if (auto parentDispatcher = dispatcher.lock()) {
+        qDebug() << "Reset parent LogConsole pointer";
+        parentDispatcher->attachLogConsole(nullptr);
+    } else {
+        qCritical() << "Failed to reset LogConsole from parent";
+    }
+}
+
+void
+LogConsole::setDispatcher(std::shared_ptr<ConsoleLogSinkDispatcher> ptr) noexcept
+{
+    dispatcher = std::weak_ptr<ConsoleLogSinkDispatcher>(ptr);
+}
+
+void
+LogConsole::setMessageCountLimit(int limit)
+{
+    if (limit <= 0)
+        throw std::logic_error("Can't pass a negative count!");
+    messageLimit = limit;
+}
+
+void
+LogConsole::handleLogMessage(const std::string_view cat, const LogMessage &msg) noexcept
+{
+    while (count() >= messageLimit) {
+        QListWidgetItem *itemWidget = item(0);
+        if (itemWidget != nullptr)
+            removeItemWidget(itemWidget);
+    }
+
+    QString label;
+
+    // Magic number to reduce early allocations
+    label.reserve(std::max(static_cast<int>(cat.size()) * 2, 10));
+
+    label.append('[');
+    for (const char c : cat)
+        label.append(c);
+    label.append("] ");
+
+    for (const char c : LogLevel::toStdStringView(msg.getHeader().severity))
+        label.append(c);
+    label.append(" -> ");
+
+    // Add the content of the message
+    label.reserve(static_cast<int>(1 + label.size() +
+                                   std::max(10, static_cast<int>(msg.getTextBuffer().size()))));
+    for (const char c : msg.getTextBuffer())
+        label.append(c);
+
+    addItem(label);
+    QListWidgetItem *lastItem = item(count() - 1);
+    scrollToItem(lastItem);
+}
diff --git a/src/UI/LogConsole.hh b/src/UI/LogConsole.hh
new file mode 100644
index 0000000000000000000000000000000000000000..a388e330d81b87ff10e4ba93ee520bbf1ca30d16
--- /dev/null
+++ b/src/UI/LogConsole.hh
@@ -0,0 +1,46 @@
+#pragma once
+
+#include "Utils.hh"
+#include "../Lib/Log.hh"
+
+namespace Vivy
+{
+class LogConsole;
+class ConsoleLogSinkDispatcher;
+
+class LogConsole final : public QListWidget {
+    VIVY_UNMOVABLE_OBJECT(LogConsole)
+
+    int messageLimit{ 1'000 };
+    std::weak_ptr<ConsoleLogSinkDispatcher> dispatcher;
+
+public:
+    LogConsole(QWidget *parent = nullptr) noexcept;
+    ~LogConsole() noexcept override;
+
+    void setMessageCountLimit(int limit);
+    void handleLogMessage(const std::string_view, const LogMessage &) noexcept;
+
+    // Hacky boi to set the `LogConsole *` in the dispatcher to nullptr when
+    // the `LogConsole` is destroyed.
+    void setDispatcher(std::shared_ptr<ConsoleLogSinkDispatcher> ptr) noexcept;
+};
+
+// Log console dispatcher, this is a proxy for the LogConsole which is the real
+// widget, we don't directly set the dispatcher being the widget because the
+// QDockWidget will demete its child on destruction, but the dispatcher is
+// already managed by the std::shared_ptr...
+class ConsoleLogSinkDispatcher final
+    : public LogSinkDispatcher,
+      public std::enable_shared_from_this<ConsoleLogSinkDispatcher> {
+    VIVY_UNMOVABLE_OBJECT(ConsoleLogSinkDispatcher)
+
+    LogConsole *console{ nullptr };
+
+public:
+    explicit ConsoleLogSinkDispatcher() noexcept;
+    void handleLogMessage(const std::string_view, const LogMessage &) noexcept override;
+
+    void attachLogConsole(LogConsole *const) noexcept;
+};
+}
diff --git a/src/UI/MainWindow.cc b/src/UI/MainWindow.cc
index d789104ff1878a53cfab37214a84f64492798a48..47e764e107ac56bf06c4912a9360098dda7200dd 100644
--- a/src/UI/MainWindow.cc
+++ b/src/UI/MainWindow.cc
@@ -1,10 +1,13 @@
+#include "../Lib/Document/VivyDocumentStore.hh"
+#include "../Lib/Script/ScriptStore.hh"
+#include "../Lib/Utils.hh"
+#include "../VivyApplication.hh"
 #include "MainWindow.hh"
 #include "PropertyModel.hh"
 #include "VivyDocumentView.hh"
 #include "AboutWindow.hh"
 #include "VivyFileIconProvider.hh"
-#include "../Lib/Utils.hh"
-#include "../VivyApplication.hh"
+#include "AboutWindow.hh"
 
 #define DCL_MENU(menu, name) [[maybe_unused]] QMenu *menu##Menu = menuBar()->addMenu(name);
 
@@ -149,21 +152,18 @@ MainWindow::MainWindow() noexcept
     enableConnection(loadSubDocumentVideoAct, enableLoadSubOnDocument);
     enableConnection(loadSubDocumentAudioAct, enableLoadSubOnDocument);
 
-    // Main window has finished its construction
     statusBar()->showMessage("QSimulate has started");
 
-    // Minimal size...
     setMinimumHeight(400);
     setMinimumWidth(600);
-
-    // Always a new empty document
+    setUnifiedTitleAndToolBarOnMac(true);
     newDocument();
 }
 
 void
 MainWindow::closeEvent(QCloseEvent *event) noexcept
 {
-    qDebug() << "Closing the main window!";
+    logDebug() << "Closing the main window!";
     forEachViews<VivyDocumentView>([](VivyDocumentView *view, int) { view->closeDocument(); });
     QMainWindow::closeEvent(event);
 }
@@ -190,7 +190,7 @@ MainWindow::openProperties(int index) noexcept
         return;
     }
 
-    qDebug().nospace() << "Tab n°" << index << " was double clicked";
+    logDebug() << "Tab n°" << index << " was double clicked";
     AbstractDocumentView *current = getTab(index);
     current->openProperties();
 }
@@ -225,7 +225,7 @@ MainWindow::saveFile() noexcept
     }
 
     catch (const std::runtime_error &e) {
-        qCritical() << "Failed to save current document:" << e.what();
+        logError() << "Failed to save current document: " << e.what();
     }
 }
 
@@ -247,7 +247,7 @@ MainWindow::renameFile() noexcept
     }
 
     catch (const std::runtime_error &e) {
-        qCritical() << "Failed to save current document:" << e.what();
+        logError() << "Failed to save current document: " << e.what();
     }
 }
 
@@ -269,7 +269,7 @@ MainWindow::saveFileAs() noexcept
     }
 
     catch (const std::runtime_error &e) {
-        qCritical() << "Failed to save current document:" << e.what();
+        logError() << "Failed to save current document: " << e.what();
     }
 }
 
@@ -299,7 +299,8 @@ MainWindow::closeDocument(int index) noexcept
                &MainWindow::documentViewActionsChanged);
 
     if (documentToClose) {
-        qDebug() << "Delete document view" << documentToClose->getDocumentTabName();
+        logDebug()
+            << "Delete document view " << VIVY_LOG_QUOTED(documentToClose->getDocumentTabName());
         documentToClose->closeDocument();
         delete documentToClose;
     }
@@ -310,9 +311,9 @@ MainWindow::newDocument() noexcept
 {
     try {
         addTab(new VivyDocumentView(
-            vivyApp->documentStore.newDocument(VivyDocument::UntouchedByDefault), documents));
+            vivyApp->documentStore->newDocument(VivyDocument::UntouchedByDefault), documents));
     } catch (const std::runtime_error &e) {
-        qCritical() << "Failed to create a new empty document:" << e.what();
+        logError() << "Failed to create a new empty document: " << e.what();
     }
 }
 
@@ -322,7 +323,7 @@ MainWindow::openDocument() noexcept
     const QString filename = dialogOpenFileName("Select a document to open", QDir::homePath(),
                                                 Utils::getAnyTopLevelDocumentFileSuffixFilter());
     if (filename.isEmpty()) {
-        qWarning() << "Found an empty filename, don't open a file";
+        logDebug() << "Found an empty filename, don't open a file";
         return;
     }
 
@@ -330,29 +331,30 @@ MainWindow::openDocument() noexcept
     Utils::DocumentType fileType;
 
     if (!Utils::detectDocumentType(fileInfo, &fileType)) {
-        qWarning() << "Failed to detect file type for" << filename;
+        logWarning() << "Failed to detect file type for " << VIVY_LOG_QUOTED(filename);
         return;
     }
 
     // Handle the different types here
     try {
         if (fileType == Utils::DocumentType::Vivy)
-            addTab(new VivyDocumentView(vivyApp->documentStore.loadDocument(filename), documents));
+            addTab(new VivyDocumentView(vivyApp->documentStore->loadDocument(filename), documents));
 
         else if (fileType == Utils::DocumentType::VivyScript) {
-            auto scriptDocument = vivyApp->scriptStore.loadDocument(filename);
-            auto errorTuple     = vivyApp->scriptStore.executeScript(scriptDocument->getUuid());
+            auto scriptDocument = vivyApp->scriptStore->loadDocument(filename);
+            const bool rc       = vivyApp->scriptStore->executeScript(scriptDocument->getUuid());
             ScriptDocumentView *newView = new ScriptDocumentView(scriptDocument, documents);
 
-            if (errorTuple.has_value()) {
-                const auto &[line, desc] = errorTuple.value();
-                emit newView->luaErrorFound(line, QString::fromUtf8(desc.c_str()));
+            if (!rc) {
+                const auto &[line, desc] = std::tuple{ 0, vivyApp->scriptStore->getLastLuaError() };
+                emit newView->luaErrorFound(line, desc);
             }
 
             addTab(newView);
         }
     } catch (const std::runtime_error &e) {
-        qCritical() << "Failed to load document" << filename << "with error:" << e.what();
+        logError() << "Failed to load document " << VIVY_LOG_QUOTED(filename)
+                   << " with error: " << e.what();
     }
 }
 
@@ -411,15 +413,15 @@ MainWindow::addTab(AbstractDocumentView *const tab)
             [this, tab]() noexcept -> void {
                 int tabIndex = getIndexOfTab(tab);
                 if (tabIndex < 0) {
-                    qDebug() << "Tab was not found!";
+                    logDebug() << "Tab was not found!";
                 } else {
-                    qDebug() << "Tab was found at index" << tabIndex;
+                    logDebug() << "Tab was found at index" << tabIndex;
                     documents->setTabToolTip(tabIndex, tab->getDocumentTabToolTip());
                     documents->setTabText(tabIndex, tab->getDocumentTabName());
                 }
             });
     documentViewActionsChanged();
-    qDebug() << "View constructed successfully";
+    logDebug() << "View constructed successfully";
 }
 
 int
@@ -486,19 +488,19 @@ MainWindow::findFirstUntouchedDocument() const noexcept
 void
 MainWindow::documentViewActionsChanged() noexcept
 {
-    qInfo() << "Document view action changed";
+    logDebug() << "Document view action changed";
     viewMenu->clear();
 
     // Change document view menu if we have a document
     try {
         viewMenu->addActions(getCurrentDocumentView()->getViewsActions());
     } catch (const std::runtime_error &e) {
-        qInfo() << "No view to display:" << e.what();
+        logDebug() << "No view to display: " << e.what();
     }
 }
 
-static inline QString
-executeDialog(MainWindow *const self, QFileDialog *const dialog) noexcept
+QString
+MainWindow::executeDialog(MainWindow *const self, QFileDialog *const dialog) noexcept
 {
     bool dialogAccepted = false;
     std::unique_ptr<VivyFileIconProvider> iconProvider(new VivyFileIconProvider());
@@ -514,7 +516,8 @@ executeDialog(MainWindow *const self, QFileDialog *const dialog) noexcept
 
     const QStringList resList = dialog->selectedFiles();
     if (resList.size() != 1) {
-        qCritical() << "You must select only one file";
+        self->logError()
+            << "You must select only one file, here got " << resList.size() << " files";
         return QStringLiteral("");
     }
 
diff --git a/src/UI/MainWindow.hh b/src/UI/MainWindow.hh
index 7d375a94be296067d5ef6b2a700f04c86921f536..30a31af108f94ceb43ce3dc34aa06f905ab33c91 100644
--- a/src/UI/MainWindow.hh
+++ b/src/UI/MainWindow.hh
@@ -4,18 +4,22 @@
 #error "This is a C++ header"
 #endif
 
+#include "../VivyApplication.hh"
 #include "../Lib/Utils.hh"
+#include "../Lib/Log.hh"
 #include "../Lib/AbstractDocument.hh"
 #include "../Lib/Document/VivyDocumentStore.hh"
 #include "DocumentViews/AudioVisualizer.hh"
 #include "VivyDocumentView.hh"
 #include "ScriptDocumentView.hh"
-#include "AboutWindow.hh"
 
 namespace Vivy
 {
+class AboutWindow;
+
 class MainWindow final : public QMainWindow {
     Q_OBJECT
+    VIVY_APP_LOGGABLE_OBJECT(MainWindow, logger)
 
     QTabWidget *documents{ nullptr };
     QMenu *viewMenu{ nullptr };
@@ -53,6 +57,8 @@ private:
     QString dialogSaveFileName(const QString &title, const QString &folder,
                                const QString &filter) noexcept;
 
+    QString executeDialog(MainWindow *const self, QFileDialog *const dialog) noexcept;
+
     // Do an action with the selected filename. The 'call' variable must be
     // callable like this: call(DocumentView, Document, QString)
     template <typename DV, typename D>
@@ -61,7 +67,7 @@ private:
         const QString filename = dialogOpenFileName(title, QDir::homePath(), filter);
 
         if (filename.isEmpty()) {
-            qWarning() << "Found an empty filename, don't open a file";
+            logWarning() << "Found an empty filename, don't open a file";
             return;
         }
 
@@ -73,7 +79,7 @@ private:
                 call(view, doc, filename);
             }
         } catch (const std::runtime_error &e) {
-            qCritical() << "Operation failed with:" << e.what();
+            logError() << "Operation failed with: " << e.what();
         }
     }
 
@@ -112,5 +118,4 @@ private slots:
 
     void documentViewActionsChanged() noexcept;
 };
-
 }
diff --git a/src/UI/ScriptDocumentView.cc b/src/UI/ScriptDocumentView.cc
index b05c137d8e37f7ed26d9f66eb193d9f62a4eec3f..1f510c7c4f48db6686035121bd5229f4db3a34e1 100644
--- a/src/UI/ScriptDocumentView.cc
+++ b/src/UI/ScriptDocumentView.cc
@@ -1,4 +1,5 @@
 #include "ScriptDocumentView.hh"
+#include "MainWindow.hh"
 #include "ScriptViews/ScriptEditor.hh"
 #include "ScriptViews/ScriptHighlighter.hh"
 #include "../VivyApplication.hh"
@@ -32,12 +33,15 @@ ScriptDocumentView::ScriptDocumentView(std::shared_ptr<ScriptDocument> ptr, QWid
                                  "  font-family: \"FiraCode\";"
                                  "  font-size: 10pt"
                                  "}"));
+
+    VIVY_LOG_CTOR() << "Creating view for script " << VIVY_LOG_QUOTED(ptr->getName()) << " with "
+                    << ptr->getUuid();
 }
 
 ScriptDocumentView::~ScriptDocumentView()
 {
     setUseFakeVimEditor(false);
-    qDebug() << "~ScriptDocumentView";
+    VIVY_LOG_DTOR() << "Closing a script view";
 }
 
 void
diff --git a/src/UI/ScriptDocumentView.hh b/src/UI/ScriptDocumentView.hh
index c1acc9c31524571e2b7eedf0703991a12296b8fe..0f7907352447f3bcb82c1081c242b519141016fd 100644
--- a/src/UI/ScriptDocumentView.hh
+++ b/src/UI/ScriptDocumentView.hh
@@ -5,7 +5,9 @@
 #error "This is a C++ header"
 #endif
 
+#include "../VivyApplication.hh"
 #include "../Lib/Utils.hh"
+#include "../Lib/Log.hh"
 #include "../Lib/Script/ScriptDocument.hh"
 #include "AbstractDocumentView.hh"
 #include "FakeVim/FakeVimHandler.hh"
@@ -18,8 +20,7 @@ namespace Vivy
 class ScriptDocumentView final : public AbstractDocumentView {
     Q_OBJECT
     VIVY_UNMOVABLE_OBJECT(ScriptDocumentView)
-
-    using FakeVimHandler = FakeVim::Internal::FakeVimHandler;
+    VIVY_APP_LOGGABLE_OBJECT(ScriptDocumentView, logger)
 
 public:
     explicit ScriptDocumentView(std::shared_ptr<ScriptDocument>, QWidget *parent = nullptr);
diff --git a/src/UI/ScriptViews/EditorProxy.cc b/src/UI/ScriptViews/EditorProxy.cc
index cff3cbd913c2ed04071fd60e8846a6032ea2d4a8..f262c96e8723db058439a30dd6c04ea828cb24cd 100644
--- a/src/UI/ScriptViews/EditorProxy.cc
+++ b/src/UI/ScriptViews/EditorProxy.cc
@@ -1,4 +1,5 @@
 #include "EditorProxy.hh"
+#include "../MainWindow.hh"
 #include "../FakeVim/FakeVimHandler.hh"
 #include "../FakeVim/FakeVimActions.hh"
 #include "../../VivyApplication.hh"
@@ -10,11 +11,10 @@ EditorProxy::connectSignals(FakeVimHandler *handler, QPlainTextEdit *editor) noe
 {
     EditorProxy *proxy = new EditorProxy(editor);
 
-    handler->commandBufferChanged.connect([proxy](const QString &contents, int cursorPos,
-                                                  int /* anchorPos */,
-                                                  int /* messageLevel */) noexcept -> void {
-        proxy->changeStatusMessage(contents, cursorPos);
-    });
+    handler->commandBufferChanged.connect(
+        [proxy](const QString &contents, int cursorPos, int, int) noexcept -> void {
+            proxy->changeStatusMessage(contents, cursorPos);
+        });
 
     handler->extraInformationChanged.connect(
         [proxy](const QString &text) noexcept -> void { proxy->changeExtraInformation(text); });
@@ -49,9 +49,11 @@ EditorProxy::EditorProxy(QPlainTextEdit *widg) noexcept
     : QObject()
     , widget(widg)
 {
+    VIVY_LOG_CTOR()
+        << "Creating new proxy for a the text view " << VIVY_LOG_QUOTED(widg->windowFilePath());
 }
 
-EditorProxy::~EditorProxy() { qDebug() << "~EditorProxy"; }
+EditorProxy::~EditorProxy() { VIVY_LOG_DTOR() << "Delete editor proxy!"; }
 
 void
 EditorProxy::changeStatusData(const QString &info) noexcept
@@ -135,12 +137,16 @@ EditorProxy::handleExCommand(bool *handled, const ExCommand &cmd) noexcept
 
     else if (wantQuit(cmd)) {
         // :q!
-        if (cmd.hasBang)
+        if (cmd.hasBang) {
+            logInfo() << "Want to force quit the editor!";
             emit requestQuit();
+        }
 
         // :q
-        else
+        else {
+            logInfo() << "Want to quit the editor";
             emit requestQuit();
+        }
     }
 
     else if (wantRun(cmd))
@@ -231,8 +237,7 @@ EditorProxy::indentRegion(int beginBlock, int endBlock, QChar typedChar) noexcep
     QTextDocument *doc = widget->document();
     Q_ASSERT(doc);
 
-    const int indentSize =
-        static_cast<int>(FakeVim::Internal::fakeVimSettings()->shiftWidth.value());
+    const int indentSize  = static_cast<int>(fakeVimSettings()->shiftWidth.value());
     QTextBlock startBlock = doc->findBlockByNumber(beginBlock);
 
     // Record line lenghts for mark adjustments
@@ -305,17 +310,20 @@ EditorProxy::wantSaveAndQuit(const ExCommand &cmd) noexcept
 bool
 EditorProxy::wantSave(const ExCommand &cmd) noexcept
 {
+    logInfo() << "Want to save the editor's file";
     return cmd.matches("w", "write") || cmd.matches("wa", "wall");
 }
 
 bool
 EditorProxy::wantQuit(const ExCommand &cmd) noexcept
 {
+    logInfo() << "Want to quit editor";
     return cmd.matches("q", "quit") || cmd.matches("qa", "qall");
 }
 
 bool
 EditorProxy::wantRun(const ExCommand &cmd) noexcept
 {
+    logInfo() << "Want to run a command: " << cmd.cmd;
     return cmd.matches("run", "run") || cmd.matches("make", "make");
 }
diff --git a/src/UI/ScriptViews/EditorProxy.hh b/src/UI/ScriptViews/EditorProxy.hh
index c93a3823f753320f5521b446ddcb309bc5090d39..2db6da9e889a6d51b1b62d6686ba6d7435039ed3 100644
--- a/src/UI/ScriptViews/EditorProxy.hh
+++ b/src/UI/ScriptViews/EditorProxy.hh
@@ -1,30 +1,21 @@
 #pragma once
 
+#include "../../VivyApplication.hh"
+#include "../../Lib/Log.hh"
 #include "ScriptEditor.hh"
 
-class QMainWindow;
-class QTextDocument;
-class QString;
-class QWidget;
-class QTextCursor;
-
-namespace FakeVim::Internal
-{
-class FakeVimHandler;
-struct ExCommand;
-}
-
 namespace Vivy
 {
 class EditorProxy;
+class FakeVimHandler;
+struct ExCommand;
 
 class EditorProxy final : public QObject {
     Q_OBJECT
     VIVY_UNMOVABLE_OBJECT(EditorProxy)
+    VIVY_APP_LOGGABLE_OBJECT(EditorProxy, logger)
 
     explicit EditorProxy(QPlainTextEdit *widget) noexcept;
-    using FakeVimHandler = FakeVim::Internal::FakeVimHandler;
-    using ExCommand      = FakeVim::Internal::ExCommand;
 
 public:
     ~EditorProxy() override;
diff --git a/src/UI/ScriptViews/ScriptEditor.cc b/src/UI/ScriptViews/ScriptEditor.cc
index 80b88fcfdfaaa8bf24a6107c32b5413f92661bcd..4ba88645d892ba1848dd9e7d113b135b46a6d820 100644
--- a/src/UI/ScriptViews/ScriptEditor.cc
+++ b/src/UI/ScriptViews/ScriptEditor.cc
@@ -168,5 +168,5 @@ ScriptEditor::lineNumberAreaPaintEvent(QPaintEvent *event) noexcept
 void
 ScriptEditor::updateLastLuaError(int line, QString desc)
 {
-    qDebug() << "Update error on line" << line << "with description" << desc;
+    logDebug() << "Update error on line " << line << " with description " << desc;
 }
diff --git a/src/UI/ScriptViews/ScriptEditor.hh b/src/UI/ScriptViews/ScriptEditor.hh
index 5a68ef9c71bdd6afe31211179ae7c28dbeed5c8c..1a2391cf6a40495859c0be0eedf6ddb5dd1b58a6 100644
--- a/src/UI/ScriptViews/ScriptEditor.hh
+++ b/src/UI/ScriptViews/ScriptEditor.hh
@@ -4,6 +4,8 @@
 #error "This is a C++ header"
 #endif
 
+#include "../../VivyApplication.hh"
+#include "../../Lib/Log.hh"
 #include "../../Lib/Utils.hh"
 
 namespace Vivy
@@ -11,6 +13,7 @@ namespace Vivy
 class ScriptEditor final : public QPlainTextEdit {
     Q_OBJECT
     VIVY_UNMOVABLE_OBJECT(ScriptEditor)
+    VIVY_APP_LOGGABLE_OBJECT(ScriptEditor, logger)
 
     // Get the line numbers, private class
     class LineNumberArea final : public QWidget {
diff --git a/src/UI/ScriptViews/ScriptHighlighter.cc b/src/UI/ScriptViews/ScriptHighlighter.cc
index 2baf555a1ac8e625e4f3e6b4c87546ef4b9b6265..6c50b405e8d066fed53d1f8a9118b9ceb3bc37ce 100644
--- a/src/UI/ScriptViews/ScriptHighlighter.cc
+++ b/src/UI/ScriptViews/ScriptHighlighter.cc
@@ -1,4 +1,6 @@
 #include "ScriptHighlighter.hh"
+#include "../Theme/Theme.hh"
+#include "../../VivyApplication.hh"
 #include "../../Lib/Utils.hh"
 
 using namespace Vivy;
@@ -13,16 +15,17 @@ void
 ScriptHighlighter::resetHighlightingRule() noexcept
 {
     highlightingRules.clear();
+    const Theme::HighlightingTheme &theme = vivyApp->getTheme()->getHighlightingTheme();
 
     // function calls
-    functionFormat.setForeground(fromTheme().functionForeground);
+    functionFormat.setForeground(theme.functionForeground);
     highlightingRules.emplace_back(QRegExpLiteral("\\b[A-Za-z0-9_]+[ ]*(?=\\()"), functionFormat);
     highlightingRules.emplace_back(QRegExpLiteral("\\b[A-Za-z0-9_]+[ ]*(?=\\{)"), functionFormat);
 
     // Member and enum members
-    enumFormat.setForeground(fromTheme().enumForeground);
+    enumFormat.setForeground(theme.enumForeground);
     enumFormat.setFontWeight(QFont::Bold);
-    memberFormat.setForeground(fromTheme().memberForeground);
+    memberFormat.setForeground(theme.memberForeground);
     memberFormat.setFontWeight(QFont::StyleItalic);
     highlightingRules.emplace_back(QRegExpLiteral("\\.[a-z][\\dA-Za-z]*\\b"), memberFormat);
     highlightingRules.emplace_back(QRegExpLiteral("\\b[A-Z][\\dA-Z_]*\\b"), enumFormat);
@@ -43,7 +46,7 @@ ScriptHighlighter::resetHighlightingRule() noexcept
         QStringLiteral("\\b\\*\\=\\b"),   QStringLiteral("\\b\\/\\=\\b"),
     };
 
-    keywordFormat.setForeground(fromTheme().keywordForeground);
+    keywordFormat.setForeground(theme.keywordForeground);
     keywordFormat.setFontWeight(QFont::Bold);
 
     for (QString const &pattern : keywordPatterns)
@@ -60,21 +63,21 @@ ScriptHighlighter::resetHighlightingRule() noexcept
         QStringLiteral("\\b\\[\\dA-Fa-F]+\\b"),
     };
 
-    valueFormat.setForeground(fromTheme().valueForeground);
+    valueFormat.setForeground(theme.valueForeground);
     valueFormat.setFontWeight(QFont::Normal);
 
     for (QString const &pattern : valuePatterns)
         highlightingRules.emplace_back(QRegExp(pattern), valueFormat);
 
     // strings
-    quotationFormat.setForeground(fromTheme().valueForeground);
+    quotationFormat.setForeground(theme.valueForeground);
     highlightingRules.emplace_back(QRegExpLiteral("\"[^\"]*\""), quotationFormat);
     highlightingRules.emplace_back(QRegExpLiteral("\'[^\']*\'"), quotationFormat);
     quoteStartExpression = QRegExpLiteral("\\[\\[");
     quoteEndExpression   = QRegExpLiteral("\\]\\]");
 
     // comments
-    singleLineCommentFormat.setForeground(fromTheme().commentForeground);
+    singleLineCommentFormat.setForeground(theme.commentForeground);
     highlightingRules.emplace_back(QRegExpLiteral("--[^\n]*"), singleLineCommentFormat);
     commentStartExpression = QRegExpLiteral("--\\[\\[");
     commentEndExpression   = QRegExpLiteral("\\]\\]");
@@ -166,9 +169,3 @@ ScriptHighlighter::highlightCommentBlock(const QString &text,
     highlightBlock(text, previousState, BlockState::Comment, commentStartExpression,
                    commentEndExpression, singleLineCommentFormat);
 }
-
-const ScriptHighlighter::HighlightingTheme
-ScriptHighlighter::fromTheme() const noexcept
-{
-    return highlightingTheme[theme];
-}
diff --git a/src/UI/ScriptViews/ScriptHighlighter.hh b/src/UI/ScriptViews/ScriptHighlighter.hh
index 492259dad9adc6795952277f02b958396fccc425..cf636bdb3fff70692d69f778314a76087ec09b15 100644
--- a/src/UI/ScriptViews/ScriptHighlighter.hh
+++ b/src/UI/ScriptViews/ScriptHighlighter.hh
@@ -4,7 +4,7 @@
 #error "This is a C++ header"
 #endif
 
-class QTextDocument;
+#include "../Theme/Theme.hh"
 
 namespace Vivy
 {
@@ -18,16 +18,6 @@ class ScriptHighlighter final : public QSyntaxHighlighter {
         QTextCharFormat format;
     };
 
-    struct HighlightingTheme {
-        const QBrush functionForeground;
-        const QBrush keywordForeground;
-        const QBrush valueForeground;
-        const QBrush commentForeground;
-        const QBrush memberForeground;
-        const QBrush enumForeground;
-    };
-
-    enum Theme { DarkTheme = 0, LightTheme = 1, ThemeCount };
     enum class BlockState { None = -1, Quote = 1, Comment = 2 };
 
 public:
@@ -47,34 +37,8 @@ private:
                         const BlockState toHighlight, const QRegExp &start, const QRegExp &end,
                         const QTextCharFormat &format) noexcept;
 
-    const HighlightingTheme fromTheme() const noexcept;
     void resetHighlightingRule() noexcept;
 
-    const HighlightingTheme darkHighlightingTheme = {
-        .functionForeground = Qt::darkCyan,
-        .keywordForeground  = Qt::darkYellow,
-        .valueForeground    = QColor(Qt::cyan).darker(120),
-        .commentForeground  = QColor(Qt::darkGray).darker(120),
-        .memberForeground   = QColor(Qt::darkGray).lighter(160),
-        .enumForeground     = QColor(Qt::magenta).darker(170),
-    };
-
-    const HighlightingTheme lightHighlightingTheme = {
-        .functionForeground = Qt::blue,
-        .keywordForeground  = Qt::darkBlue,
-        .valueForeground    = Qt::red,
-        .commentForeground  = QColor(Qt::darkGray).darker(120),
-        .memberForeground   = QColor(Qt::darkGray).lighter(120),
-        .enumForeground     = Qt::darkMagenta,
-    };
-
-    const HighlightingTheme highlightingTheme[ThemeCount] = {
-        darkHighlightingTheme,
-        lightHighlightingTheme,
-    };
-
-    Theme theme{ DarkTheme };
-
     std::vector<HighlightingRule> highlightingRules;
 
     QRegExp commentStartExpression;
diff --git a/src/UI/Theme/Theme.cc b/src/UI/Theme/Theme.cc
new file mode 100644
index 0000000000000000000000000000000000000000..7f60106d1a72ae47ba0209b39379674017be2503
--- /dev/null
+++ b/src/UI/Theme/Theme.cc
@@ -0,0 +1,372 @@
+#include "Theme.hh"
+#include "../../VivyApplication.hh"
+#include "../../Lib/HostOsInfo.hh"
+#if VIVY_MACOS
+#import "ThemeMac.hh"
+#endif
+
+namespace Vivy
+{
+struct Theme::Private {
+    Private() noexcept;
+
+    ThemeType classType{ ThemeType::System };
+    VivyThemeType preferedColor{ VivyThemeType::Dark };
+
+    QString id;
+    QString fileName;
+    QString displayName;
+    QStringList preferredStyles;
+    QString defaultTextEditorColorScheme;
+    QVector<QPair<QColor, QString>> colors;
+    QVector<QString> imageFiles;
+    QVector<QGradientStops> gradients;
+    QVector<bool> flags;
+    QMap<QString, QColor> palette;
+};
+
+// If you copy QPalette, default values stay at default, even if that default is different
+// within the context of different widgets. Create deep copy.
+static QPalette
+copyPalette(const QPalette &p) noexcept
+{
+    QPalette res;
+    for (int group = 0; group < QPalette::NColorGroups; ++group) {
+        for (int role = 0; role < QPalette::NColorRoles; ++role) {
+            res.setBrush(QPalette::ColorGroup(group), QPalette::ColorRole(role),
+                         p.brush(QPalette::ColorGroup(group), QPalette::ColorRole(role)));
+        }
+    }
+    return res;
+}
+
+[[maybe_unused]] static bool
+systemUsesDarkMode() noexcept
+{
+    if (Utils::HostOsInfo::isWindowsHost()) {
+        constexpr char regkey[] =
+            "HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize";
+        bool ok;
+        const auto setting =
+            QSettings(regkey, QSettings::NativeFormat).value("AppsUseLightTheme").toInt(&ok);
+        return ok && setting == 0;
+    }
+
+    else if constexpr (Utils::HostOsInfo::isMacHost())
+        return false;
+
+    else if constexpr (Utils::HostOsInfo::isLinuxHost())
+        return true;
+
+    return false;
+}
+
+static QPalette
+initialPalette() noexcept
+{
+    static QPalette palette = copyPalette(QApplication::palette());
+    return palette;
+}
+
+Theme::Private::Private() noexcept
+{
+    const QMetaObject &m = Theme::staticMetaObject;
+    colors.resize(m.enumerator(m.indexOfEnumerator("Color")).keyCount());
+    imageFiles.resize(m.enumerator(m.indexOfEnumerator("ImageFile")).keyCount());
+    gradients.resize(m.enumerator(m.indexOfEnumerator("Gradient")).keyCount());
+    flags.resize(m.enumerator(m.indexOfEnumerator("Flag")).keyCount());
+}
+
+void
+Theme::applyToApplication() const noexcept
+{
+    if (d->classType == ThemeType::QssFile) {
+        QFile stylesheet(d->fileName);
+        if (!stylesheet.exists()) {
+            qFatal("Missing stylesheet");
+        } else {
+            stylesheet.open(QFile::ReadOnly | QFile::Text);
+            QTextStream stylesheetStream(&stylesheet);
+            vivyApp->setStyleSheet(stylesheetStream.readAll());
+        }
+    }
+
+    else {
+#if VIVY_MACOS
+        // Match the native UI theme and palette with the creator
+        // theme by forcing light aqua for light creator themes.
+        if (!flag(Theme::DarkUserInterface))
+            Utils::forceMacOSLightAquaApperance();
+#endif
+        QApplication::setPalette(palette());
+    }
+}
+
+Theme::Theme(const QString &id, QObject *parent) noexcept
+    : QObject(parent)
+    , d(new Private)
+{
+    d->id = id;
+}
+
+Theme::~Theme() noexcept { delete d; }
+
+QStringList
+Theme::preferredStyles() const noexcept
+{
+    return d->preferredStyles;
+}
+
+QString
+Theme::defaultTextEditorColorScheme() const noexcept
+{
+    return d->defaultTextEditorColorScheme;
+}
+
+QString
+Theme::id() const noexcept
+{
+    return d->id;
+}
+
+bool
+Theme::flag(Theme::Flag f) const noexcept
+{
+    return d->flags[f];
+}
+
+QColor
+Theme::color(Theme::Color role) const noexcept
+{
+    return d->colors[role].first;
+}
+
+QString
+Theme::imageFile(Theme::ImageFile imageFile, const QString &fallBack) const noexcept
+{
+    const QString &file = d->imageFiles.at(imageFile);
+    return file.isEmpty() ? fallBack : file;
+}
+
+QGradientStops
+Theme::gradient(Theme::Gradient role) const noexcept
+{
+    return d->gradients[role];
+}
+
+QPair<QColor, QString>
+Theme::readNamedColor(const QString &color) const
+{
+    if (d->palette.contains(color))
+        return qMakePair(d->palette[color], color);
+    if (color == QLatin1String("style"))
+        return qMakePair(QColor(), QString());
+
+    const QColor col('#' + color);
+    if (!col.isValid()) {
+        logWarning() << "Color " << VIVY_LOG_QUOTED(qPrintable(color))
+                     << " is neither a named color nor a valid color";
+        return qMakePair(Qt::black, QString());
+    }
+    return qMakePair(col, QString());
+}
+
+QString
+Theme::filePath() const noexcept
+{
+    return d->fileName;
+}
+
+QString
+Theme::displayName() const noexcept
+{
+    return d->displayName;
+}
+
+void
+Theme::setDisplayName(const QString &name) noexcept
+{
+    d->displayName = name;
+}
+
+Theme *
+Theme::fromQssFile(const QString &id, const QString &file) noexcept
+{
+    Theme *ret        = new Theme(id);
+    ret->d->fileName  = file;
+    ret->d->classType = ThemeType::QssFile;
+    ret->setDisplayName(QFileInfo(file).baseName());
+    return ret;
+}
+
+Theme *
+Theme::fromSettings(const QString &id, QSettings *const settings) noexcept
+{
+    Theme *ret = new Theme(id);
+    ret->readSettings(settings);
+    return ret;
+}
+
+Theme *
+Theme::fromVivyTheme(const QString &id, const VivyThemeType which) noexcept
+{
+    Theme *ret = fromQssFile(id, which == VivyThemeType::Dark
+                                     ? QStringLiteral(":qdarkstyle/dark/style.qss")
+                                     : QStringLiteral(":qdarkstyle/light/style.qss"));
+    ret->setDisplayName(QStringLiteral("Vivy Theme"));
+    return ret;
+}
+
+const Theme::HighlightingTheme &
+Theme::getHighlightingTheme() const noexcept
+{
+    return highlightingTheme[Utils::toUnderlying(d->preferedColor)];
+}
+
+void
+Theme::readSettings(QSettings *const settings) noexcept
+{
+    d->fileName          = settings->fileName();
+    d->classType         = ThemeType::QtCreator;
+    const QMetaObject &m = *metaObject();
+
+    {
+        d->displayName =
+            settings->value(QLatin1String("ThemeName"), QLatin1String("unnamed")).toString();
+        d->preferredStyles = settings->value(QLatin1String("PreferredStyles")).toStringList();
+        d->preferredStyles.removeAll(QString());
+        d->defaultTextEditorColorScheme =
+            settings->value(QLatin1String("DefaultTextEditorColorScheme")).toString();
+    }
+    {
+        settings->beginGroup(QLatin1String("Palette"));
+        const QStringList allKeys = settings->allKeys();
+        for (const QString &key : allKeys)
+            d->palette[key] = readNamedColor(settings->value(key).toString()).first;
+        settings->endGroup();
+    }
+    {
+        settings->beginGroup(QLatin1String("Colors"));
+        QMetaEnum e = m.enumerator(m.indexOfEnumerator("Color"));
+        for (int i = 0, total = e.keyCount(); i < total; ++i) {
+            const QString key = QLatin1String(e.key(i));
+            if (!settings->contains(key)) {
+                if (i < PaletteWindow || i > PalettePlaceholderTextDisabled)
+                    logWarning()
+                        << "Theme " << VIVY_LOG_QUOTED(qPrintable(d->fileName))
+                        << " misses color setting for key " << VIVY_LOG_QUOTED(qPrintable(key));
+                continue;
+            }
+            d->colors[i] = readNamedColor(settings->value(key).toString());
+        }
+        settings->endGroup();
+    }
+    {
+        settings->beginGroup(QLatin1String("ImageFiles"));
+        QMetaEnum e = m.enumerator(m.indexOfEnumerator("ImageFile"));
+        for (int i = 0, total = e.keyCount(); i < total; ++i) {
+            const QString key = QLatin1String(e.key(i));
+            d->imageFiles[i]  = settings->value(key).toString();
+        }
+        settings->endGroup();
+    }
+    {
+        settings->beginGroup(QLatin1String("Gradients"));
+        QMetaEnum e = m.enumerator(m.indexOfEnumerator("Gradient"));
+        for (int i = 0, total = e.keyCount(); i < total; ++i) {
+            const QString key = QLatin1String(e.key(i));
+            QGradientStops stops;
+            int size = settings->beginReadArray(key);
+            for (int j = 0; j < size; ++j) {
+                settings->setArrayIndex(j);
+                VIVY_ASSERT(settings->contains(QLatin1String("pos")));
+                VIVY_ASSERT(settings->contains(QLatin1String("color")));
+                const double pos = settings->value(QLatin1String("pos")).toDouble();
+                const QColor c('#' + settings->value(QLatin1String("color")).toString());
+                stops.append(qMakePair(pos, c));
+            }
+            settings->endArray();
+            d->gradients[i] = stops;
+        }
+        settings->endGroup();
+    }
+    {
+        settings->beginGroup(QLatin1String("Flags"));
+        QMetaEnum e = m.enumerator(m.indexOfEnumerator("Flag"));
+        for (int i = 0, total = e.keyCount(); i < total; ++i) {
+            const QString key = QLatin1String(e.key(i));
+            VIVY_ASSERT(settings->contains(key));
+            d->flags[i] = settings->value(key).toBool();
+        }
+        settings->endGroup();
+    }
+}
+
+QPalette
+Theme::palette() const noexcept
+{
+    QPalette pal = initialPalette();
+
+    const static struct {
+        Color themeColor;
+        QPalette::ColorRole paletteColorRole;
+        QPalette::ColorGroup paletteColorGroup;
+        bool setColorRoleAsBrush;
+    } mapping[] = {
+        { PaletteWindow, QPalette::Window, QPalette::All, false },
+        { PaletteWindowDisabled, QPalette::Window, QPalette::Disabled, false },
+        { PaletteWindowText, QPalette::WindowText, QPalette::All, true },
+        { PaletteWindowTextDisabled, QPalette::WindowText, QPalette::Disabled, true },
+        { PaletteBase, QPalette::Base, QPalette::All, false },
+        { PaletteBaseDisabled, QPalette::Base, QPalette::Disabled, false },
+        { PaletteAlternateBase, QPalette::AlternateBase, QPalette::All, false },
+        { PaletteAlternateBaseDisabled, QPalette::AlternateBase, QPalette::Disabled, false },
+        { PaletteToolTipBase, QPalette::ToolTipBase, QPalette::All, true },
+        { PaletteToolTipBaseDisabled, QPalette::ToolTipBase, QPalette::Disabled, true },
+        { PaletteToolTipText, QPalette::ToolTipText, QPalette::All, false },
+        { PaletteToolTipTextDisabled, QPalette::ToolTipText, QPalette::Disabled, false },
+        { PaletteText, QPalette::Text, QPalette::All, true },
+        { PaletteTextDisabled, QPalette::Text, QPalette::Disabled, true },
+        { PaletteButton, QPalette::Button, QPalette::All, false },
+        { PaletteButtonDisabled, QPalette::Button, QPalette::Disabled, false },
+        { PaletteButtonText, QPalette::ButtonText, QPalette::All, true },
+        { PaletteButtonTextDisabled, QPalette::ButtonText, QPalette::Disabled, true },
+        { PaletteBrightText, QPalette::BrightText, QPalette::All, false },
+        { PaletteBrightTextDisabled, QPalette::BrightText, QPalette::Disabled, false },
+        { PaletteHighlight, QPalette::Highlight, QPalette::All, true },
+        { PaletteHighlightDisabled, QPalette::Highlight, QPalette::Disabled, true },
+        { PaletteHighlightedText, QPalette::HighlightedText, QPalette::All, true },
+        { PaletteHighlightedTextDisabled, QPalette::HighlightedText, QPalette::Disabled, true },
+        { PaletteLink, QPalette::Link, QPalette::All, false },
+        { PaletteLinkDisabled, QPalette::Link, QPalette::Disabled, false },
+        { PaletteLinkVisited, QPalette::LinkVisited, QPalette::All, false },
+        { PaletteLinkVisitedDisabled, QPalette::LinkVisited, QPalette::Disabled, false },
+        { PaletteLight, QPalette::Light, QPalette::All, false },
+        { PaletteLightDisabled, QPalette::Light, QPalette::Disabled, false },
+        { PaletteMidlight, QPalette::Midlight, QPalette::All, false },
+        { PaletteMidlightDisabled, QPalette::Midlight, QPalette::Disabled, false },
+        { PaletteDark, QPalette::Dark, QPalette::All, false },
+        { PaletteDarkDisabled, QPalette::Dark, QPalette::Disabled, false },
+        { PaletteMid, QPalette::Mid, QPalette::All, false },
+        { PaletteMidDisabled, QPalette::Mid, QPalette::Disabled, false },
+        { PaletteShadow, QPalette::Shadow, QPalette::All, false },
+        { PaletteShadowDisabled, QPalette::Shadow, QPalette::Disabled, false },
+        { PalettePlaceholderText, QPalette::PlaceholderText, QPalette::All, false },
+        { PalettePlaceholderTextDisabled, QPalette::PlaceholderText, QPalette::Disabled, false },
+    };
+
+    for (auto entry : mapping) {
+        const QColor themeColor = color(entry.themeColor);
+        // Use original color if color is not defined in theme.
+        if (themeColor.isValid()) {
+            if (entry.setColorRoleAsBrush)
+                // TODO: Find out why sometimes setBrush is used
+                pal.setBrush(entry.paletteColorGroup, entry.paletteColorRole, themeColor);
+            else
+                pal.setColor(entry.paletteColorGroup, entry.paletteColorRole, themeColor);
+        }
+    }
+
+    return pal;
+}
+}
diff --git a/src/UI/Theme/Theme.hh b/src/UI/Theme/Theme.hh
new file mode 100644
index 0000000000000000000000000000000000000000..532bcb6795f658e1049738e53c64adf6ae1e0afd
--- /dev/null
+++ b/src/UI/Theme/Theme.hh
@@ -0,0 +1,502 @@
+#pragma once
+
+#include "../../VivyApplication.hh"
+#include "../../Lib/Utils.hh"
+#include "../../Lib/Log.hh"
+
+namespace Vivy
+{
+class Private;
+class Theme;
+
+class Theme final : public QObject {
+    Q_OBJECT
+    VIVY_APP_LOGGABLE_OBJECT(Theme, logger)
+
+public:
+    struct HighlightingTheme final {
+        const QBrush functionForeground;
+        const QBrush keywordForeground;
+        const QBrush valueForeground;
+        const QBrush commentForeground;
+        const QBrush memberForeground;
+        const QBrush enumForeground;
+    };
+
+private:
+    struct Private;
+
+    Theme(const QString &id, QObject *parent = nullptr) noexcept;
+    void readSettings(QSettings *const settings) noexcept;
+
+public:
+    ~Theme() noexcept override;
+
+    [[nodiscard("free")]] static Theme *fromQssFile(const QString &, const QString &) noexcept;
+    [[nodiscard("free")]] static Theme *fromSettings(const QString &, QSettings *const) noexcept;
+    [[nodiscard("free")]] static Theme *fromVivyTheme(const QString &,
+                                                      const VivyThemeType) noexcept;
+
+    void applyToApplication() const noexcept;
+    [[nodiscard]] const HighlightingTheme &getHighlightingTheme() const noexcept;
+
+    enum Color {
+        BackgroundColorAlternate,
+        BackgroundColorDark,
+        BackgroundColorHover,
+        BackgroundColorNormal,
+        BackgroundColorSelected,
+        BackgroundColorDisabled,
+        BadgeLabelBackgroundColorChecked,
+        BadgeLabelBackgroundColorUnchecked,
+        BadgeLabelTextColorChecked,
+        BadgeLabelTextColorUnchecked,
+        CanceledSearchTextColor,
+        ComboBoxArrowColor,
+        ComboBoxArrowColorDisabled,
+        ComboBoxTextColor,
+        DetailsButtonBackgroundColorHover,
+        DetailsWidgetBackgroundColor,
+        DockWidgetResizeHandleColor,
+        DoubleTabWidget1stSeparatorColor,
+        DoubleTabWidget1stTabActiveTextColor,
+        DoubleTabWidget1stTabBackgroundColor,
+        DoubleTabWidget1stTabInactiveTextColor,
+        DoubleTabWidget2ndSeparatorColor,
+        DoubleTabWidget2ndTabActiveTextColor,
+        DoubleTabWidget2ndTabBackgroundColor,
+        DoubleTabWidget2ndTabInactiveTextColor,
+        EditorPlaceholderColor,
+        FancyToolBarSeparatorColor,
+        FancyTabBarBackgroundColor,
+        FancyTabBarSelectedBackgroundColor,
+        FancyTabWidgetDisabledSelectedTextColor,
+        FancyTabWidgetDisabledUnselectedTextColor,
+        FancyTabWidgetEnabledSelectedTextColor,
+        FancyTabWidgetEnabledUnselectedTextColor,
+        FancyToolButtonHoverColor,
+        FancyToolButtonSelectedColor,
+        FutureProgressBackgroundColor,
+        InfoBarBackground,
+        InfoBarText, // TODO: Deprecate. Unused.
+        MenuBarEmptyAreaBackgroundColor,
+        MenuBarItemBackgroundColor,
+        MenuBarItemTextColorDisabled,
+        MenuBarItemTextColorNormal,
+        MenuItemTextColorDisabled,
+        MenuItemTextColorNormal,
+        MiniProjectTargetSelectorBackgroundColor, // TODO: Deprecate. -> Utils::StyleHelper().baseColor()
+        MiniProjectTargetSelectorBorderColor,
+        MiniProjectTargetSelectorSummaryBackgroundColor, // TODO: Deprecate. -> Utils::StyleHelper().baseColor()
+        MiniProjectTargetSelectorTextColor,
+        OutputPaneButtonFlashColor,
+        OutputPaneToggleButtonTextColorChecked,
+        OutputPaneToggleButtonTextColorUnchecked,
+        PanelStatusBarBackgroundColor,
+        PanelsWidgetSeparatorLineColor, // TODO: Deprecate. Unused.
+        PanelTextColorDark,
+        PanelTextColorMid,
+        PanelTextColorLight,
+        ProgressBarColorError,
+        ProgressBarColorFinished,
+        ProgressBarColorNormal,
+        ProgressBarTitleColor,
+        ProgressBarBackgroundColor,
+        SplitterColor,
+        TextColorDisabled,
+        TextColorError,
+        TextColorHighlight,
+        TextColorHighlightBackground,
+        TextColorLink,
+        TextColorLinkVisited,
+        TextColorNormal,
+        ToggleButtonBackgroundColor,
+        ToolBarBackgroundColor,
+        TreeViewArrowColorNormal,
+        TreeViewArrowColorSelected,
+
+        /* Palette for QPalette */
+
+        PaletteWindow,
+        PaletteWindowText,
+        PaletteBase,
+        PaletteAlternateBase,
+        PaletteToolTipBase,
+        PaletteToolTipText,
+        PaletteText,
+        PaletteButton,
+        PaletteButtonText,
+        PaletteBrightText,
+        PaletteHighlight,
+        PaletteHighlightedText,
+        PaletteLink,
+        PaletteLinkVisited,
+
+        PaletteLight,
+        PaletteMidlight,
+        PaletteDark,
+        PaletteMid,
+        PaletteShadow,
+
+        PaletteWindowDisabled,
+        PaletteWindowTextDisabled,
+        PaletteBaseDisabled,
+        PaletteAlternateBaseDisabled,
+        PaletteToolTipBaseDisabled,
+        PaletteToolTipTextDisabled,
+        PaletteTextDisabled,
+        PaletteButtonDisabled,
+        PaletteButtonTextDisabled,
+        PaletteBrightTextDisabled,
+        PaletteHighlightDisabled,
+        PaletteHighlightedTextDisabled,
+        PaletteLinkDisabled,
+        PaletteLinkVisitedDisabled,
+
+        PaletteLightDisabled,
+        PaletteMidlightDisabled,
+        PaletteDarkDisabled,
+        PaletteMidDisabled,
+        PaletteShadowDisabled,
+
+        PalettePlaceholderText,
+        PalettePlaceholderTextDisabled,
+
+        /* Icons */
+
+        IconsBaseColor,
+        IconsDisabledColor,
+        IconsInfoColor,
+        IconsInfoToolBarColor,
+        IconsWarningColor,
+        IconsWarningToolBarColor,
+        IconsErrorColor,
+        IconsErrorToolBarColor,
+        IconsRunColor,
+        IconsRunToolBarColor,
+        IconsStopColor,
+        IconsStopToolBarColor,
+        IconsInterruptColor,
+        IconsInterruptToolBarColor,
+        IconsDebugColor,
+        IconsNavigationArrowsColor,
+        IconsBuildHammerHandleColor,
+        IconsBuildHammerHeadColor,
+        IconsModeWelcomeActiveColor,
+        IconsModeEditActiveColor,
+        IconsModeDesignActiveColor,
+        IconsModeDebugActiveColor,
+        IconsModeProjectActiveColor,
+        IconsModeAnalyzeActiveColor,
+        IconsModeHelpActiveColor,
+
+        /* Code model Icons */
+
+        IconsCodeModelKeywordColor,
+        IconsCodeModelClassColor,
+        IconsCodeModelStructColor,
+        IconsCodeModelFunctionColor,
+        IconsCodeModelVariableColor,
+        IconsCodeModelEnumColor,
+        IconsCodeModelMacroColor,
+        IconsCodeModelAttributeColor,
+        IconsCodeModelUniformColor,
+        IconsCodeModelVaryingColor,
+        IconsCodeModelOverlayBackgroundColor,
+        IconsCodeModelOverlayForegroundColor,
+
+        /* Code model text marks */
+
+        CodeModel_Error_TextMarkColor,
+        CodeModel_Warning_TextMarkColor,
+
+        /* Output panes */
+
+        OutputPanes_DebugTextColor,
+        OutputPanes_ErrorMessageTextColor,
+        OutputPanes_MessageOutput,
+        OutputPanes_NormalMessageTextColor,
+        OutputPanes_StdErrTextColor,
+        OutputPanes_StdOutTextColor,
+        OutputPanes_WarningMessageTextColor,
+        OutputPanes_TestPassTextColor,
+        OutputPanes_TestFailTextColor,
+        OutputPanes_TestXFailTextColor,
+        OutputPanes_TestXPassTextColor,
+        OutputPanes_TestSkipTextColor,
+        OutputPanes_TestWarnTextColor,
+        OutputPanes_TestFatalTextColor,
+        OutputPanes_TestDebugTextColor,
+
+        /* Debugger Log Window */
+
+        Debugger_LogWindow_LogInput,
+        Debugger_LogWindow_LogStatus,
+        Debugger_LogWindow_LogTime,
+
+        /* Debugger Watch Item */
+
+        Debugger_WatchItem_ValueNormal,
+        Debugger_WatchItem_ValueInvalid,
+        Debugger_WatchItem_ValueChanged,
+
+        /* Welcome Plugin */
+
+        Welcome_TextColor,
+        Welcome_ForegroundPrimaryColor,
+        Welcome_ForegroundSecondaryColor,
+        Welcome_BackgroundColor,
+        Welcome_ButtonBackgroundColor,
+        Welcome_DividerColor,
+        Welcome_LinkColor,
+        Welcome_HoverColor,
+        Welcome_DisabledLinkColor,
+
+        /* Timeline Library */
+        Timeline_TextColor,
+        Timeline_BackgroundColor1,
+        Timeline_BackgroundColor2,
+        Timeline_DividerColor,
+        Timeline_HighlightColor,
+        Timeline_PanelBackgroundColor,
+        Timeline_PanelHeaderColor,
+        Timeline_HandleColor,
+        Timeline_RangeColor,
+
+        /* VcsBase Plugin */
+        VcsBase_FileStatusUnknown_TextColor,
+        VcsBase_FileAdded_TextColor,
+        VcsBase_FileModified_TextColor,
+        VcsBase_FileDeleted_TextColor,
+        VcsBase_FileRenamed_TextColor,
+        VcsBase_FileUnmerged_TextColor,
+
+        /* Bookmarks Plugin */
+        Bookmarks_TextMarkColor,
+
+        /* TextEditor Plugin */
+        TextEditor_SearchResult_ScrollBarColor,
+        TextEditor_CurrentLine_ScrollBarColor,
+
+        /* Debugger Plugin */
+        Debugger_Breakpoint_TextMarkColor,
+
+        /* ProjectExplorer Plugin */
+        ProjectExplorer_TaskError_TextMarkColor,
+        ProjectExplorer_TaskWarn_TextMarkColor,
+
+        /* QmlDesigner Plugin */
+        QmlDesigner_BackgroundColor,
+        QmlDesigner_HighlightColor,
+        QmlDesigner_FormEditorSelectionColor,
+        QmlDesigner_FormEditorForegroundColor,
+        QmlDesigner_BackgroundColorDarker,
+        QmlDesigner_BackgroundColorDarkAlternate,
+        QmlDesigner_TabLight,
+        QmlDesigner_TabDark,
+        QmlDesigner_ButtonColor,
+        QmlDesigner_BorderColor,
+        QmlDesigner_FormeditorBackgroundColor,
+        QmlDesigner_AlternateBackgroundColor,
+        QmlDesigner_ScrollBarHandleColor,
+
+        /* Palette for DS Controls */
+
+        DSpanelBackground,
+        DSinteraction,
+        DSerrorColor,
+        DSdisabledColor,
+        DScontrolBackground,
+        DScontrolBackgroundInteraction,
+        DScontrolBackgroundDisabled,
+        DScontrolBackgroundGlobalHover,
+        DScontrolBackgroundHover,
+        DScontrolOutline,
+        DScontrolOutlineInteraction,
+        DScontrolOutlineDisabled,
+        DStextColor,
+        DStextColorDisabled,
+        DStextSelectionColor,
+        DStextSelectedTextColor,
+
+        DSplaceholderTextColor,
+        DSplaceholderTextColorInteraction,
+
+        DSiconColor,
+        DSiconColorHover,
+        DSiconColorInteraction,
+        DSiconColorDisabled,
+        DSiconColorSelected,
+        DSlinkIndicatorColor,
+        DSlinkIndicatorColorHover,
+        DSlinkIndicatorColorInteraction,
+        DSlinkIndicatorColorDisabled,
+        DSpopupBackground,
+        DSpopupOverlayColor,
+        DSsliderActiveTrack,
+        DSsliderActiveTrackHover,
+        DSsliderActiveTrackFocus,
+        DSsliderInactiveTrack,
+        DSsliderInactiveTrackHover,
+        DSsliderInactiveTrackFocus,
+        DSsliderHandle,
+        DSsliderHandleHover,
+        DSsliderHandleFocus,
+        DSsliderHandleInteraction,
+        DSscrollBarTrack,
+        DSscrollBarHandle,
+        DSsectionHeadBackground,
+        DSstateDefaultHighlight,
+        DSstateSeparatorColor,
+        DSstateBackgroundColor,
+        DSstatePreviewOutline,
+        DSchangedStateText,
+        DS3DAxisXColor,
+        DS3DAxisYColor,
+        DS3DAxisZColor,
+        DSactionBinding,
+        DSactionAlias,
+        DSactionKeyframe,
+        DSactionJIT,
+
+        DStableHeaderBackground,
+        DStableHeaderText,
+
+        DSdockContainerBackground,
+        DSdockContainerSplitter,
+        DSdockAreaBackground,
+
+        DSdockWidgetBackground,
+        DSdockWidgetSplitter,
+        DSdockWidgetTitleBar,
+
+        DStitleBarText,
+        DStitleBarIcon,
+        DStitleBarButtonHover,
+        DStitleBarButtonPress,
+
+        DStabContainerBackground,
+        DStabSplitter,
+
+        DStabInactiveBackground,
+        DStabInactiveText,
+        DStabInactiveIcon,
+        DStabInactiveButtonHover,
+        DStabInactiveButtonPress,
+
+        DStabActiveBackground,
+        DStabActiveText,
+        DStabActiveIcon,
+        DStabActiveButtonHover,
+        DStabActiveButtonPress,
+
+        DStabFocusBackground,
+        DStabFocusText,
+        DStabFocusIcon,
+        DStabFocusButtonHover,
+        DStabFocusButtonPress,
+
+        DSnavigatorBranch,
+        DSnavigatorBranchIndicator,
+        DSnavigatorItemBackground,
+        DSnavigatorItemBackgroundHover,
+        DSnavigatorItemBackgroundSelected,
+        DSnavigatorText,
+        DSnavigatorTextHover,
+        DSnavigatorTextSelected,
+        DSnavigatorIcon,
+        DSnavigatorIconHover,
+        DSnavigatorIconSelected,
+        DSnavigatorAliasIconChecked,
+        DSnavigatorDropIndicatorBackground,
+        DSnavigatorDropIndicatorOutline,
+
+        DSheaderViewBackground,
+        DStableViewAlternateBackground,
+
+        DStoolTipBackground,
+        DStoolTipOutline,
+        DStoolTipText,
+
+        DSUnimportedModuleColor
+    };
+
+    enum Gradient {
+        DetailsWidgetHeaderGradient,
+    };
+
+    enum ImageFile {
+        IconOverlayCSource,
+        IconOverlayCppHeader,
+        IconOverlayCppSource,
+        IconOverlayPri,
+        IconOverlayPrf,
+        IconOverlayPro,
+        StandardPixmapFileIcon,
+        StandardPixmapDirIcon
+    };
+
+    enum Flag {
+        DrawTargetSelectorBottom,
+        DrawSearchResultWidgetFrame,
+        DrawIndicatorBranch,
+        DrawToolBarHighlights,
+        DrawToolBarBorders,
+        ComboBoxDrawTextShadow,
+        DerivePaletteFromTheme,
+        ApplyThemePaletteGlobally,
+        FlatToolBars,
+        FlatSideBarIcons,
+        FlatProjectsMode,
+        FlatMenuBar,
+        ToolBarIconShadow,
+        WindowColorAsBase,
+        DarkUserInterface
+    };
+
+    Q_ENUM(Color)
+    Q_ENUM(ImageFile)
+    Q_ENUM(Gradient)
+    Q_ENUM(Flag)
+
+    Q_INVOKABLE bool flag(Flag f) const noexcept;
+    Q_INVOKABLE QColor color(Color role) const noexcept;
+    QString imageFile(ImageFile imageFile, const QString &fallBack) const noexcept;
+    QGradientStops gradient(Gradient role) const noexcept;
+    QPalette palette() const noexcept;
+    QStringList preferredStyles() const noexcept;
+    QString defaultTextEditorColorScheme() const noexcept;
+
+    QString id() const noexcept;
+    QString filePath() const noexcept;
+    QString displayName() const noexcept;
+    void setDisplayName(const QString &displayName) noexcept;
+
+private:
+    QPair<QColor, QString> readNamedColor(const QString &color) const;
+    Private *const d;
+
+    const Theme::HighlightingTheme darkHighlightingTheme = {
+        .functionForeground = Qt::darkCyan,
+        .keywordForeground  = Qt::darkYellow,
+        .valueForeground    = QColor(Qt::cyan).darker(120),
+        .commentForeground  = QColor(Qt::darkGray).darker(120),
+        .memberForeground   = QColor(Qt::darkGray).lighter(160),
+        .enumForeground     = QColor(Qt::magenta).darker(170),
+    };
+
+    const Theme::HighlightingTheme lightHighlightingTheme = {
+        .functionForeground = Qt::blue,
+        .keywordForeground  = Qt::darkBlue,
+        .valueForeground    = Qt::red,
+        .commentForeground  = QColor(Qt::darkGray).darker(120),
+        .memberForeground   = QColor(Qt::darkGray).lighter(120),
+        .enumForeground     = Qt::darkMagenta,
+    };
+
+    const Theme::HighlightingTheme highlightingTheme[Utils::toUnderlying(VivyThemeType::___COUNT)] = {
+        darkHighlightingTheme,
+        lightHighlightingTheme,
+    };
+};
+}
diff --git a/src/UI/Theme/ThemeMac.hh b/src/UI/Theme/ThemeMac.hh
new file mode 100644
index 0000000000000000000000000000000000000000..93d1e0ea1bce5724d911522248866832a0625ee2
--- /dev/null
+++ b/src/UI/Theme/ThemeMac.hh
@@ -0,0 +1,6 @@
+#pragma once
+
+namespace Vivy::Utils
+{
+void forceMacOSLightAquaApperance();
+}
diff --git a/src/UI/Theme/ThemeMac.mm b/src/UI/Theme/ThemeMac.mm
new file mode 100644
index 0000000000000000000000000000000000000000..9b2d3d6f912948b0194004e5aba951ba4f2ba2d4
--- /dev/null
+++ b/src/UI/Theme/ThemeMac.mm
@@ -0,0 +1,21 @@
+#include "ThemeMac.hh"
+#include <AppKit/AppKit.h>
+
+#if !QT_MACOS_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_14)
+@interface NSApplication (MojaveForwardDeclarations)
+@property (strong) NSAppearance *appearance NS_AVAILABLE_MAC(10_14);
+@end
+#endif
+
+namespace Vivy::Utils {
+void forceMacOSLightAquaApperance()
+{
+#if __has_builtin(__builtin_available)
+    if (__builtin_available(macOS 10.14, *))
+#else // Xcode 8
+    if (QOperatingSystemVersion::current() >= QOperatingSystemVersion(QOperatingSystemVersion::MacOS, 10, 14, 0))
+#endif
+        NSApp.appearance = [NSAppearance appearanceNamed:NSAppearanceNameAqua];
+}
+
+}
diff --git a/src/UI/VivyDocumentView.cc b/src/UI/VivyDocumentView.cc
index be7437916ed688742d5beeb24454295e5ea124b2..86e155afd60eac27964e0f270e8dba6f6d8ff4f8 100644
--- a/src/UI/VivyDocumentView.cc
+++ b/src/UI/VivyDocumentView.cc
@@ -6,6 +6,7 @@
 #include "DocumentViews/AssLinesModel.hh"
 #include "../VivyApplication.hh"
 #include "../Lib/Document/VivyDocument.hh"
+#include "../Lib/Document/VivyDocumentStore.hh"
 
 using namespace Vivy;
 
@@ -13,6 +14,9 @@ VivyDocumentView::VivyDocumentView(std::shared_ptr<VivyDocument> doc, QWidget *p
     : AbstractDocumentView(AbstractDocumentView::Type::Vivy, parent)
     , document(doc)
 {
+    VIVY_LOG_CTOR() << "Creating view for vivy document " << VIVY_LOG_QUOTED(doc->getName())
+                    << " with " << doc->getUuid() << "...";
+
     setDockNestingEnabled(true);
 
     loadAudioView();
@@ -30,17 +34,17 @@ VivyDocumentView::VivyDocumentView(std::shared_ptr<VivyDocument> doc, QWidget *p
     viewsActions.append(openPropertiesAct);
 
     // The separator
-    QAction *separator = new QAction(this);
+    QAction *const separator = new QAction(this);
     separator->setSeparator(true);
     viewsActions.append(separator);
+
+    VIVY_LOG_CTOR() << "View for " << doc->getUuid() << " done!";
 }
 
 VivyDocumentView::~VivyDocumentView() noexcept
 {
-    qDebug()
-        << "Deleting the document view: ref count on document" << document->getName() << "is"
-        << document.use_count() << "and" << (visualizer ? "has visualizer" : "without visualizer");
-
+    VIVY_LOG_DTOR() << "Deleting view for " << document->getUuid() << ", file name is "
+                    << VIVY_LOG_QUOTED(document->getName());
     deleteAllContent();
 }
 
@@ -121,7 +125,7 @@ VivyDocumentView::loadAudioView() noexcept
     AudioContext::StreamPtr stream                  = audioDocument->getDefaultStream();
 
     if (stream == nullptr) {
-        qCritical() << "Failed to get default audio stream";
+        logError() << "Failed to get default audio stream";
         return;
     }
 
@@ -145,9 +149,8 @@ VivyDocumentView::loadAudioView() noexcept
 void
 VivyDocumentView::closeDocument() noexcept
 {
-    qDebug() << "Closing the document:" << document->getName() << "( ref count is"
-             << document.use_count() << ")";
-    vivyApp->documentStore.closeDocument(document->getUuid());
+    logDebug() << "Closing document " << document->getUuid();
+    vivyApp->documentStore->closeDocument(document->getUuid());
     allowToCloseAllDocks();
 }
 
diff --git a/src/UI/VivyDocumentView.hh b/src/UI/VivyDocumentView.hh
index 0a6f13b4519362f6468632348a854051a44e4b64..a17d1c34c8a0cdbb61d9ae12807d10a96c383266 100644
--- a/src/UI/VivyDocumentView.hh
+++ b/src/UI/VivyDocumentView.hh
@@ -1,23 +1,25 @@
-#ifndef VIVY_DOCUMENTVIEW_H
-#define VIVY_DOCUMENTVIEW_H
+#pragma once
 
 #ifndef __cplusplus
 #error "This is a C++ header"
 #endif
 
-#include "AbstractDocumentView.hh"
+#include "../VivyApplication.hh"
+#include "../Lib/Log.hh"
 #include "../Lib/Document/VivyDocument.hh"
+#include "PropertyModel.hh"
+#include "UnclosableDockWidget.hh"
+#include "AbstractDocumentView.hh"
 #include "DocumentViews/VideoView.hh"
 #include "DocumentViews/AssLinesView.hh"
 #include "DocumentViews/AssLinesModel.hh"
-#include "PropertyModel.hh"
-#include "UnclosableDockWidget.hh"
 
 namespace Vivy
 {
 class VivyDocumentView final : public AbstractDocumentView {
     Q_OBJECT
     VIVY_UNMOVABLE_OBJECT(VivyDocumentView)
+    VIVY_APP_LOGGABLE_OBJECT(VivyDocumentView, logger)
 
     static inline constexpr QDockWidget::DockWidgetFeatures allDockFeatures =
         QDockWidget::DockWidgetClosable | QDockWidget::DockWidgetFloatable |
@@ -53,7 +55,4 @@ private:
     UnclosableDockWidget *videoView{ nullptr };
     QAction *openPropertiesAct{ nullptr };
 };
-
 }
-
-#endif // VIVY_DOCUMENTVIEW_H
diff --git a/src/VivyApplication.cc b/src/VivyApplication.cc
index efb10c01c29927a412fa85c03a65975524f00250..3be55a7da0ea7b285d699476804fb4e43b44bfdf 100644
--- a/src/VivyApplication.cc
+++ b/src/VivyApplication.cc
@@ -1,30 +1,69 @@
 #include "VivyApplication.hh"
 #include "UI/MainWindow.hh"
+#include "UI/DockWidgetTitleBar.hh"
+#include "UI/LogConsole.hh"
+#include "UI/Theme/Theme.hh"
+#include "Lib/Document/VivyDocumentStore.hh"
+#include "Lib/Document/VivyDocument.hh"
+#include "Lib/Script/ScriptStore.hh"
+#include "Lib/Script/ScriptDocument.hh"
 
 using namespace Vivy;
 
 VivyApplication::VivyApplication(int &argc, char **argv)
     : QApplication(argc, argv)
 {
+    documentStore = std::make_shared<VivyDocumentStore>();
+    scriptStore   = std::make_shared<ScriptStore>();
+    VIVY_LOG_CTOR() << "Construction is OK";
+
+    if (argc >= 2) {
+        for (int i = 1; i < argc; ++i) {
+            const QString passedFileName(QString::fromUtf8(argv[1]));
+            const QFileInfo passedArgumentFile(passedFileName);
+            Utils::DocumentType passedFileType;
+            if (Utils::detectDocumentType(passedArgumentFile, &passedFileType)) {
+                if (passedFileType == Utils::DocumentType::Vivy) {
+                    selectedVivyDoc = documentStore->loadDocument(passedFileName);
+                    logInfo() << "Select vivy document " << passedFileName;
+                } else if (passedFileType == Utils::DocumentType::VivyScript) {
+                    selectedScriptDoc = scriptStore->loadDocument(passedFileName);
+                    logInfo() << "Select script document " << passedFileName;
+                } else {
+                    logError() << "Not handled file type for file " << passedFileName;
+                }
+            } else {
+                logError() << "Unrecognized file type for file " << passedFileName;
+            }
+        }
+
+        selectedType = ApplicationType::CLI;
+        VIVY_LOG_CTOR() << "Select the ApplicationType::CLI";
+    }
 }
 
+VivyApplication::~VivyApplication() noexcept {}
+
 void
-VivyApplication::setTheme(Theme theme) noexcept
+VivyApplication::setTheme(ThemeType theme, VivyThemeType preferedColor) noexcept
 {
-    if (theme == Theme::System)
+    if (theme == ThemeType::System) {
         return;
+    }
 
-    const QString sheet = theme == Theme::Dark ? QStringLiteral(":qdarkstyle/dark/style.qss")
-                                               : QStringLiteral(":qdarkstyle/light/style.qss");
+    else if (theme == ThemeType::QtCreator) {
+        QSettings settings(QStringLiteral(":theme/design.creatortheme"), QSettings::IniFormat);
+        qtCreatorThemePtr.reset(Vivy::Theme::fromSettings("QtCreator", &settings));
+        qtCreatorThemePtr->applyToApplication();
+        return;
+    }
 
-    QFile stylesheet(sheet);
-    if (!stylesheet.exists()) {
-        qFatal("Missing stylesheet");
-    } else {
-        stylesheet.open(QFile::ReadOnly | QFile::Text);
-        QTextStream stylesheetStream(&stylesheet);
-        setStyleSheet(stylesheetStream.readAll());
+    if (theme != ThemeType::QssFile) {
+        qFatal("Unknown Theme type");
     }
+
+    qtCreatorThemePtr.reset(Vivy::Theme::fromVivyTheme("VivyTheme", preferedColor));
+    qtCreatorThemePtr->applyToApplication();
 }
 
 int
@@ -46,18 +85,51 @@ VivyApplication::exec() noexcept
     setAttribute(Qt::AA_DontShowIconsInMenus, false);
     setAttribute(Qt::AA_DontShowShortcutsInContextMenus, false);
     setFont(getApplicationFont(Font::Default));
-    setTheme(Theme::Dark); // TODO: Set system theme for now (because we
-                           // don't have a central theme provider).
+    setTheme(ThemeType::QtCreator, VivyThemeType::Dark);
 
     // Cursor blinking
     setCursorFlashTime(0);
 
-    // Show the main window
-    mainWindowPtr = std::make_unique<MainWindow>();
-    mainWindowPtr->show();
+    switch (selectedType) {
+    case ApplicationType::CLI: {
+        if ((selectedVivyDoc == nullptr) || (selectedScriptDoc == nullptr))
+            return 1;
+
+        if (!scriptStore->executeScript(selectedScriptDoc->getUuid(), selectedVivyDoc))
+            return 2;
+
+        for (const auto &str : scriptStore->getLoadedModules()) {
+            std::cout << "Module " << str << " was loaded!\n";
+            [[maybe_unused]] const auto *mod = scriptStore->getModule(str);
+        }
+        return 0;
+    }
+
+    case ApplicationType::GUI: {
+        // Show the main window, also set up the log console
+        mainWindowPtr               = std::make_unique<MainWindow>();
+        QDockWidget *logConsoleDock = new QDockWidget("Console", mainWindowPtr.get());
+        LogConsole *logConsole      = new LogConsole(mainWindowPtr.get());
+        std::shared_ptr<ConsoleLogSinkDispatcher> consoleLogSinkDispatcher =
+            logSink->newDispatcher<ConsoleLogSinkDispatcher>();
+
+        DockWidgetTitleBar::addToDock(logConsoleDock);
+        consoleLogSinkDispatcher->attachLogConsole(logConsole);
+        logConsoleDock->setAllowedAreas(Qt::BottomDockWidgetArea);
+        logConsoleDock->setFeatures(QDockWidget::DockWidgetMovable);
+        logConsoleDock->setWidget(logConsole);
+        mainWindowPtr->addDockWidget(Qt::BottomDockWidgetArea, logConsoleDock);
+        mainWindowPtr->show();
+
+        logInfo() << "Entering the main event loop";
+
+        // Main loop
+        return QApplication::exec();
+    }
+    }
 
-    // Main loop
-    return QApplication::exec();
+    logFatal() << "Unreachable: undefined ApplicationType";
+    return 1;
 }
 
 QFont
@@ -77,7 +149,8 @@ VivyApplication::getApplicationFont(Font id) const noexcept
     }
 
     // Let the program crash
-    qFatal("UNREACHABLE");
+    logError() << "Unreachable: undefined font id";
+    return getFontFromId(fontIdRegular);
 }
 
 MainWindow *
diff --git a/src/VivyApplication.hh b/src/VivyApplication.hh
index 0bfa830a8a20267695db7d08faf19f7ed076da55..1546392992bb3ab29383f3c76fdf7ab79b8503e6 100644
--- a/src/VivyApplication.hh
+++ b/src/VivyApplication.hh
@@ -4,9 +4,19 @@
 #error "This is a C++ header"
 #endif
 
-#define vivyApp static_cast<VivyApplication *>(QApplication::instance())
+// Get the VivyApplication pointer
+#define vivyApp (static_cast<::Vivy::VivyApplication *>(VivyApplication::instance()))
 
-#define currentVivyDocument() dynamic_cast<VivyDocument *>(vivyApp->getCurrentDocument())
+// Install logger, use the global LogSink
+#define VIVY_APP_LOGGABLE_OBJECT(name, logger) \
+    VIVY_LOGGABLE_OBJECT(vivyApp->getLogSink(), name, logger)
+
+// Install logger, use the global LogSink, with a stored name like in the
+// VIVY_LOGGABLE_OBJECT_BY_STORED_NAME macro.
+#define VIVY_APP_LOGGABLE_OBJECT_BY_STORED_NAME(name, logger) \
+    VIVY_LOGGABLE_OBJECT_BY_STORED_NAME(vivyApp->getLogSink(), name, logger)
+
+#define currentVivyDocument   dynamic_cast<VivyDocument *>(vivyApp->getCurrentDocument())
 #define currentScriptDocument dynamic_cast<ScriptDocument *>(vivyApp->getCurrentDocument())
 
 // Only support dark theme for now
@@ -26,24 +36,37 @@
 
 // Detect MacOS
 #if defined(Q_OS_DARWIN) || defined(Q_OS_MACOS)
-#define VIVY_MACOS
+#define VIVY_MACOS 1
+#else
+#define VIVY_MACOS 0
 #endif
 
-#include "Lib/Script/ScriptStore.hh"
-#include "Lib/Document/VivyDocumentStore.hh"
-#include "UI/MainWindow.hh"
+#include "Lib/Utils.hh"
+#include "Lib/Log.hh"
+#include "Lib/HostOsInfo.hh"
 
 namespace Vivy
 {
 class MainWindow;
+class ScriptStore;
+class VivyDocumentStore;
+class AbstractDocument;
+class ScriptDocument;
+class VivyDocument;
+class Theme;
 
 // Vivy application class
 class VivyApplication : public QApplication {
     Q_OBJECT
+    VIVY_DCL_LOG_SINK(logSink)
+    VIVY_DCL_LOG_DISPATCH(logSink, stderrLogDispathcer, StderrLogSinkDispatcher)
+    VIVY_LOGGABLE_OBJECT(logSink, APPLICATION, logger)
+
+    enum class ApplicationType { GUI, CLI };
 
 public:
-    VivyDocumentStore documentStore{};
-    ScriptStore scriptStore{};
+    std::shared_ptr<VivyDocumentStore> documentStore;
+    std::shared_ptr<ScriptStore> scriptStore;
 
     enum class Font {
         Monospace,
@@ -54,8 +77,6 @@ public:
         DefaultBoldItalic
     };
 
-    enum class Theme { System, Dark, Light };
-
 private:
     int fontIdMonospace;
     int fontIdMonospaceBold;
@@ -64,11 +85,18 @@ private:
     int fontIdBold;
     int fontIdBoldItalic;
 
+    std::unique_ptr<Vivy::Theme> qtCreatorThemePtr{ nullptr };
     std::unique_ptr<MainWindow> mainWindowPtr{ nullptr };
     bool useFakeVim{ false };
+    ApplicationType selectedType{ ApplicationType::GUI };
+
+    // ApplicationType::CLI Only!
+    std::shared_ptr<ScriptDocument> selectedScriptDoc{ nullptr };
+    std::shared_ptr<VivyDocument> selectedVivyDoc{ nullptr };
 
 public:
     VivyApplication(int &argc, char **argv);
+    ~VivyApplication() noexcept override;
 
     int exec() noexcept;
 
@@ -76,9 +104,11 @@ public:
     [[nodiscard]] MainWindow *getMainWindow() const;
     [[nodiscard]] AbstractDocument *getCurrentDocument() const;
     [[nodiscard]] bool getUseFakeVimEditor() const noexcept;
+    [[nodiscard]] const Theme *getTheme() const noexcept { return qtCreatorThemePtr.get(); }
+    [[nodiscard]] std::shared_ptr<LogSink> getLogSink() noexcept { return logSink; }
 
     void setUseFakeVimEditor(bool) noexcept;
-    void setTheme(Theme) noexcept;
+    void setTheme(ThemeType, VivyThemeType) noexcept;
 };
 
 }
diff --git a/src/VivyCli.cc b/src/VivyCli.cc
deleted file mode 100644
index 0dedba63eb20d103bb628c7a8ad915dfa44ce38d..0000000000000000000000000000000000000000
--- a/src/VivyCli.cc
+++ /dev/null
@@ -1,27 +0,0 @@
-#include "VivyCli.hh"
-#include <iostream>
-#include <QTextCodec>
-
-using namespace Vivy;
-
-VivyCli::VivyCli(int &argc, char **argv) noexcept
-{
-    if (argc >= 2) {
-        selectedDoc = scriptStore.loadDocument(QString::fromUtf8(argv[1]));
-    }
-}
-
-int
-VivyCli::exec() noexcept
-{
-    QTextCodec::setCodecForLocale(QTextCodec::codecForName("UTF-8"));
-
-    if ((selectedDoc == nullptr) || (!scriptStore.executeScript(selectedDoc->getUuid())))
-        return 1;
-
-    for (const auto &str : scriptStore.getLoadedModules()) {
-        std::cout << "Module " << str << " was loaded!\n";
-        [[maybe_unused]] const auto *mod = scriptStore.getModule(str);
-    }
-    return 0;
-}
diff --git a/src/VivyCli.hh b/src/VivyCli.hh
deleted file mode 100644
index 9ceef273fa57a52fb9d2e0d2bfbf7eb31aefb037..0000000000000000000000000000000000000000
--- a/src/VivyCli.hh
+++ /dev/null
@@ -1,19 +0,0 @@
-#pragma once
-
-#ifndef __cplusplus
-#error "This is a C++ header"
-#endif
-
-#include "Lib/Script/ScriptStore.hh"
-
-namespace Vivy
-{
-class VivyCli final {
-    ScriptStore scriptStore{};
-    std::shared_ptr<ScriptDocument> selectedDoc{ nullptr };
-
-public:
-    VivyCli(int &argc, char **argv) noexcept;
-    int exec() noexcept;
-};
-}