637 lines
		
	
	
	
		
			17 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			637 lines
		
	
	
	
		
			17 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
// This file is part of Desktop App Toolkit,
 | 
						|
// a set of libraries for developing nice desktop applications.
 | 
						|
//
 | 
						|
// For license and copyright information please follow this link:
 | 
						|
// https://github.com/desktop-app/legal/blob/master/LEGAL
 | 
						|
//
 | 
						|
#include "ui/platform/win/ui_window_shadow_win.h"
 | 
						|
 | 
						|
#include "ui/rp_widget.h"
 | 
						|
#include "ui/platform/win/ui_window_win.h"
 | 
						|
#include "base/platform/base_platform_info.h"
 | 
						|
#include "styles/style_widgets.h"
 | 
						|
 | 
						|
#include <QtGui/QPainter>
 | 
						|
#include <QtWidgets/QApplication>
 | 
						|
#include <QtWidgets/QDesktopWidget>
 | 
						|
 | 
						|
#include <windowsx.h>
 | 
						|
 | 
						|
// WM_POINTER support from Windows 8 onwards (WINVER >= 0x0602)
 | 
						|
#ifndef WM_POINTERUPDATE
 | 
						|
#  define WM_NCPOINTERUPDATE 0x0241
 | 
						|
#  define WM_NCPOINTERDOWN   0x0242
 | 
						|
#  define WM_NCPOINTERUP     0x0243
 | 
						|
#  define WM_POINTERUPDATE   0x0245
 | 
						|
#  define WM_POINTERDOWN     0x0246
 | 
						|
#  define WM_POINTERUP       0x0247
 | 
						|
#  define WM_POINTERENTER    0x0249
 | 
						|
#  define WM_POINTERLEAVE    0x024A
 | 
						|
#  define WM_POINTERACTIVATE 0x024B
 | 
						|
#  define WM_POINTERCAPTURECHANGED 0x024C
 | 
						|
#  define WM_POINTERWHEEL    0x024E
 | 
						|
#  define WM_POINTERHWHEEL   0x024F
 | 
						|
#endif // WM_POINTERUPDATE
 | 
						|
 | 
						|
