mirror of
https://github.com/Mauler125/r5sdk.git
synced 2025-02-09 19:15:03 +01:00
cppkore uses string/wstring as StringBase while we use std::string/std::wstring as string/wstring. Changed all types in cppkore to String/WString instead.
778 lines
17 KiB
C++
778 lines
17 KiB
C++
#include "stdafx.h"
|
|
#include "ToolTip.h"
|
|
|
|
namespace Forms
|
|
{
|
|
ToolTip::ToolTip()
|
|
: Control(), _Flags(0x33), _ToolTipIcon(ToolTipIcon::None), _DelayTimes{}
|
|
{
|
|
#if 0
|
|
// This is used to debug the tooltip state flags, we use the magic number 0x33 right now...
|
|
_Flags.set(0, true); // Active
|
|
_Flags.set(1, true); // Auto
|
|
_Flags.set(2, false); // ShowAlways
|
|
_Flags.set(3, false); // StripAmpersands
|
|
_Flags.set(4, true); // UseAnimation
|
|
_Flags.set(5, true); // UseFading
|
|
_Flags.set(6, false); // OwnerDraw
|
|
_Flags.set(7, false); // IsBalloon
|
|
_Flags.set(8, false); // TrackPosition
|
|
_Flags.set(9, false); // Cancelled
|
|
#endif
|
|
|
|
// Set the automatic time default
|
|
_DelayTimes[TTDT_AUTOMATIC] = DEFAULT_DELAY;
|
|
// Adjust the base times from auto
|
|
AdjustBaseFromAuto();
|
|
|
|
// Default tooltip background and foreground
|
|
this->SetBackColor(Drawing::GetSystemColor(Drawing::SystemColors::Info));
|
|
this->SetForeColor(Drawing::GetSystemColor(Drawing::SystemColors::InfoText));
|
|
|
|
// We are a tooltip control
|
|
this->_RTTI = ControlTypes::ToolTip;
|
|
}
|
|
|
|
void ToolTip::CreateControl(Control* Parent)
|
|
{
|
|
INITCOMMONCONTROLSEX iCC{};
|
|
iCC.dwSize = sizeof(iCC);
|
|
iCC.dwICC = ICC_TAB_CLASSES;
|
|
|
|
InitCommonControlsEx(&iCC);
|
|
|
|
Control::CreateControl(Parent);
|
|
}
|
|
|
|
bool ToolTip::Active()
|
|
{
|
|
return this->_Flags[0];
|
|
}
|
|
|
|
void ToolTip::SetActive(bool Value)
|
|
{
|
|
if (Active() != Value)
|
|
{
|
|
this->_Flags[0] = Value;
|
|
|
|
if (GetState(ControlStates::StateCreated))
|
|
SendMessageA(this->_Handle, TTM_ACTIVATE, (Value) ? 1 : 0, NULL);
|
|
}
|
|
}
|
|
|
|
uint32_t ToolTip::AutomaticDelay()
|
|
{
|
|
return _DelayTimes[TTDT_AUTOMATIC];
|
|
}
|
|
|
|
void ToolTip::SetAutomaticDelay(uint32_t Value)
|
|
{
|
|
SetDelayTime(TTDT_AUTOMATIC, Value);
|
|
}
|
|
|
|
uint32_t ToolTip::AutoPopDelay()
|
|
{
|
|
return _DelayTimes[TTDT_AUTOPOP];
|
|
}
|
|
|
|
void ToolTip::SetAutoPopDelay(uint32_t Value)
|
|
{
|
|
SetDelayTime(TTDT_AUTOPOP, Value);
|
|
}
|
|
|
|
bool ToolTip::IsBalloon()
|
|
{
|
|
return this->_Flags[7];
|
|
}
|
|
|
|
void ToolTip::SetIsBalloon(bool Value)
|
|
{
|
|
if (this->_Flags[7] != Value)
|
|
{
|
|
this->_Flags[7] = Value;
|
|
UpdateStyles();
|
|
}
|
|
}
|
|
|
|
uint32_t ToolTip::InitialDelay()
|
|
{
|
|
return _DelayTimes[TTDT_INITIAL];
|
|
}
|
|
|
|
void ToolTip::SetInitialDelay(uint32_t Value)
|
|
{
|
|
SetDelayTime(TTDT_INITIAL, Value);
|
|
}
|
|
|
|
bool ToolTip::OwnerDraw()
|
|
{
|
|
return this->_Flags[6];
|
|
}
|
|
|
|
void ToolTip::SetOwnerDraw(bool Value)
|
|
{
|
|
this->_Flags[6] = Value;
|
|
}
|
|
|
|
uint32_t ToolTip::ReshowDelay()
|
|
{
|
|
return _DelayTimes[TTDT_RESHOW];
|
|
}
|
|
|
|
void ToolTip::SetReshowDelay(uint32_t Value)
|
|
{
|
|
SetDelayTime(TTDT_RESHOW, Value);
|
|
}
|
|
|
|
bool ToolTip::ShowAlways()
|
|
{
|
|
return this->_Flags[2];
|
|
}
|
|
|
|
void ToolTip::SetShowAlways(bool Value)
|
|
{
|
|
if (this->_Flags[2] != Value)
|
|
{
|
|
this->_Flags[2] = Value;
|
|
UpdateStyles();
|
|
}
|
|
}
|
|
|
|
bool ToolTip::StripAmpersands()
|
|
{
|
|
return this->_Flags[3];
|
|
}
|
|
|
|
void ToolTip::SetStripAmpersands(bool Value)
|
|
{
|
|
if (this->_Flags[3] != Value)
|
|
{
|
|
this->_Flags[3] = Value;
|
|
UpdateStyles();
|
|
}
|
|
}
|
|
|
|
ToolTipIcon ToolTip::GetToolTipIcon()
|
|
{
|
|
return this->_ToolTipIcon;
|
|
}
|
|
|
|
void ToolTip::SetToolTipIcon(ToolTipIcon Value)
|
|
{
|
|
if (_ToolTipIcon != Value)
|
|
{
|
|
_ToolTipIcon = Value;
|
|
|
|
if (GetState(ControlStates::StateCreated))
|
|
{
|
|
String Title = !String::IsNullOrEmpty(_ToolTipTitle) ? _ToolTipTitle : String(" ");
|
|
|
|
SendMessageA(this->_Handle, TTM_SETTITLEA, (WPARAM)Value, (LPARAM)(char*)Title);
|
|
SendMessageA(this->_Handle, TTM_UPDATE, NULL, NULL);
|
|
}
|
|
}
|
|
}
|
|
|
|
String ToolTip::ToolTipTitle()
|
|
{
|
|
return this->_ToolTipTitle;
|
|
}
|
|
|
|
void ToolTip::SetToolTipTitle(const String& Value)
|
|
{
|
|
if (_ToolTipTitle != Value)
|
|
{
|
|
_ToolTipTitle = Value;
|
|
|
|
if (GetState(ControlStates::StateCreated))
|
|
{
|
|
SendMessageA(this->_Handle, TTM_SETTITLEA, (WPARAM)_ToolTipIcon, (LPARAM)(char*)_ToolTipTitle);
|
|
SendMessageA(this->_Handle, TTM_UPDATE, NULL, NULL);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool ToolTip::UseAnimation()
|
|
{
|
|
return this->_Flags[4];
|
|
}
|
|
|
|
void ToolTip::SetUseAnimation(bool Value)
|
|
{
|
|
if (this->_Flags[4] != Value)
|
|
{
|
|
this->_Flags[4] = Value;
|
|
UpdateStyles();
|
|
}
|
|
}
|
|
|
|
bool ToolTip::UseFading()
|
|
{
|
|
return this->_Flags[5];
|
|
}
|
|
|
|
void ToolTip::SetUseFading(bool Value)
|
|
{
|
|
if (this->_Flags[5] != Value)
|
|
{
|
|
this->_Flags[5] = Value;
|
|
UpdateStyles();
|
|
}
|
|
}
|
|
|
|
void ToolTip::SetToolTip(Control* Ctrl, const String& Caption)
|
|
{
|
|
if (Ctrl->GetState(ControlStates::StateCreated))
|
|
{
|
|
// Check if we already setup the control...
|
|
if (this->_ControlCache.ContainsKey((uintptr_t)Ctrl))
|
|
{
|
|
TOOLINFOA Ti{};
|
|
Ti.cbSize = sizeof(Ti);
|
|
Ti.hwnd = Ctrl->GetHandle();
|
|
Ti.uFlags = TTF_IDISHWND | TTF_TRANSPARENT | TTF_SUBCLASS;
|
|
Ti.uId = (UINT_PTR)Ctrl->GetHandle();
|
|
Ti.lpszText = (char*)Caption;
|
|
|
|
SendMessageA(this->_Handle, TTM_UPDATETIPTEXTA, NULL, (LPARAM)&Ti);
|
|
|
|
auto Ptr = (uintptr_t)Ctrl;
|
|
this->_ControlCache[Ptr] = Caption;
|
|
|
|
return;
|
|
}
|
|
|
|
// Add the item to the control cache
|
|
this->_ControlCache.Add((uintptr_t)Ctrl, Caption);
|
|
// Register the tooltip handler
|
|
this->RegisterTooltip(Ctrl);
|
|
|
|
// Hook if control gets deleted
|
|
Ctrl->HandleDestroyed += &ToolTip::ControlHandleDestroyed;
|
|
}
|
|
else
|
|
{
|
|
// Add the item and wait for creation
|
|
this->_ControlCache.Add((uintptr_t)Ctrl, Caption);
|
|
Ctrl->HandleCreated += &ToolTip::ControlHandleCreated;
|
|
}
|
|
}
|
|
|
|
void ToolTip::OnHandleCreated()
|
|
{
|
|
Control::OnHandleCreated();
|
|
|
|
if (OwnerDraw())
|
|
{
|
|
auto Style = (int)GetWindowLong(this->_Handle, GWL_STYLE);
|
|
Style &= WS_BORDER;
|
|
SetWindowLong(this->_Handle, GWL_STYLE, Style);
|
|
}
|
|
|
|
SendMessageA(this->_Handle, TTM_SETMAXTIPWIDTH, NULL, GetSystemMetrics(SM_CXMAXTRACK));
|
|
|
|
if (this->_Flags[1])
|
|
{
|
|
SetDelayTime(TTDT_AUTOMATIC, _DelayTimes[TTDT_AUTOMATIC]);
|
|
_DelayTimes[TTDT_AUTOPOP] = GetDelayTime(TTDT_AUTOPOP);
|
|
_DelayTimes[TTDT_INITIAL] = GetDelayTime(TTDT_INITIAL);
|
|
_DelayTimes[TTDT_RESHOW] = GetDelayTime(TTDT_RESHOW);
|
|
}
|
|
else
|
|
{
|
|
for (int32_t i = 0; i < 4; i++)
|
|
{
|
|
if (_DelayTimes[i] > 1)
|
|
SetDelayTime(i, _DelayTimes[i]);
|
|
}
|
|
}
|
|
|
|
SendMessageA(this->_Handle, TTM_ACTIVATE, (Active() == true) ? 1 : 0, NULL);
|
|
|
|
SendMessageA(this->_Handle, TTM_SETTIPBKCOLOR, Drawing::ColorToWin32(this->BackColor()), NULL);
|
|
SendMessageA(this->_Handle, TTM_SETTIPTEXTCOLOR, Drawing::ColorToWin32(this->ForeColor()), NULL);
|
|
|
|
if ((int)_ToolTipIcon > 0 || !String::IsNullOrEmpty(_ToolTipTitle))
|
|
{
|
|
String Title = !String::IsNullOrEmpty(_ToolTipTitle) ? _ToolTipTitle : String(" ");
|
|
SendMessageA(this->_Handle, TTM_SETTITLEA, (WPARAM)_ToolTipIcon, (LPARAM)(char*)Title);
|
|
}
|
|
}
|
|
|
|
void ToolTip::OnBackColorChanged()
|
|
{
|
|
if (GetState(ControlStates::StateCreated))
|
|
SendMessageA(this->_Handle, TTM_SETTIPBKCOLOR, Drawing::ColorToWin32(this->BackColor()), NULL);
|
|
|
|
Control::OnBackColorChanged();
|
|
}
|
|
|
|
void ToolTip::OnForeColorChanged()
|
|
{
|
|
if (GetState(ControlStates::StateCreated))
|
|
SendMessageA(this->_Handle, TTM_SETTIPTEXTCOLOR, Drawing::ColorToWin32(this->ForeColor()), NULL);
|
|
|
|
Control::OnForeColorChanged();
|
|
}
|
|
|
|
void ToolTip::OnPopup(const std::unique_ptr<PopupEventArgs>& EventArgs)
|
|
{
|
|
Popup.RaiseEvent(EventArgs, this);
|
|
}
|
|
|
|
void ToolTip::OnDraw(const std::unique_ptr<DrawToolTipEventArgs>& EventArgs)
|
|
{
|
|
Draw.RaiseEvent(EventArgs, this);
|
|
}
|
|
|
|
void ToolTip::RemoveAll()
|
|
{
|
|
for (auto& Kvp : this->_ControlCache)
|
|
{
|
|
auto Ctrl = (Control*)Kvp.Key();
|
|
|
|
if (GetState(ControlStates::StateCreated) && Ctrl->GetState(ControlStates::StateCreated))
|
|
{
|
|
TOOLINFOA Ti{};
|
|
Ti.cbSize = sizeof(Ti);
|
|
Ti.hwnd = Ctrl->GetHandle();
|
|
Ti.uFlags = TTF_IDISHWND | TTF_TRANSPARENT | TTF_SUBCLASS;
|
|
Ti.uId = (UINT_PTR)Ctrl->GetHandle();
|
|
|
|
SendMessageA(this->_Handle, TTM_DELTOOLA, NULL, (LPARAM)& Ti);
|
|
}
|
|
}
|
|
|
|
this->_ControlCache.Clear();
|
|
}
|
|
|
|
void ToolTip::Remove(Control* Ctrl)
|
|
{
|
|
if (GetState(ControlStates::StateCreated) && Ctrl->GetState(ControlStates::StateCreated))
|
|
{
|
|
TOOLINFOA Ti{};
|
|
Ti.cbSize = sizeof(Ti);
|
|
Ti.hwnd = Ctrl->GetHandle();
|
|
Ti.uFlags = TTF_IDISHWND | TTF_TRANSPARENT | TTF_SUBCLASS;
|
|
Ti.uId = (UINT_PTR)Ctrl->GetHandle();
|
|
|
|
SendMessageA(this->_Handle, TTM_DELTOOLA, NULL, (LPARAM)&Ti);
|
|
}
|
|
|
|
this->_ControlCache.Remove((uintptr_t)Ctrl);
|
|
}
|
|
|
|
void ToolTip::WndProc(Message& Msg)
|
|
{
|
|
switch (Msg.Msg)
|
|
{
|
|
case WM_REFLECT + WM_NOTIFY:
|
|
{
|
|
NMHDR* nMHDR = (NMHDR*)Msg.LParam;
|
|
if (nMHDR->code == TTN_SHOW && !this->_Flags[8])
|
|
{
|
|
WmShow();
|
|
}
|
|
else if (nMHDR->code == TTN_POP)
|
|
{
|
|
WmPop();
|
|
DefWndProc(Msg);
|
|
}
|
|
}
|
|
break;
|
|
case WM_WINDOWPOSCHANGING:
|
|
DefWndProc(Msg);
|
|
break;
|
|
case WM_WINDOWPOSCHANGED:
|
|
if (!WmWindowPosChanged())
|
|
DefWndProc(Msg);
|
|
break;
|
|
case WM_MOVE:
|
|
WmMove();
|
|
break;
|
|
case TTM_WINDOWFROMPOINT:
|
|
WmWindowFromPoint(Msg);
|
|
break;
|
|
case WM_PRINTCLIENT:
|
|
case WM_PAINT:
|
|
if (OwnerDraw() && !IsBalloon() && !this->_Flags[8])
|
|
{
|
|
PAINTSTRUCT Ps{};
|
|
auto Dc = BeginPaint(this->_Handle, &Ps);
|
|
|
|
Drawing::Rectangle Bounds(Ps.rcPaint.left, Ps.rcPaint.top, Ps.rcPaint.right - Ps.rcPaint.left, Ps.rcPaint.bottom - Ps.rcPaint.top);
|
|
|
|
if (Bounds.IsEmptyArea())
|
|
{
|
|
EndPaint(this->_Handle, &Ps);
|
|
return;
|
|
}
|
|
|
|
TOOLINFOA Ti{};
|
|
Ti.cbSize = sizeof(Ti);
|
|
|
|
auto Result = (int32_t)SendMessageA(this->_Handle, TTM_GETCURRENTTOOLA, NULL, (LPARAM)&Ti);
|
|
|
|
if (Result != 0)
|
|
{
|
|
auto Font = this->GetFont();
|
|
auto Ctrl = Control::FromHandle(Ti.hwnd);
|
|
auto CtrlPtr = (uintptr_t)Ctrl;
|
|
|
|
auto& Value = this->_ControlCache[CtrlPtr];
|
|
|
|
OnDraw(std::make_unique<DrawToolTipEventArgs>(Dc, Ctrl, Ctrl, Bounds, Value, BackColor(), ForeColor(), Font));
|
|
}
|
|
|
|
EndPaint(this->_Handle, &Ps);
|
|
}
|
|
else
|
|
{
|
|
DefWndProc(Msg);
|
|
}
|
|
break;
|
|
default:
|
|
DefWndProc(Msg);
|
|
break;
|
|
}
|
|
}
|
|
|
|
CreateParams ToolTip::GetCreateParams()
|
|
{
|
|
CreateParams Cp{};
|
|
Cp.ClassName = "tooltips_class32";
|
|
|
|
if (ShowAlways())
|
|
Cp.Style = TTS_ALWAYSTIP;
|
|
if (IsBalloon())
|
|
Cp.Style |= TTS_BALLOON;
|
|
if (!StripAmpersands())
|
|
Cp.Style |= TTS_NOPREFIX;
|
|
if (!UseAnimation())
|
|
Cp.Style |= TTS_NOANIMATE;
|
|
if (!UseFading())
|
|
Cp.Style |= TTS_NOFADE;
|
|
|
|
return Cp;
|
|
}
|
|
|
|
void ToolTip::RegisterTooltip(Control* Ctrl)
|
|
{
|
|
auto Ptr = (uintptr_t)Ctrl;
|
|
auto Value = this->_ControlCache[Ptr];
|
|
|
|
TOOLINFOA Ti{};
|
|
Ti.cbSize = sizeof(Ti);
|
|
Ti.hwnd = Ctrl->GetHandle();
|
|
Ti.uFlags = TTF_IDISHWND | TTF_TRANSPARENT | TTF_SUBCLASS;
|
|
Ti.uId = (UINT_PTR)Ctrl->GetHandle();
|
|
Ti.lpszText = (char*)Value;
|
|
|
|
SendMessageA(this->_Handle, TTM_ADDTOOLA, NULL, (LPARAM)&Ti);
|
|
|
|
Ctrl->HandleDestroyed += &ToolTip::ControlHandleDestroyed;
|
|
}
|
|
|
|
void ToolTip::ControlHandleCreated(Control* Sender)
|
|
{
|
|
Sender->HandleCreated -= &ToolTip::ControlHandleCreated;
|
|
|
|
// The tooltip should be a child of the form of the sender
|
|
auto FormOwner = Sender->FindForm();
|
|
if (FormOwner == nullptr)
|
|
return;
|
|
|
|
auto& ControlList = *FormOwner->Controls().get();
|
|
|
|
for (auto& Control : ControlList)
|
|
{
|
|
if (Control.GetType() == ControlTypes::ToolTip)
|
|
{
|
|
auto Tt = (ToolTip*)&Control;
|
|
|
|
if (Tt->_ControlCache.ContainsKey((uintptr_t)Sender))
|
|
{
|
|
Tt->RegisterTooltip(Sender);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void ToolTip::ControlHandleDestroyed(Control* Sender)
|
|
{
|
|
Sender->HandleDestroyed -= &ToolTip::ControlHandleDestroyed;
|
|
|
|
// The tooltip should be a child of the form of the sender
|
|
auto FormOwner = Sender->FindForm();
|
|
if (FormOwner == nullptr || FormOwner->GetState(ControlStates::StateDisposed))
|
|
return;
|
|
|
|
auto& ControlList = *FormOwner->Controls().get();
|
|
|
|
for (auto& Control : ControlList)
|
|
{
|
|
if (Control.GetType() == ControlTypes::ToolTip)
|
|
{
|
|
auto Tt = (ToolTip*)&Control;
|
|
|
|
if (Tt->GetState(ControlStates::StateCreated) && Tt->_ControlCache.ContainsKey((uintptr_t)Sender))
|
|
{
|
|
Tt->Remove(Sender);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void ToolTip::WmShow()
|
|
{
|
|
RECT R{};
|
|
GetWindowRect(this->_Handle, &R);
|
|
|
|
TOOLINFOA Ti{};
|
|
Ti.cbSize = sizeof(Ti);
|
|
|
|
auto Result = (int32_t)SendMessageA(this->_Handle, TTM_GETCURRENTTOOLA, NULL, (LPARAM)&Ti);
|
|
|
|
if (Result != NULL)
|
|
{
|
|
Drawing::Rectangle Rc(R.left, R.top, R.right - R.left, R.bottom - R.top);
|
|
Drawing::Size CurrentToolTipSize;
|
|
|
|
Rc.GetSize(&CurrentToolTipSize);
|
|
|
|
auto Ctrl = Control::FromHandle(Ti.hwnd);
|
|
|
|
if (Ctrl == nullptr)
|
|
return;
|
|
|
|
auto EventArgs = std::make_unique<PopupEventArgs>(Ctrl, Ctrl, IsBalloon(), CurrentToolTipSize);
|
|
OnPopup(EventArgs);
|
|
|
|
GetWindowRect(this->_Handle, &R);
|
|
if (EventArgs->ToolTipSize.Equals(CurrentToolTipSize))
|
|
Rc.GetSize(&CurrentToolTipSize);
|
|
else
|
|
CurrentToolTipSize = EventArgs->ToolTipSize;
|
|
|
|
if (IsBalloon())
|
|
{
|
|
SendMessageA(this->_Handle, TTM_ADJUSTRECT, (WPARAM)1, (LPARAM)&R);
|
|
|
|
if ((R.bottom - R.top) > CurrentToolTipSize.Height)
|
|
CurrentToolTipSize.Height = (R.bottom - R.top);
|
|
}
|
|
|
|
if (!CurrentToolTipSize.Equals(Drawing::Size(R.right - R.left, R.bottom - R.top)))
|
|
{
|
|
auto CursorPos = Control::GetMousePosition();
|
|
HMONITOR MonHandle = MonitorFromPoint({ CursorPos.X, CursorPos.Y }, MONITOR_DEFAULTTONEAREST);
|
|
|
|
if (MonHandle != nullptr)
|
|
{
|
|
MONITORINFO Info;
|
|
Info.cbSize = sizeof(Info);
|
|
GetMonitorInfo(MonHandle, &Info);
|
|
|
|
int32_t MaxWidth = 0;
|
|
|
|
if (IsBalloon())
|
|
MaxWidth = min(CurrentToolTipSize.Width - 2 * (int32_t)XBALLOONOFFSET, (Info.rcWork.right - Info.rcWork.left));
|
|
else
|
|
MaxWidth = min(CurrentToolTipSize.Width, (Info.rcWork.right - Info.rcWork.left));
|
|
|
|
SendMessageA(this->_Handle, TTM_SETMAXTIPWIDTH, NULL, (LPARAM)MaxWidth);
|
|
}
|
|
}
|
|
|
|
if (EventArgs->Cancel)
|
|
{
|
|
this->_Flags[9] = true;
|
|
SetWindowPos(this->_Handle, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOOWNERZORDER);
|
|
}
|
|
else
|
|
{
|
|
this->_Flags[9] = false;
|
|
SetWindowPos(this->_Handle, HWND_TOPMOST, R.left, R.top, CurrentToolTipSize.Width, CurrentToolTipSize.Height, SWP_NOACTIVATE | SWP_NOOWNERZORDER);
|
|
}
|
|
}
|
|
}
|
|
|
|
void ToolTip::WmPop()
|
|
{
|
|
TOOLINFOA Ti{};
|
|
Ti.cbSize = sizeof(Ti);
|
|
|
|
auto Result = (int32_t)SendMessageA(this->_Handle, TTM_GETCURRENTTOOLA, NULL, (LPARAM)&Ti);
|
|
|
|
if (Result != 0)
|
|
{
|
|
auto Ctrl = Control::FromHandle(Ti.hwnd);
|
|
if (Ctrl == nullptr)
|
|
return;
|
|
|
|
// This should only happen on Type::Auto / Type::SemiAbsolute
|
|
|
|
auto CursorPos = Control::GetMousePosition();
|
|
HMONITOR MonHandle = MonitorFromPoint({ CursorPos.X, CursorPos.Y }, MONITOR_DEFAULTTONEAREST);
|
|
|
|
if (MonHandle != nullptr)
|
|
{
|
|
MONITORINFO Info;
|
|
Info.cbSize = sizeof(Info);
|
|
GetMonitorInfo(MonHandle, &Info);
|
|
|
|
SendMessageA(this->_Handle, TTM_SETMAXTIPWIDTH, NULL, (Info.rcWork.right - Info.rcWork.left));
|
|
}
|
|
|
|
// TODO: If we support single-shot tooltips, remove them here...
|
|
}
|
|
}
|
|
|
|
void ToolTip::WmMove()
|
|
{
|
|
RECT R{};
|
|
GetWindowRect(this->_Handle, &R);
|
|
|
|
TOOLINFOA Ti{};
|
|
Ti.cbSize = sizeof(Ti);
|
|
|
|
auto Result = (int32_t)SendMessageA(this->_Handle, TTM_GETCURRENTTOOLA, NULL, (LPARAM)&Ti);
|
|
|
|
if (Result != 0)
|
|
{
|
|
auto Win = Control::FromHandle(Ti.hwnd);
|
|
|
|
if (Win == nullptr)
|
|
return;
|
|
|
|
// TODO: If Info != Point::Empty:
|
|
// TODO: Reposition the control...
|
|
}
|
|
}
|
|
|
|
bool ToolTip::WmWindowPosChanged()
|
|
{
|
|
if (this->_Flags[9])
|
|
{
|
|
SendMessageA(this->_Handle, SW_HIDE, NULL, NULL);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void ToolTip::WmWindowFromPoint(Message& Msg)
|
|
{
|
|
POINT* Pt = (POINT*)Msg.LParam;
|
|
Drawing::Point ScreenCoords(Pt->x, Pt->y);
|
|
bool Result = false;
|
|
Msg.Result = GetWindowFromPoint(ScreenCoords, Result);
|
|
}
|
|
|
|
void ToolTip::AdjustBaseFromAuto()
|
|
{
|
|
_DelayTimes[TTDT_RESHOW] = _DelayTimes[TTDT_AUTOMATIC] / RESHOW_RATIO;
|
|
_DelayTimes[TTDT_AUTOPOP] = _DelayTimes[TTDT_AUTOMATIC] / AUTOPOP_RATIO;
|
|
_DelayTimes[TTDT_INITIAL] = _DelayTimes[TTDT_AUTOMATIC];
|
|
}
|
|
|
|
void ToolTip::SetDelayTime(uint32_t Type, uint32_t Time)
|
|
{
|
|
if (Type == TTDT_AUTOMATIC)
|
|
this->_Flags[1] = true;
|
|
else
|
|
this->_Flags[1] = false;
|
|
|
|
_DelayTimes[Type] = Time;
|
|
|
|
if (GetState(ControlStates::StateCreated) && Time >= 0)
|
|
{
|
|
SendMessageA(this->_Handle, TTM_SETDELAYTIME, (WPARAM)Type, (LPARAM)Time);
|
|
|
|
if (this->_Flags[1])
|
|
{
|
|
_DelayTimes[TTDT_AUTOPOP] = GetDelayTime(TTDT_AUTOPOP);
|
|
_DelayTimes[TTDT_INITIAL] = GetDelayTime(TTDT_INITIAL);
|
|
_DelayTimes[TTDT_RESHOW] = GetDelayTime(TTDT_RESHOW);
|
|
}
|
|
}
|
|
else if (this->_Flags[1])
|
|
{
|
|
AdjustBaseFromAuto();
|
|
}
|
|
}
|
|
|
|
uint32_t ToolTip::GetDelayTime(uint32_t Type)
|
|
{
|
|
if (GetState(ControlStates::StateCreated))
|
|
return (uint32_t)SendMessageA(this->_Handle, TTM_GETDELAYTIME, (WPARAM)Type, NULL);
|
|
else
|
|
return _DelayTimes[Type];
|
|
}
|
|
|
|
uintptr_t ToolTip::GetWindowFromPoint(Drawing::Point ScreenCoords, bool& Success)
|
|
{
|
|
HWND BaseHwnd = NULL;
|
|
Control* BaseVar = this->FindForm();
|
|
|
|
if (BaseVar != nullptr)
|
|
BaseHwnd = BaseVar->GetHandle();
|
|
|
|
HWND hWnd = NULL;
|
|
bool FinalMatch = false;
|
|
|
|
while (!FinalMatch)
|
|
{
|
|
auto Pt = ScreenCoords;
|
|
if (BaseVar != nullptr)
|
|
Pt = BaseVar->PointToClient(ScreenCoords);
|
|
|
|
auto Found = ChildWindowFromPointEx(BaseHwnd, { Pt.X, Pt.Y }, CWP_SKIPINVISIBLE);
|
|
|
|
if (Found == BaseHwnd)
|
|
{
|
|
hWnd = Found;
|
|
FinalMatch = true;
|
|
}
|
|
else if (Found == NULL)
|
|
{
|
|
FinalMatch = true;
|
|
}
|
|
else
|
|
{
|
|
BaseVar = Control::FromHandle(Found);
|
|
if (BaseVar == nullptr)
|
|
{
|
|
BaseVar = Control::FromChildHandle(Found);
|
|
if (BaseVar != nullptr)
|
|
hWnd = BaseVar->GetHandle();
|
|
|
|
FinalMatch = true;
|
|
}
|
|
else
|
|
BaseHwnd = BaseVar->GetHandle();
|
|
}
|
|
}
|
|
|
|
if (hWnd != NULL)
|
|
{
|
|
auto Ctrl = Control::FromHandle(hWnd);
|
|
if (Ctrl != nullptr)
|
|
{
|
|
Control* Current = Ctrl;
|
|
while (Current != nullptr && Current->Visible())
|
|
Current = Current->Parent();
|
|
|
|
if (Current != nullptr)
|
|
hWnd = NULL;
|
|
|
|
Success = true;
|
|
}
|
|
}
|
|
|
|
return (uintptr_t)hWnd;
|
|
}
|
|
}
|