// ==WindhawkMod== // @id taskbar-xii // @name Taskbar XII // @description ✦ Windows 12 fluent redesign // @version 4.0.0 // @author ryokr // @github https://github.com/ryokr // @homepage https://ryokr.github.io // @include explorer.exe // @architecture x86-64 // @compilerOptions -lcomctl32 -lole32 -loleaut32 -lruntimeobject -Wl,--export-all-symbols // ==/WindhawkMod== // ==WindhawkModReadme== /* ✦ Set display scale to 100% for optimal visuals. ✦ Enhance experience with the Start11 app. ![demonstration](https://raw.githubusercontent.com/ryokr/TaskbarXII/main/Assets/preview.png) */ // ==/WindhawkModReadme== #undef GetCurrentTime #include #include #include #include struct ThemeTargetStyles { PCWSTR target; std::vector styles; }; struct Theme { std::vector targetStyles; }; // const PCWSTR WeatherImage = L"Background:="; // const PCWSTR TaskbarBG = L"Background:="; const PCWSTR TaskbarBG = L"Background:="; const PCWSTR HorizontalAlignRight = L"HorizontalAlignment=Right"; const PCWSTR HorizontalAlignLeft = L"HorizontalAlignment=Left"; const PCWSTR RoundAllCorner = L"CornerRadius=4"; const PCWSTR TaskbarHeight = L"Height=56"; const PCWSTR NormalHeight = L"Height=48"; const PCWSTR WeatherIconHeight = L"MaxHeight=27"; const PCWSTR WeatherIconWidth = L"MaxWidth=27"; const PCWSTR TaskbarBGTransform = L"Transform3D:="; const PCWSTR TaskbarColumn = L"Grid.Column=1"; const PCWSTR SystemTrayColumn = L"Grid.Column=3"; const PCWSTR CenteredColumns = L"ColumnDefinitions:="; // ------------------------------------------------------------------------------------------------------------- const Theme themeTaskbarXII = {{ ThemeTargetStyles{L"ScrollViewer > ScrollContentPresenter > Border > Grid", { CenteredColumns, L"Background:=" }}, ThemeTargetStyles{L"Taskbar.TaskbarFrame#TaskbarFrame", { HorizontalAlignRight, TaskbarColumn, TaskbarHeight }}, ThemeTargetStyles{L"Taskbar.TaskbarFrame#TaskbarFrame > Grid", { NormalHeight, RoundAllCorner }}, // right round main section ThemeTargetStyles{L"Taskbar.TaskbarBackground#BackgroundControl", { NormalHeight, TaskbarBGTransform, L"Opacity=0.7" }}, ThemeTargetStyles{L"Taskbar.TaskbarBackground > Grid", { RoundAllCorner, L"Opacity=1" }}, // left round main section ThemeTargetStyles{L"Microsoft.UI.Xaml.Controls.ItemsRepeater#TaskbarFrameRepeater", { L"Margin=0,0,3,0" }}, // main section right margin ThemeTargetStyles{L"TextBlock#SearchBoxTextBlock", { L"Text=✦ RYO.O" }}, // searchbox text ThemeTargetStyles{L"Windows.UI.Xaml.Shapes.Rectangle#BackgroundStroke", { L"Visibility=Collapsed" }}, // top line in main section // ------------------------------------------------------------------------------------------------------------- ThemeTargetStyles{L"Taskbar.AugmentedEntryPointButton > Taskbar.TaskListButtonPanel", { TaskbarBG, RoundAllCorner, L"Padding=0", L"Margin=0,0,7,0" }}, ThemeTargetStyles{L"Taskbar.AugmentedEntryPointButton > Taskbar.TaskListButtonPanel > Grid", { L"Margin=8,0,0,0" }}, // ThemeTargetStyles{L"Taskbar.AugmentedEntryPointButton > Taskbar.TaskListButtonPanel", { WeatherImage, RoundAllCorner, L"Padding=0", L"Margin=0,0,7,0" }}, // ThemeTargetStyles{L"Taskbar.AugmentedEntryPointButton > Taskbar.TaskListButtonPanel > Grid", { L"Visibility=Collapsed" }}, ThemeTargetStyles{L"Border#LargeTicker1", { L"Margin=0,2,4,0" }}, ThemeTargetStyles{L"Border#LargeTicker1 > AdaptiveCards.Rendering.Uwp.WholeItemsPanel > Image", { WeatherIconHeight, WeatherIconWidth }}, // weather icon ThemeTargetStyles{L"Border#LargeTicker1 > AdaptiveCards.Rendering.Uwp.WholeItemsPanel > Microsoft.UI.Xaml.Controls.AnimatedVisualPlayer", { WeatherIconHeight, WeatherIconWidth }}, // weather icon // ------------------------------------------------------------------------------------------------------------- ThemeTargetStyles{L"SystemTray.SystemTrayFrame", { HorizontalAlignLeft, SystemTrayColumn, L"VerticalAlignment=Center" }}, ThemeTargetStyles{L"StackPanel#SystemTrayFrameGrid", { TaskbarBG, RoundAllCorner, L"Padding=8,3,0,3" }}, // ------------------------------------------------------------------------------------------------------------- // ThemeTargetStyles{L"SystemTray.Stack#NotifyIconStack", { L"Grid.Column=0" }}, // systemtray / order 0 // ThemeTargetStyles{L"SystemTray.NotificationAreaIcons#NotificationAreaIcons", { L"Grid.Column=1" }}, // restart icon / order 1 // ThemeTargetStyles{L"SystemTray.Stack#NonActivatableStack", { L"Grid.Column=3" }}, // mic icon / order 3 // ThemeTargetStyles{L"StackPanel#SystemTrayFrameGrid > SystemTray.Stack#SecondaryClockStack", { L"Grid.Column=8" }}, // ThemeTargetStyles{L"StackPanel#SystemTrayFrameGrid > SystemTray.OmniButton#ControlCenterButton", { L"Grid.Column=4" }}, // ThemeTargetStyles{L"StackPanel#SystemTrayFrameGrid > SystemTray.OmniButton#NotificationCenterButton", { L"Grid.Column=5" }}, // ThemeTargetStyles{L"StackPanel#SystemTrayFrameGrid > SystemTray.Stack#MainStack", { L"Grid.Column=6" }}, // ThemeTargetStyles{L"StackPanel#SystemTrayFrameGrid > SystemTray.Stack#ShowDesktopStack", { L"Grid.Column=7" }}, // ------------------------------------------------------------------------------------------------------------- ThemeTargetStyles{L"TextBlock#InnerTextBlock[Text=\uE971]", { L"Text=\uED14" }}, // uE712-dots uE878-triangle uED14-QR ThemeTargetStyles{L"SystemTray.DateTimeIconContent > Grid > StackPanel", { L"Orientation=Horizontal", L"Spacing=12" }}, ThemeTargetStyles{L"TextBlock#TimeInnerTextBlock", { L"FontSize=15", L"FontWeight=Bold" }}, ThemeTargetStyles{L"TextBlock#DateInnerTextBlock", { L"FontSize=15", L"FontWeight=SemiBold" }}, }}; // ------------------------------------------------------------------------------------------------------------- std::atomic g_targetThreadId = 0; void ApplyCustomizations(InstanceHandle handle, winrt::Windows::UI::Xaml::FrameworkElement element, PCWSTR fallbackClassName); void CleanupCustomizations(InstanceHandle handle); HMODULE GetCurrentModuleHandle() { HMODULE module; if (!GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, L"", &module)) return nullptr; return module; } // ------------------------------------------------------------------------------------------------------------- #pragma region winrt_hpp #include #include #include namespace winrt { namespace Windows { namespace Foundation::Collections {} namespace UI::Xaml { namespace Controls {} namespace Hosting {} } } } // ------------------------------------------------------------------------------------------------------------- #pragma region visualtreewatcher_hpp #include class VisualTreeWatcher : public winrt::implements { public: VisualTreeWatcher(winrt::com_ptr site); VisualTreeWatcher(const VisualTreeWatcher&) = delete; VisualTreeWatcher& operator=(const VisualTreeWatcher&) = delete; VisualTreeWatcher(VisualTreeWatcher&&) = delete; VisualTreeWatcher& operator=(VisualTreeWatcher&&) = delete; void UnadviseVisualTreeChange(); private: HRESULT STDMETHODCALLTYPE OnVisualTreeChange(ParentChildRelation relation, VisualElement element, VisualMutationType mutationType) override; HRESULT STDMETHODCALLTYPE OnElementStateChanged(InstanceHandle element, VisualElementState elementState, LPCWSTR context) noexcept override; template T FromHandle(InstanceHandle handle) { winrt::Windows::Foundation::IInspectable obj; winrt::check_hresult(m_XamlDiagnostics->GetIInspectableFromHandle(handle, reinterpret_cast<::IInspectable**>(winrt::put_abi(obj)))); return obj.as(); } winrt::com_ptr m_XamlDiagnostics = nullptr; }; // ------------------------------------------------------------------------------------------------------------- #pragma region visualtreewatcher_cpp #include VisualTreeWatcher::VisualTreeWatcher(winrt::com_ptr site) : m_XamlDiagnostics(site.as()) { winrt::check_hresult(m_XamlDiagnostics.as()->AdviseVisualTreeChange(this)); } void VisualTreeWatcher::UnadviseVisualTreeChange() { winrt::check_hresult(m_XamlDiagnostics.as()->UnadviseVisualTreeChange(this)); } HRESULT VisualTreeWatcher::OnVisualTreeChange(ParentChildRelation, VisualElement element, VisualMutationType mutationType) try { if (GetCurrentThreadId() != g_targetThreadId) return S_OK; if (mutationType == Add) { const auto inspectable = FromHandle(element.Handle); auto frameworkElement = inspectable.try_as(); if (!frameworkElement) { const auto desktopXamlSource = FromHandle(element.Handle); frameworkElement = desktopXamlSource.Content().try_as(); } if (frameworkElement) { ApplyCustomizations(element.Handle, frameworkElement, element.Type); } } else if (mutationType == Remove) CleanupCustomizations(element.Handle); return S_OK; } catch (...) { winrt::to_hresult(); return S_OK; } HRESULT VisualTreeWatcher::OnElementStateChanged(InstanceHandle, VisualElementState, LPCWSTR) noexcept { return S_OK; } // ------------------------------------------------------------------------------------------------------------- #pragma region tap_hpp #include winrt::com_ptr g_visualTreeWatcher; static constexpr CLSID CLSID_WindhawkTAP = { 0xc85d8cc7, 0x5463, 0x40e8, { 0xa4, 0x32, 0xf5, 0x91, 0x6b, 0x64, 0x27, 0xe5 } }; // {C85D8CC7-5463-40E8-A432-F5916B6427E5} class WindhawkTAP : public winrt::implements { public: HRESULT STDMETHODCALLTYPE SetSite(IUnknown *pUnkSite) override; HRESULT STDMETHODCALLTYPE GetSite(REFIID riid, void **ppvSite) noexcept override; private: winrt::com_ptr site; }; // ------------------------------------------------------------------------------------------------------------- #pragma region tap_cpp HRESULT WindhawkTAP::SetSite(IUnknown *pUnkSite) try { if (g_visualTreeWatcher) { g_visualTreeWatcher->UnadviseVisualTreeChange(); g_visualTreeWatcher = nullptr; } site.copy_from(pUnkSite); if (site) { FreeLibrary(GetCurrentModuleHandle()); g_visualTreeWatcher = winrt::make_self(site); } return S_OK; } catch (...) { return winrt::to_hresult(); } HRESULT WindhawkTAP::GetSite(REFIID riid, void **ppvSite) noexcept { return site.as(riid, ppvSite); } // ------------------------------------------------------------------------------------------------------------- #pragma region simplefactory_hpp #include template struct SimpleFactory : winrt::implements, IClassFactory> { HRESULT STDMETHODCALLTYPE CreateInstance(IUnknown* pUnkOuter, REFIID riid, void** ppvObject) override try { if (!pUnkOuter) { *ppvObject = nullptr; return winrt::make().as(riid, ppvObject); } return CLASS_E_NOAGGREGATION; } catch (...) { return winrt::to_hresult(); } HRESULT STDMETHODCALLTYPE LockServer(BOOL) noexcept override { return S_OK; } }; // ------------------------------------------------------------------------------------------------------------- #pragma region module_cpp #include #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdll-attribute-on-redeclaration" __declspec(dllexport) _Use_decl_annotations_ STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID* ppv) try { if (rclsid == CLSID_WindhawkTAP) { *ppv = nullptr; return winrt::make>().as(riid, ppv); } else return CLASS_E_CLASSNOTAVAILABLE; } catch (...) { return winrt::to_hresult(); } __declspec(dllexport) _Use_decl_annotations_ STDAPI DllCanUnloadNow(void) { if (winrt::get_module_lock()) return S_FALSE; else return S_OK; } #pragma clang diagnostic pop // ------------------------------------------------------------------------------------------------------------- #pragma region api_cpp using PFN_INITIALIZE_XAML_DIAGNOSTICS_EX = decltype(&InitializeXamlDiagnosticsEx); HRESULT InjectWindhawkTAP() noexcept { HMODULE module = GetCurrentModuleHandle(); if (!module) return HRESULT_FROM_WIN32(GetLastError()); WCHAR location[MAX_PATH]; switch (GetModuleFileName(module, location, ARRAYSIZE(location))) { case 0: case ARRAYSIZE(location): return HRESULT_FROM_WIN32(GetLastError()); } const HMODULE wux(LoadLibraryEx(L"Windows.UI.Xaml.dll", nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32)); if (!wux) [[unlikely]] return HRESULT_FROM_WIN32(GetLastError()); const auto ixde = reinterpret_cast(GetProcAddress(wux, "InitializeXamlDiagnosticsEx")); if (!ixde) [[unlikely]] return HRESULT_FROM_WIN32(GetLastError()); const HRESULT hr2 = ixde(L"VisualDiagConnection1", GetCurrentProcessId(), nullptr, location, CLSID_WindhawkTAP, nullptr); if (FAILED(hr2)) [[unlikely]] return hr2; return S_OK; } // ------------------------------------------------------------------------------------------------------------- #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace winrt::Windows::UI::Xaml; using PropertyKeyValue = std::pair; using PropertyValuesUnresolved = std::vector>; using PropertyValues = std::vector; using PropertyValuesMaybeUnresolved = std::variant; struct StyleRule { std::wstring name; std::wstring visualState; std::wstring value; bool isXamlValue = false; }; using PropertyOverridesUnresolved = std::vector; using PropertyOverrides = std::unordered_map>; using PropertyOverridesMaybeUnresolved = std::variant; struct ElementMatcher { std::wstring type; std::wstring name; std::optional visualStateGroupName; int oneBasedIndex = 0; PropertyValuesMaybeUnresolved propertyValues; }; struct ElementCustomizationRules { ElementMatcher elementMatcher; std::vector parentElementMatchers; PropertyOverridesMaybeUnresolved propertyOverrides; }; std::vector g_elementsCustomizationRules; struct ElementPropertyCustomizationState { std::optional originalValue; std::optional customValue; int64_t propertyChangedToken = 0; }; struct ElementCustomizationStateForVisualStateGroup { std::unordered_map propertyCustomizationStates; winrt::event_token VSGCurrentStateChangedToken; }; struct ElementCustomizationState { winrt::weak_ref element; std::list>, ElementCustomizationStateForVisualStateGroup>> perVisualStateGroup; }; std::unordered_map g_elementsCustomizationState; struct BackgroundFillDelayedApplyData { UINT_PTR timer = 0; winrt::weak_ref element; std::wstring fallbackClassName; }; std::unordered_map g_backgroundFillDelayedApplyData; bool g_elementPropertyModifying; winrt::Windows::Foundation::IInspectable ReadLocalValueWithWorkaround( DependencyObject elementDo, DependencyProperty property) { const auto value = elementDo.ReadLocalValue(property); if (value && winrt::get_class_name(value) == L"Windows.UI.Xaml.Data.BindingExpressionBase") return elementDo.GetAnimationBaseValue(property); return value; } void SetOrClearValue(DependencyObject elementDo, DependencyProperty property, winrt::Windows::Foundation::IInspectable value) { if (value == DependencyProperty::UnsetValue()) { elementDo.ClearValue(property); return; } try { if (property == Controls::TextBlock::FontWeightProperty()) { auto valueInt = value.try_as(); if (valueInt && *valueInt >= std::numeric_limits::min() && *valueInt <= std::numeric_limits::max()) { value = winrt::box_value(winrt::Windows::UI::Text::FontWeight{ static_cast(*valueInt) }); } } elementDo.SetValue(property, value); } catch (winrt::hresult_error const& ex) {} } std::wstring EscapeXmlAttribute(std::wstring_view data) { std::wstring buffer; buffer.reserve(data.size()); for (size_t pos = 0; pos != data.size(); ++pos) { switch (data[pos]) { case '&': buffer.append(L"&"); break; case '\"': buffer.append(L"""); break; // case '\'': // buffer.append(L"'"); // break; case '<': buffer.append(L"<"); break; case '>': buffer.append(L">"); break; default: buffer.append(&data[pos], 1); break; } } return buffer; } std::wstring_view TrimStringView(std::wstring_view s) { s.remove_prefix(std::min(s.find_first_not_of(L" \t\r\v\n"), s.size())); s.remove_suffix(std::min(s.size() - s.find_last_not_of(L" \t\r\v\n") - 1, s.size())); return s; } std::vector SplitStringView(std::wstring_view s, std::wstring_view delimiter) { size_t pos_start = 0, pos_end, delim_len = delimiter.length(); std::wstring_view token; std::vector res; while ((pos_end = s.find(delimiter, pos_start)) != std::wstring_view::npos) { token = s.substr(pos_start, pos_end - pos_start); pos_start = pos_end + delim_len; res.push_back(token); } res.push_back(s.substr(pos_start)); return res; } Style GetStyleFromXamlSetters(const std::wstring_view type, const std::wstring_view xamlStyleSetters) { std::wstring xaml = LR"(\n" L" \n" L""; auto resourceDictionary = Markup::XamlReader::Load(xaml).as(); auto [styleKey, styleInspectable] = resourceDictionary.First().Current(); return styleInspectable.as