namespace Ui {
 | 
						|
namespace Platform {
 | 
						|
namespace {
 | 
						|
 | 
						|
base::flat_map<HWND, not_null<WindowShadow*>> ShadowByHandle;
 | 
						|
 | 
						|
} // namespace
 | 
						|
 | 
						|
WindowShadow::WindowShadow(not_null<RpWidget*> window, QColor color)
 | 
						|
: _window(window)
 | 
						|
, _handle(GetWindowHandle(window))
 | 
						|
, _windows11(::Platform::IsWindows11OrGreater()) {
 | 
						|
	init(color);
 | 
						|
}
 | 
						|
 | 
						|
WindowShadow::~WindowShadow() {
 | 
						|
	destroy();
 | 
						|
}
 | 
						|
 | 
						|
void WindowShadow::setColor(QColor value) {
 | 
						|
	_r = value.red();
 | 
						|
	_g = value.green();
 | 
						|
	_b = value.blue();
 | 
						|
	if (!working()) {
 | 
						|
		return;
 | 
						|
	}
 | 
						|
 | 
						|
	auto brush = getBrush(_alphas[0]);
 | 
						|
	for (auto i = 0; i != 4; ++i) {
 | 
						|
		auto graphics = Gdiplus::Graphics(_contexts[i]);
 | 
						|
		graphics.SetCompositingMode(Gdiplus::CompositingModeSourceCopy);
 | 
						|
		if ((i % 2) && _h || !(i % 2) && _w) {
 | 
						|
			const auto width = (i % 2) ? _size : _w;
 | 
						|
			const auto height = (i % 2) ? _h : _size;
 | 
						|
			graphics.FillRectangle(&brush, 0, 0, width, height);
 | 
						|
		}
 | 
						|
	}
 | 
						|
	initCorners();
 | 
						|
 | 
						|
	_x = _y = _w = _h = 0;
 | 
						|
	update(Change::Moved | Change::Resized);
 | 
						|
}
 | 
						|
 | 
						|
bool WindowShadow::working() const {
 | 
						|
	return (_handle != nullptr) && (_handles[0] != nullptr);
 | 
						|
}
 | 
						|
 | 
						|
void WindowShadow::destroy() {
 | 
						|
	for (int i = 0; i < 4; ++i) {
 | 
						|
		if (_contexts[i]) {
 | 
						|
			DeleteDC(_contexts[i]);
 | 
						|
			_contexts[i] = nullptr;
 | 
						|
		}
 | 
						|
		if (_bitmaps[i]) {
 | 
						|
			DeleteObject(_bitmaps[i]);
 | 
						|
			_bitmaps[i] = nullptr;
 | 
						|
		}
 | 
						|
		if (_handles[i]) {
 | 
						|
			ShadowByHandle.remove(_handles[i]);
 | 
						|
			DestroyWindow(_handles[i]);
 | 
						|
			_handles[i] = nullptr;
 | 
						|
		}
 | 
						|
	}
 | 
						|
	if (_screenContext) {
 | 
						|
		ReleaseDC(nullptr, _screenContext);
 | 
						|
		_screenContext = nullptr;
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
void WindowShadow::init(QColor color) {
 | 
						|
	if (!_handle) {
 | 
						|
		return;
 | 
						|
	}
 | 
						|
 | 
						|
	initBlend();
 | 
						|
 | 
						|
	_fullsize = st::windowShadow.width();
 | 
						|
	_shift = st::windowShadowShift;
 | 
						|
	auto cornersImage = QImage(
 | 
						|
		QSize(_fullsize, _fullsize),
 | 
						|
		QImage::Format_ARGB32_Premultiplied);
 | 
						|
	cornersImage.fill(QColor(0, 0, 0));
 | 
						|
	{
 | 
						|
		QPainter p(&cornersImage);
 | 
						|
		p.setCompositionMode(QPainter::CompositionMode_Source);
 | 
						|
		st::windowShadow.paint(p, 0, 0, _fullsize, QColor(255, 255, 255));
 | 
						|
	}
 | 
						|
	if (style::RightToLeft()) {
 | 
						|
		cornersImage = cornersImage.mirrored(true, false);
 | 
						|
	}
 | 
						|
	const auto pixels = cornersImage.bits();
 | 
						|
	const auto pixel = [&](int x, int y) {
 | 
						|
		if (x < 0 || y < 0) {
 | 
						|
			return 0;
 | 
						|
		}
 | 
						|
		const auto data = pixels
 | 
						|
			+ (cornersImage.bytesPerLine() * y)
 | 
						|
			+ (sizeof(uint32) * x);
 | 
						|
		return int(data[0]);
 | 
						|
	};
 | 
						|
 | 
						|
	_metaSize = _fullsize + 2 * _shift;
 | 
						|
	_alphas.reserve(_metaSize);
 | 
						|
	_colors.reserve(_metaSize * _metaSize);
 | 
						|
	for (auto j = 0; j != _metaSize; ++j) {
 | 
						|
		for (auto i = 0; i != _metaSize; ++i) {
 | 
						|
			const auto value = pixel(i - 2 * _shift, j - 2 * _shift);
 | 
						|
			_colors.push_back(uchar(std::max(value, 1)));
 | 
						|
		}
 | 
						|
	}
 | 
						|
	auto previous = uchar(0);
 | 
						|
	for (auto i = 0; i != _metaSize; ++i) {
 | 
						|
		const auto alpha = _colors[(_metaSize - 1) * _metaSize + i];
 | 
						|
		if (alpha < previous) {
 | 
						|
			break;
 | 
						|
		}
 | 
						|
		_alphas.push_back(alpha);
 | 
						|
		previous = alpha;
 | 
						|
	}
 | 
						|
	_size = _alphas.size() - 2 * _shift;
 | 
						|
 | 
						|
	Gdiplus::GdiplusStartupInput gdiplusStartupInput;
 | 
						|
	ULONG_PTR gdiplusToken;
 | 
						|
	Gdiplus::Status gdiRes = Gdiplus::GdiplusStartup(
 | 
						|
		&gdiplusToken,
 | 
						|
		&gdiplusStartupInput,
 | 
						|
		NULL);
 | 
						|
 | 
						|
	if (gdiRes != Gdiplus::Ok) {
 | 
						|
		return;
 | 
						|
	}
 | 
						|
 | 
						|
	_screenContext = GetDC(nullptr);
 | 
						|
	if (!_screenContext) {
 | 
						|
		return;
 | 
						|
	}
 | 
						|
 | 
						|
	const auto avail = QApplication::desktop()->availableGeometry();
 | 
						|
	_widthMax = std::max(avail.width(), 1);
 | 
						|
	_heightMax = std::max(avail.height(), 1);
 | 
						|
 | 
						|
	static const auto instance = (HINSTANCE)GetModuleHandle(nullptr);
 | 
						|
	static const auto className = u"WindowShadow"_q;
 | 
						|
	static const auto wcharClassName = className.toStdWString();
 | 
						|
	static const auto registered = [] {
 | 
						|
		auto wc = WNDCLASSEX();
 | 
						|
		wc.cbSize = sizeof(wc);
 | 
						|
		wc.style = 0;
 | 
						|
		wc.lpfnWndProc = WindowCallback;
 | 
						|
		wc.cbClsExtra = 0;
 | 
						|
		wc.cbWndExtra = 0;
 | 
						|
		wc.hInstance = instance;
 | 
						|
		wc.hIcon = 0;
 | 
						|
		wc.hCursor = 0;
 | 
						|
		wc.hbrBackground = 0;
 | 
						|
		wc.lpszMenuName = NULL;
 | 
						|
		wc.lpszClassName = wcharClassName.c_str();
 | 
						|
		wc.hIconSm = 0;
 | 
						|
		return RegisterClassEx(&wc) ? true : false;
 | 
						|
	}();
 | 
						|
	if (!registered) {
 | 
						|
		return;
 | 
						|
	}
 | 
						|
	for (auto i = 0; i != 4; ++i) {
 | 
						|
		_handles[i] = CreateWindowEx(
 | 
						|
			WS_EX_LAYERED | WS_EX_TOOLWINDOW,
 | 
						|
			wcharClassName.c_str(),
 | 
						|
			0,
 | 
						|
			WS_POPUP,
 | 
						|
			0,
 | 
						|
			0,
 | 
						|
			0,
 | 
						|
			0,
 | 
						|
			0,
 | 
						|
			0,
 | 
						|
			instance,
 | 
						|
			0);
 | 
						|
		if (!_handles[i]) {
 | 
						|
			destroy();
 | 
						|
			return;
 | 
						|
		}
 | 
						|
		ShadowByHandle.emplace(_handles[i], this);
 | 
						|
		SetWindowLongPtr(_handles[i], GWLP_HWNDPARENT, (LONG_PTR)_handle);
 | 
						|
 | 
						|
		_contexts[i] = CreateCompatibleDC(_screenContext);
 | 
						|
		if (!_contexts[i]) {
 | 
						|
			destroy();
 | 
						|
			return;
 | 
						|
		}
 | 
						|
 | 
						|
		const auto width = (i % 2) ? _size : _widthMax;
 | 
						|
		const auto height = (i % 2) ? _heightMax : _size;
 | 
						|
		_bitmaps[i] = CreateCompatibleBitmap(_screenContext, width, height);
 | 
						|
		if (!_bitmaps[i]) {
 | 
						|
			return;
 | 
						|
		}
 | 
						|
 | 
						|
		SelectObject(_contexts[i], _bitmaps[i]);
 | 
						|
	}
 | 
						|
	setColor(color);
 | 
						|
}
 | 
						|
 | 
						|
void WindowShadow::initCorners(Directions directions) {
 | 
						|
	const auto hor = (directions & Direction::Horizontal);
 | 
						|
	const auto ver = (directions & Direction::Vertical);
 | 
						|
	auto graphics0 = Gdiplus::Graphics(_contexts[0]);
 | 
						|
	auto graphics1 = Gdiplus::Graphics(_contexts[1]);
 | 
						|
	auto graphics2 = Gdiplus::Graphics(_contexts[2]);
 | 
						|
	auto graphics3 = Gdiplus::Graphics(_contexts[3]);
 | 
						|
	graphics0.SetCompositingMode(Gdiplus::CompositingModeSourceCopy);
 | 
						|
	graphics1.SetCompositingMode(Gdiplus::CompositingModeSourceCopy);
 | 
						|
	graphics2.SetCompositingMode(Gdiplus::CompositingModeSourceCopy);
 | 
						|
	graphics3.SetCompositingMode(Gdiplus::CompositingModeSourceCopy);
 | 
						|
 | 
						|
	auto brush = getBrush(_alphas[0]);
 | 
						|
	if (hor) {
 | 
						|
		graphics0.FillRectangle(&brush, 0, 0, _fullsize - (_size - _shift), 2 * _shift);
 | 
						|
	}
 | 
						|
 | 
						|
	if (ver) {
 | 
						|
		graphics1.FillRectangle(&brush, 0, 0, _size, 2 * _shift);
 | 
						|
		graphics3.FillRectangle(&brush, 0, 0, _size, 2 * _shift);
 | 
						|
		graphics1.FillRectangle(&brush, _size - _shift, 2 * _shift, _shift, _fullsize);
 | 
						|
		graphics3.FillRectangle(&brush, 0, 2 * _shift, _shift, _fullsize);
 | 
						|
	}
 | 
						|
 | 
						|
	if (hor) {
 | 
						|
		for (int j = 2 * _shift; j < _size; ++j) {
 | 
						|
			for (int k = 0; k < _fullsize - (_size - _shift); ++k) {
 | 
						|
				brush.SetColor(getColor(_colors[j * _metaSize + k + (_size + _shift)]));
 | 
						|
				graphics0.FillRectangle(&brush, k, j, 1, 1);
 | 
						|
				graphics2.FillRectangle(&brush, k, _size - (j - 2 * _shift) - 1, 1, 1);
 | 
						|
			}
 | 
						|
		}
 | 
						|
		for (int j = _size; j < _size + 2 * _shift; ++j) {
 | 
						|
			for (int k = 0; k < _fullsize - (_size - _shift); ++k) {
 | 
						|
				brush.SetColor(getColor(_colors[j * _metaSize + k + (_size + _shift)]));
 | 
						|
				graphics2.FillRectangle(&brush, k, _size - (j - 2 * _shift) - 1, 1, 1);
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
	if (ver) {
 | 
						|
		for (int j = 2 * _shift; j < _fullsize + 2 * _shift; ++j) {
 | 
						|
			for (int k = _shift; k < _size; ++k) {
 | 
						|
				brush.SetColor(getColor(_colors[j * _metaSize + (k + _shift)]));
 | 
						|
				graphics1.FillRectangle(&brush, _size - k - 1, j, 1, 1);
 | 
						|
				graphics3.FillRectangle(&brush, k, j, 1, 1);
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
void WindowShadow::verCorners(int h, Gdiplus::Graphics *pgraphics1, Gdiplus::Graphics *pgraphics3) {
 | 
						|
	auto brush = getBrush(_alphas[0]);
 | 
						|
	pgraphics1->FillRectangle(&brush, _size - _shift, h - _fullsize, _shift, _fullsize);
 | 
						|
	pgraphics3->FillRectangle(&brush, 0, h - _fullsize, _shift, _fullsize);
 | 
						|
	for (int j = 0; j < _fullsize; ++j) {
 | 
						|
		for (int k = _shift; k < _size; ++k) {
 | 
						|
			brush.SetColor(getColor(_colors[(j + 2 * _shift) * _metaSize + k + _shift]));
 | 
						|
			pgraphics1->FillRectangle(&brush, _size - k - 1, h - j - 1, 1, 1);
 | 
						|
			pgraphics3->FillRectangle(&brush, k, h - j - 1, 1, 1);
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
void WindowShadow::horCorners(int w, Gdiplus::Graphics *pgraphics0, Gdiplus::Graphics *pgraphics2) {
 | 
						|
	auto brush = getBrush(_alphas[0]);
 | 
						|
	pgraphics0->FillRectangle(&brush, w - 2 * _size - (_fullsize - (_size - _shift)), 0, _fullsize - (_size - _shift), 2 * _shift);
 | 
						|
	for (int j = 2 * _shift; j < _size; ++j) {
 | 
						|
		for (int k = 0; k < _fullsize - (_size - _shift); ++k) {
 | 
						|
			brush.SetColor(getColor(_colors[j * _metaSize + k + (_size + _shift)]));
 | 
						|
			pgraphics0->FillRectangle(&brush, w - 2 * _size - k - 1, j, 1, 1);
 | 
						|
			pgraphics2->FillRectangle(&brush, w - 2 * _size - k - 1, _size - (j - 2 * _shift) - 1, 1, 1);
 | 
						|
		}
 | 
						|
	}
 | 
						|
	for (int j = _size; j < _size + 2 * _shift; ++j) {
 | 
						|
		for (int k = 0; k < _fullsize - (_size - _shift); ++k) {
 | 
						|
			brush.SetColor(getColor(_colors[j * _metaSize + k + (_size + _shift)]));
 | 
						|
			pgraphics2->FillRectangle(&brush, w - 2 * _size - k - 1, _size - (j - 2 * _shift) - 1, 1, 1);
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
Gdiplus::Color WindowShadow::getColor(uchar alpha) const {
 | 
						|
	return Gdiplus::Color(BYTE(_windows11 ? 1 : alpha), _r, _g, _b);
 | 
						|
}
 | 
						|
 | 
						|
Gdiplus::SolidBrush WindowShadow::getBrush(uchar alpha) const {
 | 
						|
	return Gdiplus::SolidBrush(getColor(alpha));
 | 
						|
}
 | 
						|
 | 
						|
Gdiplus::Pen WindowShadow::getPen(uchar alpha) const {
 | 
						|
	return Gdiplus::Pen(getColor(alpha));
 | 
						|
}
 | 
						|
 | 
						|
void WindowShadow::update(Changes changes, WINDOWPOS *pos) {
 | 
						|
	if (!working()) {
 | 
						|
		return;
 | 
						|
	}
 | 
						|
 | 
						|
	if (changes == Changes(Change::Activate)) {
 | 
						|
		for (int i = 0; i < 4; ++i) {
 | 
						|
			SetWindowPos(_handles[i], _handle, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
 | 
						|
		}
 | 
						|
		return;
 | 
						|
	}
 | 
						|
 | 
						|
	if (changes & Change::Hidden) {
 | 
						|
		if (!_hidden) {
 | 
						|
			for (int i = 0; i < 4; ++i) {
 | 
						|
				_hidden = true;
 | 
						|
				ShowWindow(_handles[i], SW_HIDE);
 | 
						|
			}
 | 
						|
		}
 | 
						|
		return;
 | 
						|
	}
 | 
						|
	if (_window->isHidden()) {
 | 
						|
		return;
 | 
						|
	}
 | 
						|
 | 
						|
	int x = _x, y = _y, w = _w, h = _h;
 | 
						|
	if (pos && (!(pos->flags & SWP_NOMOVE) || !(pos->flags & SWP_NOSIZE) || !(pos->flags & SWP_NOREPOSITION))) {
 | 
						|
		if (!(pos->flags & SWP_NOMOVE)) {
 | 
						|
			x = pos->x - _size;
 | 
						|
			y = pos->y - _size;
 | 
						|
		} else if (pos->flags & SWP_NOSIZE) {
 | 
						|
			for (int i = 0; i < 4; ++i) {
 | 
						|
				SetWindowPos(_handles[i], _handle, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
 | 
						|
			}
 | 
						|
			return;
 | 
						|
		}
 | 
						|
		if (!(pos->flags & SWP_NOSIZE)) {
 | 
						|
			w = pos->cx + 2 * _size;
 | 
						|
			h = pos->cy + 2 * _size;
 | 
						|
		}
 | 
						|
	} else {
 | 
						|
		RECT r;
 | 
						|
		GetWindowRect(_handle, &r);
 | 
						|
		x = r.left - _size;
 | 
						|
		y = r.top - _size;
 | 
						|
		w = r.right + _size - x;
 | 
						|
		h = r.bottom + _size - y;
 | 
						|
	}
 | 
						|
	if (h < 2 * _fullsize + 2 * _shift) {
 | 
						|
		h = 2 * _fullsize + 2 * _shift;
 | 
						|
	}
 | 
						|
	if (w < 2 * (_fullsize + _shift)) {
 | 
						|
		w = 2 * (_fullsize + _shift);
 | 
						|
	}
 | 
						|
 | 
						|
	if (w != _w) {
 | 
						|
		int from = (_w > 2 * (_fullsize + _shift)) ? (_w - _size - _fullsize - _shift) : (_fullsize - (_size - _shift));
 | 
						|
		int to = w - _size - _fullsize - _shift;
 | 
						|
		if (w > _widthMax) {
 | 
						|
			from = _fullsize - (_size - _shift);
 | 
						|
			do {
 | 
						|
				_widthMax *= 2;
 | 
						|
			} while (w > _widthMax);
 | 
						|
			for (int i = 0; i < 4; i += 2) {
 | 
						|
				DeleteObject(_bitmaps[i]);
 | 
						|
				_bitmaps[i] = CreateCompatibleBitmap(_screenContext, _widthMax, _size);
 | 
						|
				SelectObject(_contexts[i], _bitmaps[i]);
 | 
						|
			}
 | 
						|
			initCorners(Direction::Horizontal);
 | 
						|
		}
 | 
						|
		Gdiplus::Graphics graphics0(_contexts[0]);
 | 
						|
		Gdiplus::Graphics graphics2(_contexts[2]);
 | 
						|
		graphics0.SetCompositingMode(Gdiplus::CompositingModeSourceCopy);
 | 
						|
		graphics2.SetCompositingMode(Gdiplus::CompositingModeSourceCopy);
 | 
						|
		auto brush = getBrush(_alphas[0]);
 | 
						|
		if (to > from) {
 | 
						|
			graphics0.FillRectangle(&brush, from, 0, to - from, 2 * _shift);
 | 
						|
			for (int i = 2 * _shift; i < _size; ++i) {
 | 
						|
				auto pen = getPen(_alphas[i]);
 | 
						|
				graphics0.DrawLine(&pen, from, i, to, i);
 | 
						|
				graphics2.DrawLine(&pen, from, _size - (i - 2 * _shift) - 1, to, _size - (i - 2 * _shift) - 1);
 | 
						|
			}
 | 
						|
			for (int i = _size; i < _size + 2 * _shift; ++i) {
 | 
						|
				auto pen = getPen(_alphas[i]);
 | 
						|
				graphics2.DrawLine(&pen, from, _size - (i - 2 * _shift) - 1, to, _size - (i - 2 * _shift) - 1);
 | 
						|
			}
 | 
						|
		}
 | 
						|
		if (_w > w) {
 | 
						|
			graphics0.FillRectangle(&brush, w - _size - _fullsize - _shift, 0, _fullsize - (_size - _shift), _size);
 | 
						|
			graphics2.FillRectangle(&brush, w - _size - _fullsize - _shift, 0, _fullsize - (_size - _shift), _size);
 | 
						|
		}
 | 
						|
		horCorners(w, &graphics0, &graphics2);
 | 
						|
		POINT p0 = { x + _size, y }, p2 = { x + _size, y + h - _size }, f = { 0, 0 };
 | 
						|
		SIZE s = { w - 2 * _size, _size };
 | 
						|
		updateWindow(0, &p0, &s);
 | 
						|
		updateWindow(2, &p2, &s);
 | 
						|
	} else if (x != _x || y != _y) {
 | 
						|
		POINT p0 = { x + _size, y }, p2 = { x + _size, y + h - _size };
 | 
						|
		updateWindow(0, &p0);
 | 
						|
		updateWindow(2, &p2);
 | 
						|
	} else if (h != _h) {
 | 
						|
		POINT p2 = { x + _size, y + h - _size };
 | 
						|
		updateWindow(2, &p2);
 | 
						|
	}
 | 
						|
 | 
						|
	if (h != _h) {
 | 
						|
		int from = (_h > 2 * _fullsize + 2 * _shift) ? (_h - _fullsize) : (_fullsize + 2 * _shift);
 | 
						|
		int to = h - _fullsize;
 | 
						|
		if (h > _heightMax) {
 | 
						|
			from = (_fullsize + 2 * _shift);
 | 
						|
			do {
 | 
						|
				_heightMax *= 2;
 | 
						|
			} while (h > _heightMax);
 | 
						|
			for (int i = 1; i < 4; i += 2) {
 | 
						|
				DeleteObject(_bitmaps[i]);
 | 
						|
				_bitmaps[i] = CreateCompatibleBitmap(_contexts[i], _size, _heightMax);
 | 
						|
				SelectObject(_contexts[i], _bitmaps[i]);
 | 
						|
			}
 | 
						|
			initCorners(Direction::Vertical);
 | 
						|
		}
 | 
						|
		Gdiplus::Graphics graphics1(_contexts[1]), graphics3(_contexts[3]);
 | 
						|
		graphics1.SetCompositingMode(Gdiplus::CompositingModeSourceCopy);
 | 
						|
		graphics3.SetCompositingMode(Gdiplus::CompositingModeSourceCopy);
 | 
						|
 | 
						|
		auto brush = getBrush(_alphas[0]);
 | 
						|
		if (to > from) {
 | 
						|
			graphics1.FillRectangle(&brush, _size - _shift, from, _shift, to - from);
 | 
						|
			graphics3.FillRectangle(&brush, 0, from, _shift, to - from);
 | 
						|
			for (int i = 2 * _shift; i < _size + _shift; ++i) {
 | 
						|
				auto pen = getPen(_alphas[i]);
 | 
						|
				graphics1.DrawLine(&pen, _size + _shift - i - 1, from, _size + _shift - i - 1, to);
 | 
						|
				graphics3.DrawLine(&pen, i - _shift, from, i - _shift, to);
 | 
						|
			}
 | 
						|
		}
 | 
						|
		if (_h > h) {
 | 
						|
			graphics1.FillRectangle(&brush, 0, h - _fullsize, _size, _fullsize);
 | 
						|
			graphics3.FillRectangle(&brush, 0, h - _fullsize, _size, _fullsize);
 | 
						|
		}
 | 
						|
		verCorners(h, &graphics1, &graphics3);
 | 
						|
 | 
						|
		POINT p1 = { x + w - _size, y }, p3 = { x, y }, f = { 0, 0 };
 | 
						|
		SIZE s = { _size, h };
 | 
						|
		updateWindow(1, &p1, &s);
 | 
						|
		updateWindow(3, &p3, &s);
 | 
						|
	} else if (x != _x || y != _y) {
 | 
						|
		POINT p1 = { x + w - _size, y }, p3 = { x, y };
 | 
						|
		updateWindow(1, &p1);
 | 
						|
		updateWindow(3, &p3);
 | 
						|
	} else if (w != _w) {
 | 
						|
		POINT p1 = { x + w - _size, y };
 | 
						|
		updateWindow(1, &p1);
 | 
						|
	}
 | 
						|
	_x = x;
 | 
						|
	_y = y;
 | 
						|
	_w = w;
 | 
						|
	_h = h;
 | 
						|
 | 
						|
	if (_hidden && (changes & Change::Shown)) {
 | 
						|
		for (int i = 0; i < 4; ++i) {
 | 
						|
			SetWindowPos(
 | 
						|
				_handles[i],
 | 
						|
				_handle,
 | 
						|
				0,
 | 
						|
				0,
 | 
						|
				0,
 | 
						|
				0,
 | 
						|
				SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW | SWP_NOACTIVATE);
 | 
						|
		}
 | 
						|
		_hidden = false;
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
void WindowShadow::initBlend() {
 | 
						|
	_blend.AlphaFormat = AC_SRC_ALPHA;
 | 
						|
	_blend.SourceConstantAlpha = 255;
 | 
						|
	_blend.BlendFlags = 0;
 | 
						|
	_blend.BlendOp = AC_SRC_OVER;
 | 
						|
}
 | 
						|
 | 
						|
void WindowShadow::updateWindow(int i, POINT *p, SIZE *s) {
 | 
						|
	static POINT f = { 0, 0 };
 | 
						|
	if (s) {
 | 
						|
		UpdateLayeredWindow(
 | 
						|
			_handles[i],
 | 
						|
			(s ? _screenContext : nullptr),
 | 
						|
			p,
 | 
						|
			s,
 | 
						|
			(s ? _contexts[i] : nullptr),
 | 
						|
			(s ? (&f) : nullptr),
 | 
						|
			_noKeyColor,
 | 
						|
			&_blend,
 | 
						|
			ULW_ALPHA);
 | 
						|
	} else {
 | 
						|
		SetWindowPos(
 | 
						|
			_handles[i],
 | 
						|
			0,
 | 
						|
			p->x,
 | 
						|
			p->y,
 | 
						|
			0,
 | 
						|
			0,
 | 
						|
			SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOZORDER);
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
void WindowShadow::setResizeEnabled(bool enabled) {
 | 
						|
	_resizeEnabled = enabled;
 | 
						|
}
 | 
						|
 | 
						|
LRESULT CALLBACK WindowShadow::WindowCallback(
 | 
						|
		HWND hwnd,
 | 
						|
		UINT msg,
 | 
						|
		WPARAM wParam,
 | 
						|
		LPARAM lParam) {
 | 
						|
	const auto i = ShadowByHandle.find(hwnd);
 | 
						|
	return (i != end(ShadowByHandle))
 | 
						|
		? i->second->windowCallback(hwnd, msg, wParam, lParam)
 | 
						|
		: DefWindowProc(hwnd, msg, wParam, lParam);
 | 
						|
}
 | 
						|
 | 
						|
LRESULT WindowShadow::windowCallback(
 | 
						|
		HWND hwnd,
 | 
						|
		UINT msg,
 | 
						|
		WPARAM wParam,
 | 
						|
		LPARAM lParam) {
 | 
						|
	if (!working()) {
 | 
						|
		return DefWindowProc(hwnd, msg, wParam, lParam);
 | 
						|
	}
 | 
						|
 | 
						|
	switch (msg) {
 | 
						|
	case WM_CLOSE:
 | 
						|
		_window->close();
 | 
						|
		return 0;
 | 
						|
 | 
						|
	case WM_NCHITTEST: {
 | 
						|
		if (!_resizeEnabled) {
 | 
						|
			return HTNOWHERE;
 | 
						|
		}
 | 
						|
		const auto xPos = GET_X_LPARAM(lParam);
 | 
						|
		const auto yPos = GET_Y_LPARAM(lParam);
 | 
						|
		if (hwnd == _handles[0]) {
 | 
						|
			return HTTOP;
 | 
						|
		} else if (hwnd == _handles[1]) {
 | 
						|
			return (yPos < _y + _size)
 | 
						|
				? HTTOPRIGHT
 | 
						|
				: (yPos >= _y + _h - _size)
 | 
						|
				? HTBOTTOMRIGHT
 | 
						|
				: HTRIGHT;
 | 
						|
		} else if (hwnd == _handles[2]) {
 | 
						|
			return HTBOTTOM;
 | 
						|
		} else if (hwnd == _handles[3]) {
 | 
						|
			return (yPos < _y + _size)
 | 
						|
				? HTTOPLEFT
 | 
						|
				: (yPos >= _y + _h - _size)
 | 
						|
				? HTBOTTOMLEFT
 | 
						|
				: HTLEFT;
 | 
						|
		} else {
 | 
						|
			Unexpected("Handle in WindowShadow::windowCallback.");
 | 
						|
		}
 | 
						|
	} break;
 | 
						|
 | 
						|
	case WM_NCACTIVATE:
 | 
						|
		return DefWindowProc(hwnd, msg, wParam, lParam);
 | 
						|
 | 
						|
	case WM_NCLBUTTONDOWN:
 | 
						|
	case WM_NCLBUTTONUP:
 | 
						|
	case WM_NCLBUTTONDBLCLK:
 | 
						|
	case WM_NCMBUTTONDOWN:
 | 
						|
	case WM_NCMBUTTONUP:
 | 
						|
	case WM_NCMBUTTONDBLCLK:
 | 
						|
	case WM_NCRBUTTONDOWN:
 | 
						|
	case WM_NCRBUTTONUP:
 | 
						|
	case WM_NCRBUTTONDBLCLK:
 | 
						|
	case WM_NCXBUTTONDOWN:
 | 
						|
	case WM_NCXBUTTONUP:
 | 
						|
	case WM_NCXBUTTONDBLCLK:
 | 
						|
	case WM_NCMOUSEHOVER:
 | 
						|
	case WM_NCMOUSELEAVE:
 | 
						|
	case WM_NCMOUSEMOVE:
 | 
						|
	case WM_NCPOINTERUPDATE:
 | 
						|
	case WM_NCPOINTERDOWN:
 | 
						|
	case WM_NCPOINTERUP:
 | 
						|
		if (msg == WM_NCLBUTTONDOWN) {
 | 
						|
			::SetForegroundWindow(_handle);
 | 
						|
		}
 | 
						|
		return SendMessage(_handle, msg, wParam, lParam);
 | 
						|
	case WM_ACTIVATE:
 | 
						|
		if (wParam == WA_ACTIVE) {
 | 
						|
			if ((HWND)lParam != _handle) {
 | 
						|
				::SetForegroundWindow(hwnd);
 | 
						|
				::SetWindowPos(
 | 
						|
					_handle,
 | 
						|
					hwnd,
 | 
						|
					0,
 | 
						|
					0,
 | 
						|
					0,
 | 
						|
					0,
 | 
						|
					SWP_NOMOVE | SWP_NOSIZE);
 | 
						|
			}
 | 
						|
		}
 | 
						|
		return DefWindowProc(hwnd, msg, wParam, lParam);
 | 
						|
	default:
 | 
						|
		return DefWindowProc(hwnd, msg, wParam, lParam);
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
} // namespace Platform
 | 
						|
} // namespace Ui
 |