<2021SC@SDUSC>【Overload游戏引擎】OvUI源码模块分析(四)——Modules&Panels
前言
本章我们来分析Modules和Panels。Modules中的内容是Canvas的定义和操作, Panels中的内容是 Panels的定义和操作以及和ImGui库的对接。 Panels是Canvas的一个关键组件,它们都用于UI界面的显示。
Modules
Canvas
画布表示可用于UI绘图的整个框架
#include "OvUI/Modules/Canvas.h"
void OvUI::Modules::Canvas::Draw()
{
if (!m_panels.empty())
{
ImGui_ImplOpenGL3_NewFrame();
ImGui_ImplGlfw_NewFrame();
ImGui::NewFrame();
if (m_isDockspace)
{
ImGuiViewport* viewport = ImGui::GetMainViewport();
ImGui::SetNextWindowPos(viewport->Pos);
ImGui::SetNextWindowSize(viewport->Size);
ImGui::SetNextWindowViewport(viewport->ID);
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f);
ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f);
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0f, 0.0f));
ImGui::Begin("##dockspace", nullptr, ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_MenuBar | ImGuiWindowFlags_NoDocking);
ImGuiID dockspace_id = ImGui::GetID("MyDockspace");
ImGui::DockSpace(dockspace_id, ImVec2(0.0f, 0.0f), ImGuiDockNodeFlags_PassthruCentralNode);
ImGui::SetWindowPos({ 0.f, 0.f });
ImVec2 displaySize = ImGui::GetIO().DisplaySize;
ImGui::SetWindowSize({ (float)displaySize.x, (float)displaySize.y });
ImGui::End();
ImGui::PopStyleVar(3);
}
for (auto& panel : m_panels)
panel.get().Draw();
ImGui::Render();
}
}
设置画布的窗口和风格并渲染
void OvUI::Modules::Canvas::AddPanel(Panels::APanel & p_panel)
{
m_panels.push_back(std::ref(p_panel));
}
将面板添加到画布
void OvUI::Modules::Canvas::RemovePanel(Panels::APanel & p_panel)
{
m_panels.erase(std::remove_if(m_panels.begin(), m_panels.end(), [&p_panel](std::reference_wrapper<Panels::APanel>& p_item)
{
return &p_panel == &p_item.get();
}));
}
从画布中删除面板
void OvUI::Modules::Canvas::RemoveAllPanels()
{
m_panels.clear();
}
从画布中删除每个面板
void OvUI::Modules::Canvas::MakeDockspace(bool p_state)
{
m_isDockspace = p_state;
}
使画布成为停靠空间(可停靠面板将能够将自身连接到画布)
bool OvUI::Modules::Canvas::IsDockspace() const
{
return m_isDockspace;
}
绘制画布(绘制每个面板)
Panels
面板是画布的一个组件。它是UI中的一种窗口
APanel
OvUI::Panels::APanel::APanel()
{
m_panelID = "##" + std::to_string(__PANEL_ID_INCREMENT++);
}
构造画板
void OvUI::Panels::APanel::Draw()
{
if (enabled)
_Draw_Impl();
}
绘制画板
const std::string & OvUI::Panels::APanel::GetPanelID() const
{
return m_panelID;
}
返回面板标识符
APanelTransformable
在画布中本地化的面板
APanelTransformable
(
const OvMaths::FVector2& p_defaultPosition = OvMaths::FVector2(-1.f, -1.f),
const OvMaths::FVector2& p_defaultSize = OvMaths::FVector2(-1.f, -1.f),
Settings::EHorizontalAlignment p_defaultHorizontalAlignment = Settings::EHorizontalAlignment::LEFT,
Settings::EVerticalAlignment p_defaultVerticalAlignment = Settings::EVerticalAlignment::TOP,
bool p_ignoreConfigFile = false
);
OvUI::Panels::APanelTransformable::APanelTransformable
(
const OvMaths::FVector2& p_defaultPosition,
const OvMaths::FVector2& p_defaultSize,
Settings::EHorizontalAlignment p_defaultHorizontalAlignment,
Settings::EVerticalAlignment p_defaultVerticalAlignment,
bool p_ignoreConfigFile
) :
m_defaultPosition(p_defaultPosition),
m_defaultSize(p_defaultSize),
m_defaultHorizontalAlignment(p_defaultHorizontalAlignment),
m_defaultVerticalAlignment(p_defaultVerticalAlignment),
m_ignoreConfigFile(p_ignoreConfigFile)
{
}
创建可转换的APanel 1.默认位置 2.默认大小 3.默认水平对齐 4.垂直排列 5.配置文件
void OvUI::Panels::APanelTransformable::SetPosition(const OvMaths::FVector2& p_position)
{
m_position = p_position;
m_positionChanged = true;
}
定义面板的位置
void OvUI::Panels::APanelTransformable::SetSize(const OvMaths::FVector2& p_size)
{
m_size = p_size;
m_sizeChanged = true;
}
定义面板的大小
void OvUI::Panels::APanelTransformable::SetAlignment(Settings::EHorizontalAlignment p_horizontalAlignment, Settings::EVerticalAlignment p_verticalAligment)
{
m_horizontalAlignment = p_horizontalAlignment;
m_verticalAlignment = p_verticalAligment;
m_alignmentChanged = true;
}
定义面板的对齐方式
void OvUI::Panels::APanelTransformable::UpdatePosition()
{
if (m_defaultPosition.x != -1.f && m_defaultPosition.y != 1.f)
{
OvMaths::FVector2 offsettedDefaultPos = m_defaultPosition + CalculatePositionAlignmentOffset(true);
ImGui::SetWindowPos(Internal::Converter::ToImVec2(offsettedDefaultPos), m_ignoreConfigFile ? ImGuiCond_Once : ImGuiCond_FirstUseEver);
}
if (m_positionChanged || m_alignmentChanged)
{
OvMaths::FVector2 offset = CalculatePositionAlignmentOffset(false);
OvMaths::FVector2 offsettedPos(m_position.x + offset.x, m_position.y + offset.y);
ImGui::SetWindowPos(Internal::Converter::ToImVec2(offsettedPos), ImGuiCond_Always);
m_positionChanged = false;
m_alignmentChanged = false;
}
}
计算位置偏移量,更新位置,要用到OvMath中定义的二维向量
void OvUI::Panels::APanelTransformable::UpdateSize()
{
if (m_sizeChanged)
{
ImGui::SetWindowSize(Internal::Converter::ToImVec2(m_size), ImGuiCond_Always);
m_sizeChanged = false;
}
}
更新窗口大小
OvMaths::FVector2 OvUI::Panels::APanelTransformable::CalculatePositionAlignmentOffset(bool p_default)
{
OvMaths::FVector2 result(0.0f, 0.0f);
switch (p_default ? m_defaultHorizontalAlignment : m_horizontalAlignment)
{
case OvUI::Settings::EHorizontalAlignment::CENTER:
result.x -= m_size.x / 2.0f;
break;
case OvUI::Settings::EHorizontalAlignment::RIGHT:
result.x -= m_size.x;
break;
}
switch (p_default ? m_defaultVerticalAlignment : m_verticalAlignment)
{
case OvUI::Settings::EVerticalAlignment::MIDDLE:
result.y -= m_size.y / 2.0f;
break;
case OvUI::Settings::EVerticalAlignment::BOTTOM:
result.y -= m_size.y;
break;
}
return result;
}
计算位置偏移量,分别计算水平分量和垂直分量,水平分量以中间和右边界为准,垂直分量以中间和底为准。
PanelWindow
本地化面板,其功能类似于窗口,可进行移动、调整大小等操作。
PanelWindow
(
const std::string& p_name = "",
bool p_opened = true,
const Settings::PanelWindowSettings& p_panelSettings = Settings::PanelWindowSettings{}
);
OvUI::Panels::PanelWindow::PanelWindow(const std::string& p_name, bool p_opened, const Settings::PanelWindowSettings& p_floatingPanelSettings) :
name(p_name),
resizable(p_floatingPanelSettings.resizable),
closable(p_floatingPanelSettings.closable),
movable(p_floatingPanelSettings.movable),
scrollable(p_floatingPanelSettings.scrollable),
dockable(p_floatingPanelSettings.dockable),
hideBackground(p_floatingPanelSettings.hideBackground),
forceHorizontalScrollbar(p_floatingPanelSettings.forceHorizontalScrollbar),
forceVerticalScrollbar(p_floatingPanelSettings.forceVerticalScrollbar),
allowHorizontalScrollbar(p_floatingPanelSettings.allowHorizontalScrollbar),
bringToFrontOnFocus(p_floatingPanelSettings.bringToFrontOnFocus),
collapsable(p_floatingPanelSettings.collapsable),
allowInputs(p_floatingPanelSettings.allowInputs),
m_opened(p_opened)
{
autoSize = p_floatingPanelSettings.autoSize;
}
创建面板窗口,调整参数,并打开面板窗口。
void OvUI::Panels::PanelWindow::SetOpened(bool p_value)
{
if (p_value != m_opened)
{
m_opened = p_value;
if (m_opened)
OpenEvent.Invoke();
else
CloseEvent.Invoke();
}
}
定义窗口的打开状态
bool OvUI::Panels::PanelWindow::IsAppearing() const
{
if (auto window = ImGui::FindWindowByName((name + GetPanelID()).c_str()); window)
return window->Appearing;
else
return false;
}
面板显示状态,如果面板正在显示,则返回true
void OvUI::Panels::PanelWindow::_Draw_Impl()
{
if (m_opened)
{
int windowFlags = ImGuiWindowFlags_None;
if (!resizable) windowFlags |= ImGuiWindowFlags_NoResize;
if (!movable) windowFlags |= ImGuiWindowFlags_NoMove;
if (!dockable) windowFlags |= ImGuiWindowFlags_NoDocking;
if (hideBackground) windowFlags |= ImGuiWindowFlags_NoBackground;
if (forceHorizontalScrollbar) windowFlags |= ImGuiWindowFlags_AlwaysHorizontalScrollbar;
if (forceVerticalScrollbar) windowFlags |= ImGuiWindowFlags_AlwaysVerticalScrollbar;
if (allowHorizontalScrollbar) windowFlags |= ImGuiWindowFlags_HorizontalScrollbar;
if (!bringToFrontOnFocus) windowFlags |= ImGuiWindowFlags_NoBringToFrontOnFocus;
if (!collapsable) windowFlags |= ImGuiWindowFlags_NoCollapse;
if (!allowInputs) windowFlags |= ImGuiWindowFlags_NoInputs;
if (!scrollable) windowFlags |= ImGuiWindowFlags_NoScrollWithMouse | ImGuiWindowFlags_NoScrollbar;
if (!titleBar) windowFlags |= ImGuiWindowFlags_NoTitleBar;
ImVec2 minSizeConstraint = Internal::Converter::ToImVec2(minSize);
ImVec2 maxSizeConstraint = Internal::Converter::ToImVec2(maxSize);
if (minSizeConstraint.x <= 0.f || minSizeConstraint.y <= 0.f)
minSizeConstraint = { 0.0f, 0.0f };
if (maxSizeConstraint.x <= 0.f || maxSizeConstraint.y <= 0.f)
maxSizeConstraint = { 10000.f, 10000.f };
ImGui::SetNextWindowSizeConstraints(minSizeConstraint, maxSizeConstraint);
if (ImGui::Begin((name + m_panelID).c_str(), closable ? &m_opened : nullptr, windowFlags))
{
m_hovered = ImGui::IsWindowHovered();
m_focused = ImGui::IsWindowFocused();
auto scrollY = ImGui::GetScrollY();
m_scrolledToBottom = scrollY == ImGui::GetScrollMaxY();
m_scrolledToTop = scrollY == 0.0f;
if (!m_opened)
CloseEvent.Invoke();
Update();
if (m_mustScrollToBottom)
{
ImGui::SetScrollY(ImGui::GetScrollMaxY());
m_mustScrollToBottom = false;
}
if (m_mustScrollToTop)
{
ImGui::SetScrollY(0.0f);
m_mustScrollToTop = false;
}
DrawWidgets();
}
ImGui::End();
}
}
与ImGui库的对接,控制窗口大小,背景,滚动条的操作是否开启,以及设置了一些操作的约束条件。
总结
关于Modules和Panels的分析就介绍到这里,Panels中有许多简单的显示和操作函数,在这里就不过多介绍了。界面的难点在于需要调整的参数和函数很多,和ImGui的使用,但理解起来并不困难。
|