blob: 00d47d410038764a41610b2604b85a6403d8c767 [file] [log] [blame]
// Copyright 2014 PDFium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
#include "xfa/fwl/core/cfwl_form.h"
#include <utility>
#include "third_party/base/ptr_util.h"
#include "xfa/fde/tto/fde_textout.h"
#include "xfa/fwl/core/cfwl_app.h"
#include "xfa/fwl/core/cfwl_event.h"
#include "xfa/fwl/core/cfwl_formproxy.h"
#include "xfa/fwl/core/cfwl_msgmouse.h"
#include "xfa/fwl/core/cfwl_notedriver.h"
#include "xfa/fwl/core/cfwl_noteloop.h"
#include "xfa/fwl/core/cfwl_sysbtn.h"
#include "xfa/fwl/core/cfwl_themebackground.h"
#include "xfa/fwl/core/cfwl_themepart.h"
#include "xfa/fwl/core/cfwl_themetext.h"
#include "xfa/fwl/core/cfwl_widgetmgr.h"
#include "xfa/fwl/core/ifwl_themeprovider.h"
#include "xfa/fwl/theme/cfwl_widgettp.h"
namespace {
const int kSystemButtonSize = 21;
const int kSystemButtonMargin = 5;
const int kSystemButtonSpan = 2;
} // namespace
namespace {
const uint8_t kCornerEnlarge = 10;
} // namespace
CFWL_Form::CFWL_Form(const CFWL_App* app,
std::unique_ptr<CFWL_WidgetProperties> properties,
CFWL_Widget* pOuter)
: CFWL_Widget(app, std::move(properties), pOuter),
#if (_FX_OS_ == _FX_MACOSX_)
m_bMouseIn(false),
#endif
m_pCloseBox(nullptr),
m_pMinBox(nullptr),
m_pMaxBox(nullptr),
m_pSubFocus(nullptr),
m_fCXBorder(0),
m_fCYBorder(0),
m_iCaptureBtn(-1),
m_iSysBox(0),
m_bLButtonDown(false),
m_bMaximized(false),
m_bSetMaximize(false),
m_bDoModalFlag(false) {
m_rtRelative.Reset();
m_rtRestore.Reset();
RegisterForm();
RegisterEventTarget(nullptr);
}
CFWL_Form::~CFWL_Form() {
UnregisterEventTarget();
UnRegisterForm();
RemoveSysButtons();
}
FWL_Type CFWL_Form::GetClassID() const {
return FWL_Type::Form;
}
bool CFWL_Form::IsInstance(const CFX_WideStringC& wsClass) const {
if (wsClass == CFX_WideStringC(FWL_CLASS_Form))
return true;
return CFWL_Widget::IsInstance(wsClass);
}
void CFWL_Form::GetWidgetRect(CFX_RectF& rect, bool bAutoSize) {
if (!bAutoSize) {
rect = m_pProperties->m_rtWidget;
return;
}
rect.Reset();
FX_FLOAT fCXBorder = GetBorderSize(true);
FX_FLOAT fCYBorder = GetBorderSize(false);
FX_FLOAT fEdge = GetEdgeWidth();
rect.height += fCYBorder + fEdge + fEdge;
rect.width += fCXBorder + fCXBorder + fEdge + fEdge;
}
void CFWL_Form::GetClientRect(CFX_RectF& rect) {
rect = m_pProperties->m_rtWidget;
rect.Offset(-rect.left, -rect.top);
}
void CFWL_Form::Update() {
if (m_iLock > 0)
return;
if (!m_pProperties->m_pThemeProvider)
m_pProperties->m_pThemeProvider = GetAvailableTheme();
Layout();
}
FWL_WidgetHit CFWL_Form::HitTest(FX_FLOAT fx, FX_FLOAT fy) {
GetAvailableTheme();
if (m_pCloseBox && m_pCloseBox->m_rtBtn.Contains(fx, fy))
return FWL_WidgetHit::CloseBox;
if (m_pMaxBox && m_pMaxBox->m_rtBtn.Contains(fx, fy))
return FWL_WidgetHit::MaxBox;
if (m_pMinBox && m_pMinBox->m_rtBtn.Contains(fx, fy))
return FWL_WidgetHit::MinBox;
CFX_RectF rtCap;
rtCap.Set(m_fCYBorder, m_fCXBorder,
0 - kSystemButtonSize * m_iSysBox - 2 * m_fCYBorder,
0 - m_fCXBorder);
if (rtCap.Contains(fx, fy))
return FWL_WidgetHit::Titlebar;
if ((m_pProperties->m_dwStyles & FWL_WGTSTYLE_Border) &&
(m_pProperties->m_dwStyleExes & FWL_STYLEEXT_FRM_Resize)) {
FX_FLOAT fWidth = m_rtRelative.width - 2 * (m_fCYBorder + kCornerEnlarge);
FX_FLOAT fHeight = m_rtRelative.height - 2 * (m_fCXBorder + kCornerEnlarge);
CFX_RectF rt;
rt.Set(0, m_fCXBorder + kCornerEnlarge, m_fCYBorder, fHeight);
if (rt.Contains(fx, fy))
return FWL_WidgetHit::Left;
rt.Set(m_rtRelative.width - m_fCYBorder, m_fCXBorder + kCornerEnlarge,
m_fCYBorder, fHeight);
if (rt.Contains(fx, fy))
return FWL_WidgetHit::Right;
rt.Set(m_fCYBorder + kCornerEnlarge, 0, fWidth, m_fCXBorder);
if (rt.Contains(fx, fy))
return FWL_WidgetHit::Top;
rt.Set(m_fCYBorder + kCornerEnlarge, m_rtRelative.height - m_fCXBorder,
fWidth, m_fCXBorder);
if (rt.Contains(fx, fy))
return FWL_WidgetHit::Bottom;
rt.Set(0, 0, m_fCYBorder + kCornerEnlarge, m_fCXBorder + kCornerEnlarge);
if (rt.Contains(fx, fy))
return FWL_WidgetHit::LeftTop;
rt.Set(0, m_rtRelative.height - m_fCXBorder - kCornerEnlarge,
m_fCYBorder + kCornerEnlarge, m_fCXBorder + kCornerEnlarge);
if (rt.Contains(fx, fy))
return FWL_WidgetHit::LeftBottom;
rt.Set(m_rtRelative.width - m_fCYBorder - kCornerEnlarge, 0,
m_fCYBorder + kCornerEnlarge, m_fCXBorder + kCornerEnlarge);
if (rt.Contains(fx, fy))
return FWL_WidgetHit::RightTop;
rt.Set(m_rtRelative.width - m_fCYBorder - kCornerEnlarge,
m_rtRelative.height - m_fCXBorder - kCornerEnlarge,
m_fCYBorder + kCornerEnlarge, m_fCXBorder + kCornerEnlarge);
if (rt.Contains(fx, fy))
return FWL_WidgetHit::RightBottom;
}
return FWL_WidgetHit::Client;
}
void CFWL_Form::DrawWidget(CFX_Graphics* pGraphics, const CFX_Matrix* pMatrix) {
if (!pGraphics)
return;
if (!m_pProperties->m_pThemeProvider)
return;
IFWL_ThemeProvider* pTheme = m_pProperties->m_pThemeProvider;
bool bInactive = !IsActive();
int32_t iState = bInactive ? CFWL_PartState_Inactive : CFWL_PartState_Normal;
if ((m_pProperties->m_dwStyleExes & FWL_STYLEEXT_FRM_NoDrawClient) == 0)
DrawBackground(pGraphics, pTheme);
#ifdef FWL_UseMacSystemBorder
return;
#endif
CFWL_ThemeBackground param;
param.m_pWidget = this;
param.m_dwStates = iState;
param.m_pGraphics = pGraphics;
param.m_rtPart = m_rtRelative;
if (pMatrix)
param.m_matrix.Concat(*pMatrix);
if (m_pProperties->m_dwStyles & FWL_WGTSTYLE_Border) {
param.m_iPart = CFWL_Part::Border;
pTheme->DrawBackground(&param);
}
if ((m_pProperties->m_dwStyleExes & FWL_WGTSTYLE_EdgeMask) !=
FWL_WGTSTYLE_EdgeNone) {
CFX_RectF rtEdge;
GetEdgeRect(rtEdge);
param.m_iPart = CFWL_Part::Edge;
param.m_rtPart = rtEdge;
param.m_dwStates = iState;
pTheme->DrawBackground(&param);
}
#if (_FX_OS_ == _FX_MACOSX_)
{
if (m_pCloseBox) {
param.m_iPart = CFWL_Part::CloseBox;
param.m_dwStates = m_pCloseBox->GetPartState();
if (m_pProperties->m_dwStates & FWL_WGTSTATE_Deactivated)
param.m_dwStates = CFWL_PartState_Disabled;
else if (CFWL_PartState_Normal == param.m_dwStates && m_bMouseIn)
param.m_dwStates = CFWL_PartState_Hovered;
param.m_rtPart = m_pCloseBox->m_rtBtn;
pTheme->DrawBackground(&param);
}
if (m_pMaxBox) {
param.m_iPart = CFWL_Part::MaximizeBox;
param.m_dwStates = m_pMaxBox->GetPartState();
if (m_pProperties->m_dwStates & FWL_WGTSTATE_Deactivated)
param.m_dwStates = CFWL_PartState_Disabled;
else if (CFWL_PartState_Normal == param.m_dwStates && m_bMouseIn)
param.m_dwStates = CFWL_PartState_Hovered;
param.m_rtPart = m_pMaxBox->m_rtBtn;
param.m_bMaximize = m_bMaximized;
pTheme->DrawBackground(&param);
}
if (m_pMinBox) {
param.m_iPart = CFWL_Part::MinimizeBox;
param.m_dwStates = m_pMinBox->GetPartState();
if (m_pProperties->m_dwStates & FWL_WGTSTATE_Deactivated)
param.m_dwStates = CFWL_PartState_Disabled;
else if (CFWL_PartState_Normal == param.m_dwStates && m_bMouseIn)
param.m_dwStates = CFWL_PartState_Hovered;
param.m_rtPart = m_pMinBox->m_rtBtn;
pTheme->DrawBackground(&param);
}
m_bMouseIn = false;
}
#else
{
if (m_pCloseBox) {
param.m_iPart = CFWL_Part::CloseBox;
param.m_dwStates = m_pCloseBox->GetPartState();
param.m_rtPart = m_pCloseBox->m_rtBtn;
pTheme->DrawBackground(&param);
}
if (m_pMaxBox) {
param.m_iPart = CFWL_Part::MaximizeBox;
param.m_dwStates = m_pMaxBox->GetPartState();
param.m_rtPart = m_pMaxBox->m_rtBtn;
param.m_bMaximize = m_bMaximized;
pTheme->DrawBackground(&param);
}
if (m_pMinBox) {
param.m_iPart = CFWL_Part::MinimizeBox;
param.m_dwStates = m_pMinBox->GetPartState();
param.m_rtPart = m_pMinBox->m_rtBtn;
pTheme->DrawBackground(&param);
}
}
#endif
}
CFWL_Widget* CFWL_Form::DoModal() {
const CFWL_App* pApp = GetOwnerApp();
if (!pApp)
return nullptr;
CFWL_NoteDriver* pDriver = pApp->GetNoteDriver();
if (!pDriver)
return nullptr;
m_pNoteLoop = pdfium::MakeUnique<CFWL_NoteLoop>();
m_pNoteLoop->SetMainForm(this);
pDriver->PushNoteLoop(m_pNoteLoop.get());
m_bDoModalFlag = true;
SetStates(FWL_WGTSTATE_Invisible, false);
pDriver->Run();
#if _FX_OS_ != _FX_MACOSX_
pDriver->PopNoteLoop();
#endif
m_pNoteLoop.reset();
return nullptr;
}
void CFWL_Form::EndDoModal() {
if (!m_pNoteLoop)
return;
m_bDoModalFlag = false;
#if (_FX_OS_ == _FX_MACOSX_)
m_pNoteLoop->EndModalLoop();
const CFWL_App* pApp = GetOwnerApp();
if (!pApp)
return;
CFWL_NoteDriver* pDriver =
static_cast<CFWL_NoteDriver*>(pApp->GetNoteDriver());
if (!pDriver)
return;
pDriver->PopNoteLoop();
SetStates(FWL_WGTSTATE_Invisible, true);
#else
SetStates(FWL_WGTSTATE_Invisible, true);
m_pNoteLoop->EndModalLoop();
#endif
}
void CFWL_Form::DrawBackground(CFX_Graphics* pGraphics,
IFWL_ThemeProvider* pTheme) {
CFWL_ThemeBackground param;
param.m_pWidget = this;
param.m_iPart = CFWL_Part::Background;
param.m_pGraphics = pGraphics;
param.m_rtPart = m_rtRelative;
param.m_rtPart.Deflate(m_fCYBorder, m_fCXBorder, m_fCYBorder, m_fCXBorder);
pTheme->DrawBackground(&param);
}
void CFWL_Form::RemoveSysButtons() {
delete m_pCloseBox;
m_pCloseBox = nullptr;
delete m_pMinBox;
m_pMinBox = nullptr;
delete m_pMaxBox;
m_pMaxBox = nullptr;
}
CFWL_SysBtn* CFWL_Form::GetSysBtnAtPoint(FX_FLOAT fx, FX_FLOAT fy) {
if (m_pCloseBox && m_pCloseBox->m_rtBtn.Contains(fx, fy))
return m_pCloseBox;
if (m_pMaxBox && m_pMaxBox->m_rtBtn.Contains(fx, fy))
return m_pMaxBox;
if (m_pMinBox && m_pMinBox->m_rtBtn.Contains(fx, fy))
return m_pMinBox;
return nullptr;
}
CFWL_SysBtn* CFWL_Form::GetSysBtnByState(uint32_t dwState) {
if (m_pCloseBox && (m_pCloseBox->m_dwState & dwState))
return m_pCloseBox;
if (m_pMaxBox && (m_pMaxBox->m_dwState & dwState))
return m_pMaxBox;
if (m_pMinBox && (m_pMinBox->m_dwState & dwState))
return m_pMinBox;
return nullptr;
}
CFWL_SysBtn* CFWL_Form::GetSysBtnByIndex(int32_t nIndex) {
if (nIndex < 0)
return nullptr;
CFX_ArrayTemplate<CFWL_SysBtn*> arrBtn;
if (m_pMinBox)
arrBtn.Add(m_pMinBox);
if (m_pMaxBox)
arrBtn.Add(m_pMaxBox);
if (m_pCloseBox)
arrBtn.Add(m_pCloseBox);
return arrBtn[nIndex];
}
int32_t CFWL_Form::GetSysBtnIndex(CFWL_SysBtn* pBtn) {
CFX_ArrayTemplate<CFWL_SysBtn*> arrBtn;
if (m_pMinBox)
arrBtn.Add(m_pMinBox);
if (m_pMaxBox)
arrBtn.Add(m_pMaxBox);
if (m_pCloseBox)
arrBtn.Add(m_pCloseBox);
return arrBtn.Find(pBtn);
}
void CFWL_Form::GetEdgeRect(CFX_RectF& rtEdge) {
rtEdge = m_rtRelative;
if (m_pProperties->m_dwStyles & FWL_WGTSTYLE_Border) {
FX_FLOAT fCX = GetBorderSize();
FX_FLOAT fCY = GetBorderSize(false);
rtEdge.Deflate(fCX, fCY, fCX, fCY);
}
}
void CFWL_Form::SetWorkAreaRect() {
m_rtRestore = m_pProperties->m_rtWidget;
CFWL_WidgetMgr* pWidgetMgr = GetOwnerApp()->GetWidgetMgr();
if (!pWidgetMgr)
return;
m_bSetMaximize = true;
Repaint(&m_rtRelative);
}
void CFWL_Form::Layout() {
GetRelativeRect(m_rtRelative);
#ifndef FWL_UseMacSystemBorder
ResetSysBtn();
#endif
}
void CFWL_Form::ResetSysBtn() {
m_fCXBorder =
*static_cast<FX_FLOAT*>(GetThemeCapacity(CFWL_WidgetCapacity::CXBorder));
m_fCYBorder =
*static_cast<FX_FLOAT*>(GetThemeCapacity(CFWL_WidgetCapacity::CYBorder));
RemoveSysButtons();
m_iSysBox = 0;
if (m_pProperties->m_dwStyles & FWL_WGTSTYLE_CloseBox) {
m_pCloseBox = new CFWL_SysBtn;
m_pCloseBox->m_rtBtn.Set(
m_rtRelative.right() - kSystemButtonMargin - kSystemButtonSize,
kSystemButtonMargin, kSystemButtonSize, kSystemButtonSize);
m_iSysBox++;
}
if (m_pProperties->m_dwStyles & FWL_WGTSTYLE_MaximizeBox) {
m_pMaxBox = new CFWL_SysBtn;
if (m_pCloseBox) {
m_pMaxBox->m_rtBtn.Set(
m_pCloseBox->m_rtBtn.left - kSystemButtonSpan - kSystemButtonSize,
m_pCloseBox->m_rtBtn.top, kSystemButtonSize, kSystemButtonSize);
} else {
m_pMaxBox->m_rtBtn.Set(
m_rtRelative.right() - kSystemButtonMargin - kSystemButtonSize,
kSystemButtonMargin, kSystemButtonSize, kSystemButtonSize);
}
m_iSysBox++;
}
if (m_pProperties->m_dwStyles & FWL_WGTSTYLE_MinimizeBox) {
m_pMinBox = new CFWL_SysBtn;
if (m_pMaxBox) {
m_pMinBox->m_rtBtn.Set(
m_pMaxBox->m_rtBtn.left - kSystemButtonSpan - kSystemButtonSize,
m_pMaxBox->m_rtBtn.top, kSystemButtonSize, kSystemButtonSize);
} else if (m_pCloseBox) {
m_pMinBox->m_rtBtn.Set(
m_pCloseBox->m_rtBtn.left - kSystemButtonSpan - kSystemButtonSize,
m_pCloseBox->m_rtBtn.top, kSystemButtonSize, kSystemButtonSize);
} else {
m_pMinBox->m_rtBtn.Set(
m_rtRelative.right() - kSystemButtonMargin - kSystemButtonSize,
kSystemButtonMargin, kSystemButtonSize, kSystemButtonSize);
}
m_iSysBox++;
}
}
void CFWL_Form::RegisterForm() {
const CFWL_App* pApp = GetOwnerApp();
if (!pApp)
return;
CFWL_NoteDriver* pDriver =
static_cast<CFWL_NoteDriver*>(pApp->GetNoteDriver());
if (!pDriver)
return;
pDriver->RegisterForm(this);
}
void CFWL_Form::UnRegisterForm() {
const CFWL_App* pApp = GetOwnerApp();
if (!pApp)
return;
CFWL_NoteDriver* pDriver =
static_cast<CFWL_NoteDriver*>(pApp->GetNoteDriver());
if (!pDriver)
return;
pDriver->UnRegisterForm(this);
}
void CFWL_Form::OnProcessMessage(CFWL_Message* pMessage) {
#ifndef FWL_UseMacSystemBorder
if (!pMessage)
return;
switch (pMessage->GetType()) {
case CFWL_Message::Type::Mouse: {
CFWL_MsgMouse* pMsg = static_cast<CFWL_MsgMouse*>(pMessage);
switch (pMsg->m_dwCmd) {
case FWL_MouseCommand::LeftButtonDown:
OnLButtonDown(pMsg);
break;
case FWL_MouseCommand::LeftButtonUp:
OnLButtonUp(pMsg);
break;
case FWL_MouseCommand::Move:
OnMouseMove(pMsg);
break;
case FWL_MouseCommand::Leave:
OnMouseLeave(pMsg);
break;
case FWL_MouseCommand::LeftButtonDblClk:
OnLButtonDblClk(pMsg);
break;
default:
break;
}
break;
}
default:
break;
}
#endif // FWL_UseMacSystemBorder
}
void CFWL_Form::OnDrawWidget(CFX_Graphics* pGraphics,
const CFX_Matrix* pMatrix) {
DrawWidget(pGraphics, pMatrix);
}
void CFWL_Form::OnLButtonDown(CFWL_MsgMouse* pMsg) {
SetGrab(true);
m_bLButtonDown = true;
CFWL_SysBtn* pPressBtn = GetSysBtnAtPoint(pMsg->m_fx, pMsg->m_fy);
m_iCaptureBtn = GetSysBtnIndex(pPressBtn);
if (!pPressBtn)
return;
pPressBtn->SetPressed();
Repaint(&pPressBtn->m_rtBtn);
}
void CFWL_Form::OnLButtonUp(CFWL_MsgMouse* pMsg) {
SetGrab(false);
m_bLButtonDown = false;
CFWL_SysBtn* pPointBtn = GetSysBtnAtPoint(pMsg->m_fx, pMsg->m_fy);
CFWL_SysBtn* pPressedBtn = GetSysBtnByIndex(m_iCaptureBtn);
if (!pPressedBtn || pPointBtn != pPressedBtn)
return;
if (pPressedBtn == GetSysBtnByState(FWL_SYSBUTTONSTATE_Pressed))
pPressedBtn->SetNormal();
if (pPressedBtn == m_pMaxBox) {
if (m_bMaximized) {
SetWidgetRect(m_rtRestore);
Update();
Repaint();
} else {
SetWorkAreaRect();
Update();
}
m_bMaximized = !m_bMaximized;
} else if (pPressedBtn != m_pMinBox) {
CFWL_Event eClose(CFWL_Event::Type::Close, this);
DispatchEvent(&eClose);
}
}
void CFWL_Form::OnMouseMove(CFWL_MsgMouse* pMsg) {
if (m_bLButtonDown)
return;
CFX_RectF rtInvalidate;
rtInvalidate.Reset();
CFWL_SysBtn* pPointBtn = GetSysBtnAtPoint(pMsg->m_fx, pMsg->m_fy);
CFWL_SysBtn* pOldHover = GetSysBtnByState(FWL_SYSBUTTONSTATE_Hover);
#if _FX_OS_ == _FX_MACOSX_
{
if (pOldHover && pPointBtn != pOldHover)
pOldHover->SetNormal();
if (pPointBtn && pPointBtn != pOldHover)
pPointBtn->SetHover();
if (m_pCloseBox)
rtInvalidate = m_pCloseBox->m_rtBtn;
if (m_pMaxBox) {
if (rtInvalidate.IsEmpty())
rtInvalidate = m_pMaxBox->m_rtBtn;
else
rtInvalidate.Union(m_pMaxBox->m_rtBtn);
}
if (m_pMinBox) {
if (rtInvalidate.IsEmpty())
rtInvalidate = m_pMinBox->m_rtBtn;
else
rtInvalidate.Union(m_pMinBox->m_rtBtn);
}
if (!rtInvalidate.IsEmpty() &&
rtInvalidate.Contains(pMsg->m_fx, pMsg->m_fy)) {
m_bMouseIn = true;
}
}
#else
{
if (pOldHover && pPointBtn != pOldHover) {
pOldHover->SetNormal();
rtInvalidate = pOldHover->m_rtBtn;
}
if (pPointBtn && pPointBtn != pOldHover) {
pPointBtn->SetHover();
if (rtInvalidate.IsEmpty())
rtInvalidate = pPointBtn->m_rtBtn;
else
rtInvalidate.Union(pPointBtn->m_rtBtn);
}
}
#endif
if (!rtInvalidate.IsEmpty())
Repaint(&rtInvalidate);
}
void CFWL_Form::OnMouseLeave(CFWL_MsgMouse* pMsg) {
CFWL_SysBtn* pHover = GetSysBtnByState(FWL_SYSBUTTONSTATE_Hover);
if (!pHover)
return;
pHover->SetNormal();
Repaint(&pHover->m_rtBtn);
}
void CFWL_Form::OnLButtonDblClk(CFWL_MsgMouse* pMsg) {
if ((m_pProperties->m_dwStyleExes & FWL_STYLEEXT_FRM_Resize) &&
HitTest(pMsg->m_fx, pMsg->m_fy) == FWL_WidgetHit::Titlebar) {
if (m_bMaximized)
SetWidgetRect(m_rtRestore);
else
SetWorkAreaRect();
Update();
m_bMaximized = !m_bMaximized;
}
}