#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); } }