Skip to content

Instantly share code, notes, and snippets.

@sylveon
Created January 2, 2019 15:56
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save sylveon/5bc68b2801333b24f7b3165c3f098cc9 to your computer and use it in GitHub Desktop.
Save sylveon/5bc68b2801333b24f7b3165c3f098cc9 to your computer and use it in GitHub Desktop.
#include "picker.hpp"
#include <dwmapi.h>
#include <ShellScalingApi.h>
#include <windows.ui.xaml.hosting.desktopwindowxamlsource.h>
#include <winrt/Windows.UI.Xaml.Media.h>
using namespace winrt;
using namespace Windows::UI;
using namespace Windows::UI::Xaml;
extern "C" IMAGE_DOS_HEADER __ImageBase;
LRESULT ColorPicker::WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
auto pThis = reinterpret_cast<ColorPicker *>(GetWindowLongPtr(hwnd, GWLP_USERDATA));
if (uMsg == WM_NCCREATE)
{
auto cs = reinterpret_cast<CREATESTRUCT *>(lParam);
pThis = static_cast<ColorPicker *>(cs->lpCreateParams);
SetWindowLongPtr(hwnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(pThis));
}
else if (pThis)
{
switch (uMsg)
{
case WM_NCCALCSIZE:
return pThis->OnNonClientCalculateSize();
case WM_SIZE:
return pThis->OnSizeChanged(LOWORD(lParam), HIWORD(lParam));
case WM_DPICHANGED:
return pThis->OnDpiChanged(LOWORD(wParam));
case WM_DWMCOMPOSITIONCHANGED:
return pThis->OnCompositionChanged();
}
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
UIElement ColorPicker::CreateContent(COLORREF col)
{
Controls::StackPanel root;
root.RenderTransform(m_scaler);
root.Padding(ThicknessHelper::FromUniformLength(12));
if (auto system_brush = root.Resources().Lookup(box_value(L"SystemControlAcrylicWindowBrush")).try_as<Media::Brush>())
{
root.Background(system_brush);
}
Controls::ColorPicker picker;
picker.IsAlphaEnabled(true);
picker.Color(ColorHelper::FromArgb((col & 0xFF000000) >> 24, col & 0xFF, (col & 0xFF00) >> 8, (col & 0xFF0000) >> 16));
root.Children().Append(picker);
Controls::StackPanel buttons;
buttons.HorizontalAlignment(HorizontalAlignment::Right);
buttons.Orientation(Controls::Orientation::Horizontal);
Controls::Button ok;
ok.Margin(ThicknessHelper::FromLengths(0, 12, 6, 0));
ok.Width(100);
ok.Content(box_value(L"OK"));
buttons.Children().Append(ok);
Controls::Button cancel;
cancel.Margin(ThicknessHelper::FromLengths(6, 12, 0, 0));
cancel.Width(100);
cancel.Content(box_value(L"Cancel"));
buttons.Children().Append(cancel);
root.Children().Append(buttons);
return root;
}
LRESULT ColorPicker::OnNonClientCalculateSize()
{
return 0;
}
LRESULT ColorPicker::OnSizeChanged(int width, int height)
{
SetWindowPos(m_interopHwnd, NULL, 0, 0, width, height, SWP_NOZORDER | SWP_NOOWNERZORDER);
return 0;
}
LRESULT ColorPicker::OnDpiChanged(unsigned int dpi)
{
m_scaler.ScaleX(dpi / static_cast<double>(USER_DEFAULT_SCREEN_DPI));
m_scaler.ScaleY(dpi / static_cast<double>(USER_DEFAULT_SCREEN_DPI));
return 0;
}
LRESULT ColorPicker::OnCompositionChanged()
{
const MARGINS mar = { -1 };
DwmExtendFrameIntoClientArea(m_hWnd, &mar);
return 0;
}
ColorPicker::ColorPicker(COLORREF col) :
m_manager(Hosting::WindowsXamlManager::InitializeForCurrentThread()),
m_interopHwnd(nullptr),
m_hWnd(nullptr)
{
m_hWnd = CreateWindowEx(
WS_EX_TOPMOST,
CLASS_NAME,
L"Color Picker",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
nullptr,
nullptr,
reinterpret_cast<HINSTANCE>(&__ImageBase), // TODO: better way of getting instance handle
this
);
if (!m_hWnd)
{
throw_last_error();
}
OnCompositionChanged();
winrt::com_ptr<IDesktopWindowXamlSourceNative> native_source;
check_hresult(get_unknown(m_source)->QueryInterface(IID_PPV_ARGS(native_source.put())));
check_hresult(native_source->AttachToWindow(m_hWnd));
check_hresult(native_source->get_WindowHandle(&m_interopHwnd));
POINT point;
if (!GetCursorPos(&point))
{
throw_last_error();
}
HMONITOR mon = MonitorFromPoint(point, MONITOR_DEFAULTTONEAREST);
UINT dpiX, dpiY;
check_hresult(GetDpiForMonitor(mon, MDT_EFFECTIVE_DPI, &dpiX, &dpiY));
OnDpiChanged(dpiX);
m_source.Content(CreateContent(col));
// TODO: get right size to use
// https://docs.microsoft.com/en-us/windows/uwp/xaml-platform/using-the-xaml-hosting-api#how-to-handle-layout-changes
SetWindowPos(m_hWnd, NULL, 0, 0, 600, 900, SWP_NOMOVE | SWP_NOZORDER | SWP_NOOWNERZORDER);
ShowWindow(m_interopHwnd, SW_SHOW);
ShowWindow(m_hWnd, SW_SHOW);
}
ColorPicker::~ColorPicker()
{
m_manager.Close();
}
bool ColorPicker::RegisterWindowClass(HINSTANCE hInst)
{
WNDCLASSEX wc = {
sizeof(wc),
CS_HREDRAW | CS_VREDRAW,
WindowProc,
0,
0,
hInst,
nullptr,
LoadCursor(nullptr, IDC_ARROW),
nullptr,
nullptr,
CLASS_NAME,
nullptr
};
return RegisterClassEx(&wc);
}
void ColorPicker::UnregisterWindowClass(HINSTANCE hInst)
{
UnregisterClass(CLASS_NAME, hInst);
}
#pragma once
#include "../TranslucentTB/arch.h"
#include <windef.h>
#include <Unknwn.h>
#undef GetCurrentTime
#include <winrt/Windows.UI.Xaml.Controls.h>
#include <winrt/Windows.UI.Xaml.Hosting.h>
#ifdef _COLORPICKER_DLL
#define COLORPICKER_EXPORT dllexport
#else
#define COLORPICKER_EXPORT dllimport
#endif
class ColorPicker {
private:
static constexpr wchar_t CLASS_NAME[] = L"TTBColorPicker";
winrt::Windows::UI::Xaml::Hosting::WindowsXamlManager m_manager;
winrt::Windows::UI::Xaml::Hosting::DesktopWindowXamlSource m_source;
winrt::Windows::UI::Xaml::Media::ScaleTransform m_scaler;
HWND m_interopHwnd, m_hWnd;
static LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
winrt::Windows::UI::Xaml::UIElement CreateContent(COLORREF col);
LRESULT OnNonClientCalculateSize();
LRESULT OnSizeChanged(int width, int height);
LRESULT OnDpiChanged(unsigned int dpi);
LRESULT OnCompositionChanged();
public:
__declspec(COLORPICKER_EXPORT) ColorPicker(COLORREF col);
~ColorPicker();
ColorPicker(const ColorPicker &) = delete;
ColorPicker &operator =(const ColorPicker &) = delete;
static bool RegisterWindowClass(HINSTANCE hInst);
static void UnregisterWindowClass(HINSTANCE hInst);
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment