Skip to content

Commit

Permalink
用户自定义界面中增加树控件TreeElement
Browse files Browse the repository at this point in the history
  • Loading branch information
zhongyang219 committed Aug 24, 2024
1 parent d8d96b1 commit bad6d41
Show file tree
Hide file tree
Showing 5 changed files with 297 additions and 1 deletion.
35 changes: 34 additions & 1 deletion MusicPlayer2/CPlayerUIBase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2711,14 +2711,40 @@ void CPlayerUIBase::DrawList(CRect rect, UiElement::ListElement* list_element, i
}

int col_x = rect_item.left + DPI(4);

int indent_space{}; //缩进距离
UiElement::TreeElement* tree_element = dynamic_cast<UiElement::TreeElement*>(list_element);
//如果是树控件
if (tree_element != nullptr)
{
const int indent_per_level = DPI(8); //每一级缩进距离
indent_space = indent_per_level * tree_element->GetItemLevel(i); //缩进距离
//再留出一定距离用于绘制折叠标志
const int collapse_width = DPI(12);
//如果当前行可折叠,绘制折叠标志
if (tree_element->IsCollapsable(i))
{
//绘制折叠标志
CRect rect_collapsd{ rect_item };
rect_collapsd.left = col_x + indent_space;
rect_collapsd.right = rect_collapsd.left + collapse_width;
m_draw.DrawWindowText(rect_collapsd, (tree_element->IsCollapsed(i) ? _T("-") : _T("+")), m_colors.color_text, Alignment::CENTER, true);
//保存折叠标志矩形区域
if (tree_element != nullptr)
tree_element->collapsd_rects[i] = rect_collapsd;
}
indent_space += collapse_width;
}

//绘制图标
if (list_element->HasIcon())
{
CRect rect_icon{ rect_item };
rect_icon.left = col_x;
rect_icon.right = rect_icon.left + DPI(20);
DrawUiIcon(rect_icon, list_element->GetIcon(i));
col_x = rect_icon.right;
rect_icon.MoveToX(rect_icon.left + indent_space);
DrawUiIcon(rect_icon, list_element->GetIcon(i));
}

