r5sdk/r5dev/thirdparty/cppnet/cppkore/ContainerControl.cpp
2022-05-21 19:58:09 +02:00

453 lines
10 KiB
C++

#include "stdafx.h"
#include "ContainerControl.h"
#include "Form.h"
namespace Forms
{
ContainerControl::ContainerControl()
: Control(), _ActiveControl(nullptr), _FocusedControl(nullptr), _AutoScaleDimensions{}, _CurrentAutoScaleDimensions{}, _AutoScaleMode(AutoScaleMode::Inherit), _ScalingNeededOnLayout(false)
{
SetStyle(ControlStyles::AllPaintingInWmPaint, false);
}
Control* ContainerControl::ActiveControl()
{
return this->_ActiveControl;
}
void ContainerControl::SetActiveControl(Control* Value)
{
SetActiveControlInternal(Value);
}
bool ContainerControl::ActivateControl(Control* Value)
{
return ActivateControlInternal(Value, true);
}
Drawing::SizeF ContainerControl::AutoScaleDimensions()
{
return this->_AutoScaleDimensions;
}
void ContainerControl::SetAutoScaleDimensions(Drawing::SizeF Size)
{
this->_AutoScaleDimensions = Size;
if (!this->_AutoScaleDimensions.Empty())
{
this->LayoutScalingNeeded();
}
}
Drawing::SizeF ContainerControl::CurrentAutoScaleDimensions()
{
if (this->_CurrentAutoScaleDimensions.Empty())
{
switch (this->_AutoScaleMode)
{
case AutoScaleMode::Font:
this->_CurrentAutoScaleDimensions = this->GetFontAutoScaleDimensions();
break;
case AutoScaleMode::Dpi:
// TODO: Handle dpi related scaling values...
break;
default:
this->_CurrentAutoScaleDimensions = this->AutoScaleDimensions();
break;
}
}
return this->_CurrentAutoScaleDimensions;
}
AutoScaleMode ContainerControl::AutoScaleMode()
{
return this->_AutoScaleMode;
}
void ContainerControl::SetAutoScaleMode(Forms::AutoScaleMode Mode)
{
bool ScalingRequired = false;
if (Mode != this->_AutoScaleMode)
{
if (_AutoScaleMode != Forms::AutoScaleMode::Inherit)
{
this->_AutoScaleDimensions = Drawing::SizeF{};
}
this->_AutoScaleMode = Mode;
ScalingRequired = true;
}
// TODO: OnAutoScaleModeChanged();
if (ScalingRequired)
this->LayoutScalingNeeded();
}
void ContainerControl::WndProc(Message& Msg)
{
switch (Msg.Msg)
{
case WM_SETFOCUS:
WmSetFocus(Msg);
break;
default:
Control::WndProc(Msg);
break;
}
}
CreateParams ContainerControl::GetCreateParams()
{
auto Cp = Control::GetCreateParams();
Cp.ExStyle |= WS_EX_CONTROLPARENT;
return Cp;
}
bool ContainerControl::IsContainerControl()
{
return true;
}
void ContainerControl::WmSetFocus(Message& Msg)
{
if (_ActiveControl != nullptr)
{
if (!_ActiveControl->Visible())
this->OnGotFocus();
FocusActiveControlInternal();
}
else
{
if (_Parent != nullptr)
{
ContainerControl* C = (ContainerControl*)_Parent->GetContainerControl();
if (C != nullptr)
{
if (!C->ActivateControlInternal(this, true))
return;
}
}
Control::WndProc(Msg);
}
}
void ContainerControl::LayoutScalingNeeded()
{
this->EnableRequiredScaling(this, true);
this->_ScalingNeededOnLayout = true;
}
void ContainerControl::EnableRequiredScaling(Control *Ctrl, bool Enable)
{
Ctrl->SetRequiredScalingEnabled(Enable);
if (!Ctrl->GetStyle(ControlStyles::ContainerControl) || Ctrl->Controls() == nullptr)
return;
uint32_t ControlCount = Ctrl->Controls()->Count();
auto& ControlList = *Ctrl->Controls().get();
for (uint32_t i = 0; i < ControlCount; i++)
{
EnableRequiredScaling(ControlList[i], Enable);
}
}
void ContainerControl::PerformNeededAutoScaleOnLayout()
{
if (this->_ScalingNeededOnLayout)
{
this->PerformAutoScale(this->_ScalingNeededOnLayout, false);
}
}
void ContainerControl::PerformAutoScale(bool IncludeBounds, bool ExcludeBounds)
{
bool Suspended = false;
if (this->_AutoScaleMode != AutoScaleMode::None && this->_AutoScaleMode != AutoScaleMode::Inherit)
{
// TODO: SuspendAllLayout(this)
Suspended = true;
Drawing::SizeF Included{};
Drawing::SizeF Excluded{};
if (IncludeBounds)
Included = this->AutoScaleFactor();
if (ExcludeBounds)
Excluded = this->AutoScaleFactor();
this->Scale(Included, Excluded, this);
this->_AutoScaleDimensions = this->CurrentAutoScaleDimensions();
}
if (IncludeBounds)
{
this->_ScalingNeededOnLayout = false;
this->EnableRequiredScaling(this, false);
}
if (Suspended)
{
// TODO: ResumeAllLayout(this, false);
}
}
Drawing::SizeF ContainerControl::GetFontAutoScaleDimensions()
{
Drawing::SizeF Result{};
auto hDC = CreateCompatibleDC(nullptr);
auto CurrentFont = this->GetFont();
auto OldFont = SelectObject(hDC, CurrentFont->GetFontHandle());
TEXTMETRICA Tm{};
GetTextMetricsA(hDC, &Tm);
Result.Height = (float)Tm.tmHeight;
if ((Tm.tmPitchAndFamily & TMPF_FIXED_PITCH) != 0)
{
constexpr const char* FontMeasureString = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
const uint32_t FontMeasureSize = (uint32_t)strlen(FontMeasureString);
SIZE Sz{};
GetTextExtentPoint32A(hDC, FontMeasureString, FontMeasureSize, &Sz);
Result.Width = (float)(int)std::roundf(((float)Sz.cx) / ((float)FontMeasureSize));
}
else
{
Result.Width = (float)Tm.tmAveCharWidth;
}
SelectObject(hDC, OldFont);
DeleteDC(hDC);
return Result;
}
bool ContainerControl::ActivateControlInternal(Control* Ctrl, bool Originator)
{
bool Result = true;
bool UpdateContainerActiveControl = false;
ContainerControl* Cc = nullptr;
Control* Parent = this->_Parent;
if (Parent != nullptr)
{
Cc = (ContainerControl*)Parent->GetContainerControl();
if (Cc != nullptr)
{
UpdateContainerActiveControl = (Cc->ActiveControl() != this);
}
}
if (Ctrl != _ActiveControl || UpdateContainerActiveControl)
{
if (UpdateContainerActiveControl)
{
if (!Cc->ActivateControlInternal(this, false))
return false;
}
Result = AssignActiveControlInternal((Ctrl == this) ? nullptr : Ctrl);
}
if (Originator)
{
// TODO: ScrollActiveControlIntoView();
}
return Result;
}
bool ContainerControl::AssignActiveControlInternal(Control* Ctrl)
{
if (_ActiveControl != Ctrl)
{
_ActiveControl = Ctrl;
// TODO: UpdateFocusedControl();
if (_ActiveControl == Ctrl)
{
auto FormCtrl = (Form*)FindForm();
if (FormCtrl != nullptr)
FormCtrl->UpdateDefaultButton();
}
}
else
{
_FocusedControl = _ActiveControl;
}
return (_ActiveControl == Ctrl);
}
void ContainerControl::Select(bool Directed, bool Forward)
{
bool CorrectParentActiveControl = true;
if (this->_Parent != nullptr)
{
auto C = (ContainerControl*)this->_Parent->GetContainerControl();
if (C != nullptr)
{
C->SetActiveControl(this);
CorrectParentActiveControl = (C->ActiveControl() == this);
}
}
if (Directed && CorrectParentActiveControl)
SelectNextControl(nullptr, Forward, true, true, false);
}
void ContainerControl::Scale(Drawing::SizeF IncludedFactor, Drawing::SizeF ExcludedFactor, Control* Ctrl)
{
if (this->_AutoScaleMode == AutoScaleMode::Inherit)
{
Control::Scale(IncludedFactor, ExcludedFactor, Ctrl);
}
else
{
Drawing::SizeF OurExcludedFactor = ExcludedFactor;
Drawing::SizeF ChildIncludedFactor = IncludedFactor;
if (!OurExcludedFactor.Empty())
OurExcludedFactor = this->AutoScaleFactor();
// If we're not supposed to be scaling, don't scale the internal ones either.
if (this->AutoScaleMode() == AutoScaleMode::None)
ChildIncludedFactor = this->AutoScaleFactor();
Drawing::SizeF OurExternalContainerFactor = OurExcludedFactor;
if (!ExcludedFactor.Empty() && this->_Parent != nullptr)
{
OurExternalContainerFactor = Drawing::SizeF{};
if ((Ctrl != this))
{
OurExternalContainerFactor = ExcludedFactor;
}
}
ScaleControl(IncludedFactor, OurExternalContainerFactor, Ctrl);
ScaleChildControls(ChildIncludedFactor, OurExcludedFactor, Ctrl);
}
}
void ContainerControl::OnLayoutResuming(bool PerformLayout)
{
this->PerformNeededAutoScaleOnLayout();
Control::OnLayoutResuming(PerformLayout);
}
void ContainerControl::OnChildLayoutResuming(Control* Child, bool PerformLayout)
{
Control::OnChildLayoutResuming(Child, PerformLayout);
// Do not scale children if AutoScaleMode is set to Dpi
if (/*DpiHelper.EnableSinglePassScalingOfDpiForms &&*/ (this->_AutoScaleMode == AutoScaleMode::Dpi))
{
return;
}
// Perform scaling of the child control
if (!PerformLayout && this->_AutoScaleMode != AutoScaleMode::None && this->_AutoScaleMode != AutoScaleMode::Inherit && this->_ScalingNeededOnLayout)
{
Child->Scale(this->AutoScaleFactor(), Drawing::SizeF{}, this);
}
}
void ContainerControl::FocusActiveControlInternal()
{
if (_ActiveControl != nullptr && _ActiveControl->Visible())
{
auto FocusHandle = GetFocus();
if (FocusHandle == NULL || FocusHandle != _ActiveControl->GetHandle())
SetFocus(_ActiveControl->GetHandle());
}
else
{
ContainerControl* Cc = this;
while (Cc != nullptr && !Cc->Visible())
{
auto Parent = Cc->Parent();
if (Parent != nullptr)
Cc = (ContainerControl*)Parent->GetContainerControl();
else
break;
}
if (Cc != nullptr && Cc->Visible())
SetFocus(Cc->GetHandle());
}
}
void ContainerControl::SetActiveControlInternal(Control* Value)
{
if (_ActiveControl != Value || (Value != nullptr && !Value->Focused()))
{
bool Result = false;
ContainerControl* Cc = nullptr;
if (Value != nullptr && Value->Parent() != nullptr)
{
Cc = (ContainerControl*)Value->Parent()->GetContainerControl();
}
if (Cc != nullptr)
{
Result = ActivateControlInternal(Value, false);
}
else
{
Result = AssignActiveControlInternal(Value);
}
if (Cc != nullptr && Result)
{
ContainerControl* CcAncestor = this;
while (CcAncestor->_Parent != nullptr && CcAncestor->_Parent->GetContainerControl() != nullptr)
CcAncestor = (ContainerControl*)CcAncestor->_Parent->GetContainerControl();
if (CcAncestor->ContainsFocus() /*&& Value is NOT USERCONTROL!!*/)
Cc->FocusActiveControlInternal();
}
}
}
Control* ContainerControl::ParentFormInternal()
{
if (this->_Parent != nullptr)
return this->_Parent->FindForm();
if (this->_RTTI == ControlTypes::Form)
return nullptr;
return FindForm();
}
Drawing::SizeF ContainerControl::AutoScaleFactor()
{
auto Current = this->CurrentAutoScaleDimensions();
auto Saved = this->AutoScaleDimensions();
if (Saved.Empty())
{
return Drawing::SizeF(1.f, 1.f);
}
return Drawing::SizeF(Current.Width / Saved.Width, Current.Height / Saved.Height);
}
}