//绘制列
Expand All @@ -2730,6 +2756,13 @@ void CPlayerUIBase::DrawList(CRect rect, UiElement::ListElement* list_element, i
rect_cell.right = rect_cell.left + list_element->GetColumnWidth(j, total_width);
std::wstring display_name{ list_element->GetItemText(i, j) };
rect_cell.left += DPI(4); //绘制文字时左侧留出4个像素

//第1列缩进
if (j == 0)
{
rect_cell.left += indent_space;
}

DrawAreaGuard guard(&m_draw, rect & rect_cell);

CRect rect_text{ rect_cell };
Expand Down
196 changes: 196 additions & 0 deletions MusicPlayer2/UIElement.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include "UserUi.h"
#include "MusicPlayerCmdHelper.h"
#include "UIWindowCmdHelper.h"
#include <stack>

///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
Expand Down Expand Up @@ -2826,10 +2827,205 @@ std::shared_ptr<UiElement::Element> CElementFactory::CreateElement(const std::st
element = std::make_shared<UiElement::MiniSpectrum>();
else if (name == "placeHolder")
element = std::make_shared<UiElement::PlaceHolder>();
else if (name == "medialibFolderExplore")
element = std::make_shared<UiElement::TestTree>();
else if (name == "ui" || name == "root" || name == "element")
element = std::make_shared<UiElement::Element>();

if (element != nullptr)
element->SetUi(ui);
return element;
}

void UiElement::TreeElement::Node::AddChild(std::shared_ptr<Node> child)
{
child->parent = this;
child_list.push_back(child);
}

int UiElement::TreeElement::Node::GetLevel() const
{
int level{};
const Node* node{ this };
while (node != nullptr && node->parent != nullptr)
{
node = node->parent;
level++;
}
return level;
}

void UiElement::TreeElement::Node::IterateNodeInOrder(std::function<void(Node*)> func, bool ignore_invisible)
{
std::stack<UiElement::TreeElement::Node*> nodeStack;
nodeStack.push(this);
while (!nodeStack.empty())
{
UiElement::TreeElement::Node* pCurNode = nodeStack.top();
nodeStack.pop();

func(pCurNode);

//如果当前节点已经折叠,且需要忽略已折叠的节点,则不再遍历其子节点
if (pCurNode->collapsed && ignore_invisible)
continue;

for (auto& child : pCurNode->child_list)
{
nodeStack.push(child.get());
}
}
}

std::wstring UiElement::TreeElement::GetItemText(int row, int col)
{
//查找节点
const Node* node = GetNodeByIndex(row);
if (node != nullptr)
{
auto iter = node->texts.find(col);
if (iter != node->texts.end())
return iter->second;
}
return std::wstring();
}

int UiElement::TreeElement::GetRowCount()
{
const auto& root_nodes{ GetRootNodes() };
int row_count{};
for (const auto& root : root_nodes)
{
root->IterateNodeInOrder([&](const Node*) {
row_count++;
}, true);
}
return row_count;
}

int UiElement::TreeElement::GetItemLevel(int row)
{
const Node* node = GetNodeByIndex(row);
if (node != nullptr)
return node->GetLevel();
return 0;
}

bool UiElement::TreeElement::IsCollapsable(int row)
{
const Node* node = GetNodeByIndex(row);
if (node != nullptr)
return !node->child_list.empty();
return false;
}

bool UiElement::TreeElement::IsCollapsed(int row)
{
const Node* node = GetNodeByIndex(row);
if (node != nullptr)
return node->collapsed;
return false;
}

void UiElement::TreeElement::LButtonUp(CPoint point)
{
//获取点击的行
int row = GetListIndexByPoint(point);
if (row >= 0)
{
auto iter = collapsd_rects.find(row);
if (iter != collapsd_rects.end())
{
CRect rect_collapsd = iter->second;
//点击了折叠标志
if (rect_collapsd.PtInRect(point))
{
Node* node = GetNodeByIndex(row);
node->collapsed = !node->collapsed;
}
}
}

ListElement::LButtonUp(point);
}

int UiElement::TreeElement::GetNodeIndex(const Node* node)
{
const auto& root_nodes{ GetRootNodes() };
int i{};
int rtn_index{ -1 };
for (const auto& root : root_nodes)
{
root->IterateNodeInOrder([&](const Node* cur_node) {
if (cur_node == node)
rtn_index = i;
i++;
}, true);
}

return rtn_index;
}

UiElement::TreeElement::Node* UiElement::TreeElement::GetNodeByIndex(int index)
{
if (index >= 0)
{
Node* find_node{};
auto& root_nodes{ GetRootNodes() };
int i{};
for (auto& root : root_nodes)
{
root->IterateNodeInOrder([&](Node* cur_node) {
if (i == index)
find_node = cur_node;
i++;
}, true);
}
return find_node;
}

return nullptr;
}

UiElement::TestTree::TestTree()
{
//创建测试节点
std::shared_ptr<Node> root1 = CreateNode(L"根节点1", nullptr);
std::shared_ptr<Node> root2 = CreateNode(L"根节点2", nullptr);

CreateNode(L"子节点11", root1);
auto node12 = CreateNode(L"子节点12", root1);

CreateNode(L"子节点121", node12);
CreateNode(L"子节点122", node12);

CreateNode(L"子节点21", root2);
CreateNode(L"子节点22", root2);

root_nodes.push_back(root1);
root_nodes.push_back(root2);
}

std::shared_ptr<UiElement::TreeElement::Node> UiElement::TestTree::CreateNode(std::wstring name, std::shared_ptr<Node> parent)
{
std::shared_ptr<Node> node = std::make_shared<Node>();
node->texts[0] = name;
if (parent != nullptr)
parent->AddChild(node);
return node;
}

int UiElement::TestTree::GetColumnCount()
{
return 1;
}

int UiElement::TestTree::GetColumnWidth(int col, int total_width)
{
return total_width;
}

std::vector<std::shared_ptr<UiElement::TestTree::Node>>& UiElement::TestTree::GetRootNodes()
{
return root_nodes;
}
55 changes: 55 additions & 0 deletions MusicPlayer2/UIElement.h
Original file line number Diff line number Diff line change
Expand Up @@ -703,6 +703,61 @@ namespace UiElement
private:
bool IsHide() const;
};

//树控件
//派生类只需要继承GetRootNodes函数返回树的数据即可
class TreeElement : public ListElement
{
public:
//树的一个节点
struct Node
{
std::map<int, std::wstring> texts; //一行的文本,key是列号,value是文本
std::vector<std::shared_ptr<Node>> child_list; //子节点列表
Node* parent{}; //父节点
bool collapsed{}; //是否折叠

void AddChild(std::shared_ptr<Node> child);
int GetLevel() const; //获取节点的级别,如果节点没有父节点,则级别为0
void IterateNodeInOrder(std::function<void(Node*)> func, bool ignore_invisible); //按顺序遍历子节点(ignore_invisible:忽略被折叠的节点)
};

virtual std::vector<std::shared_ptr<Node>>& GetRootNodes() = 0; //获取顶级节点

int GetItemLevel(int row); //获取该行的级别(级别每加1,第一列会缩进一定距离)
bool IsCollapsable(int row); //该行是否可以折叠(如果为true,则显示折叠图标)
bool IsCollapsed(int row); //该行是否折叠

// 通过 Element 继承
virtual void LButtonUp(CPoint point) override;

// 通过 ListElement 继承
std::wstring GetItemText(int row, int col) override;
int GetRowCount() override;

std::map<int, CRect> collapsd_rects; //折叠标志的矩形区域(key是行)

protected:
int GetNodeIndex(const Node* node); //查找一个节点的序号(如果节点被折叠或不存在则返回-1)
Node* GetNodeByIndex(int index); //根据一个节点的序号查找节点(忽略被折叠的节点)

};

class TestTree : public TreeElement
{
public:
TestTree();
static std::shared_ptr<Node> CreateNode(std::wstring name, std::shared_ptr<Node> parent);

virtual IconMgr::IconType GetIcon(int row) { return IconMgr::IT_Folder; }
virtual bool HasIcon() { return true; }
virtual int GetColumnCount() override;
virtual int GetColumnWidth(int col, int total_width) override;
virtual std::vector<std::shared_ptr<Node>>& GetRootNodes() override;

private:
std::vector<std::shared_ptr<Node>> root_nodes;
};
}

/////////////////////////////////////////////////////////////////////////////////////////
Expand Down
9 changes: 9 additions & 0 deletions MusicPlayer2/skins/skin.xsd
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,7 @@
<xs:element name="miniSpectrum" type="miniSpectrumType"></xs:element>
<xs:element name="allTracksList" type="allTracksListType"></xs:element>
<xs:element name="classicalControlBar" type="classicalControlBarType"></xs:element>
<xs:element name="medialibFolderExplore" type="medialibFolderExploreType"></xs:element>
</xs:choice>
</xs:sequence>
</xs:group>
Expand Down Expand Up @@ -1030,4 +1031,12 @@
<xs:group ref="elements"></xs:group>
<xs:attributeGroup ref="sharedattr"></xs:attributeGroup>
</xs:complexType>
<xs:complexType name="medialibFolderExploreType">
<xs:annotation>
<xs:documentation>媒体库中的文件夹浏览。</xs:documentation>
</xs:annotation>
<xs:group ref="elements"></xs:group>
<xs:attributeGroup ref="sharedattr"></xs:attributeGroup>
<xs:attributeGroup ref="listattr"></xs:attributeGroup>
</xs:complexType>
</xs:schema>
3 changes: 3 additions & 0 deletions MusicPlayer2/skins/test.xml
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,9 @@
</verticalLayout>
</verticalLayout>

<!--第4页-->
<medialibFolderExplore/>

</stackElement>

<!--底部控制栏-->
Expand Down

0 comments on commit bad6d41

Please sign in to comment.