Chromium GN 目标指南 - view_example 表单示例 (八)

news/2024/12/27 9:38:40/

1. 引言

在前面的文章中,我们学习了如何创建计数器示例,了解了如何使用 Label 和 Button 控件进行交互以及更新 UI 状态。在本篇文章中,我们将创建一个更复杂的示例 —— 表单,以学习如何使用 Textfield、Combobox 和 Checkbox 等控件来收集用户信息,并处理表单提交事件。

2. 创建表单示例

为了创建表单示例,我们需要创建新的头文件和源文件,并在其中定义表单类和相关的示例代码。

2.1 创建头文件

首先,我们在 ui/views/examples/ 目录下创建一个名为 my_form_example.h 的头文件,并在其中添加以下代码:

#ifndef UI_VIEWS_EXAMPLES_MY_FORM_EXAMPLE_H_
#define UI_VIEWS_EXAMPLES_MY_FORM_EXAMPLE_H_#include "ui/views/controls/button/button.h"
#include "ui/views/controls/label.h"
#include "ui/views/controls/textfield/textfield.h"
#include "ui/views/controls/textfield/textfield_controller.h"
#include "ui/views/controls/combobox/combobox.h"
#include "ui/views/controls/button/checkbox.h"
#include "ui/views/examples/example_base.h"
#include "ui/base/models/combobox_model.h"namespace views::examples {class MyFormExample : public ExampleBase,public views::TextfieldController,public ui::ComboboxModel {public:MyFormExample();MyFormExample(const MyFormExample&) = delete;MyFormExample& operator=(const MyFormExample&) = delete;~MyFormExample() override;// ExampleBase:void CreateExampleView(View* container) override;// ComboboxModel:size_t GetItemCount() const override;std::u16string GetItemAt(size_t index) const override;// TextfieldController:void ContentsChanged(Textfield* sender,const std::u16string& new_contents) override;void OnAfterUserAction(Textfield* sender) override;bool HandleKeyEvent(Textfield* sender,const ui::KeyEvent& key_event) override;private:void OnSubmitClicked();raw_ptr<views::Textfield> name_field_ = nullptr;raw_ptr<views::Combobox> role_combobox_ = nullptr;raw_ptr<views::Checkbox> active_checkbox_ = nullptr;raw_ptr<views::Label> status_label_ = nullptr;std::vector<std::u16string> role_options_;
};}  // namespace views::examples#endif  // UI_VIEWS_EXAMPLES_MY_FORM_EXAMPLE_H_

这段代码定义了一个名为 MyFormExample 的类,它继承自 ExampleBase,同时实现了 views::TextfieldControllerui::ComboboxModel 接口,用于处理文本框的输入事件和作为下拉框的数据模型。

MyFormExample 类中,我们声明了一个私有成员函数 OnSubmitClicked,用于处理表单提交事件。我们还声明了四个 raw_ptr 类型的成员变量,分别用于指向姓名文本框 (name_field_)、角色下拉框 (role_combobox_)、激活状态复选框 (active_checkbox_) 和状态标签 (status_label_)。此外,我们还声明了一个 std::vector<std::u16string> 类型的成员变量 role_options_,用于存储下拉框的选项。

2.2 创建源文件

接下来,我们在 ui/views/examples/ 目录下创建一个名为 my_form_example.cc 的源文件,并在其中添加以下代码:

#include "ui/views/examples/my_form_example.h"#include "base/strings/utf_string_conversions.h"
#include "ui/views/controls/button/md_text_button.h"
#include "ui/views/layout/box_layout.h"
#include "ui/views/border.h"namespace views::examples {MyFormExample::MyFormExample(): ExampleBase("Form Example") {role_options_ = {u"Developer",u"Product Manager",u"Designer",u"Tester"};
}MyFormExample::~MyFormExample() = default;void MyFormExample::CreateExampleView(View* container) {container->SetLayoutManager(std::make_unique<BoxLayout>(BoxLayout::Orientation::kVertical, gfx::Insets::TLBR(20, 20, 20, 20), 10));// Create form rowauto CreateFormRow = [](View* parent, const std::u16string& label_text) {auto* row = parent->AddChildView(std::make_unique<View>());row->SetLayoutManager(std::make_unique<BoxLayout>(BoxLayout::Orientation::kHorizontal, gfx::Insets(), 10));auto* label = row->AddChildView(std::make_unique<Label>(label_text));label->SetHorizontalAlignment(gfx::ALIGN_RIGHT);label->SetSize(gfx::Size(80, 0));return row;};// Name input fieldauto* name_row = CreateFormRow(container, u"Name:");name_field_ = name_row->AddChildView(std::make_unique<Textfield>());name_field_->SetPlaceholderText(u"Please enter your name");name_field_->set_controller(this);name_field_->SetSize(gfx::Size(200, 0));// Role dropdownauto* role_row = CreateFormRow(container, u"Role:");role_combobox_ = role_row->AddChildView(std::make_unique<Combobox>(this));role_combobox_->SetAccessibleName(u"Select Role");// Status checkboxauto* status_row = CreateFormRow(container, u"Status:");active_checkbox_ = status_row->AddChildView(std::make_unique<Checkbox>(u"Is Active"));active_checkbox_->SetChecked(true);// Submit buttonauto* button_container = container->AddChildView(std::make_unique<View>());button_container->SetLayoutManager(std::make_unique<BoxLayout>(BoxLayout::Orientation::kHorizontal));button_container->SetBorder(views::CreateEmptyBorder(gfx::Insets::TLBR(10, 80, 0, 0)));auto* submit_button = button_container->AddChildView(std::make_unique<MdTextButton>(base::BindRepeating(&MyFormExample::OnSubmitClicked,base::Unretained(this)),u"Submit"));// Status labelstatus_label_ = container->AddChildView(std::make_unique<Label>(u"Please fill out the form information"));status_label_->SetMultiLine(true);status_label_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
}void MyFormExample::OnSubmitClicked() {if (name_field_->GetText().empty()) {status_label_->SetText(u"Error: Name cannot be empty!");return;}std::u16string status = u"Submission successful!\n";status += u"Name: " + name_field_->GetText() + u"\n";auto selected_index = role_combobox_->GetSelectedIndex();status += u"Role: " + GetItemAt(selected_index.value_or(0)) + u"\n";status += u"Status: " + std::u16string(active_checkbox_->GetChecked() ?u"Active" : u"Inactive");status_label_->SetText(status);
}// ComboboxModel implementation
size_t MyFormExample::GetItemCount() const {return role_options_.size();
}std::u16string MyFormExample::GetItemAt(size_t index) const {return role_options_[index];
}// TextfieldController implementation
void MyFormExample::ContentsChanged(Textfield* sender,const std::u16string& new_contents) {// Update text field displaysender->SetText(new_contents);
}void MyFormExample::OnAfterUserAction(Textfield* sender) {
}bool MyFormExample::HandleKeyEvent(Textfield* sender,const ui::KeyEvent& key_event) {return false;
}}  // namespace views::examples

这段代码实现了 MyFormExample 类的构造函数、析构函数和 CreateExampleViewOnSubmitClicked 方法,以及 ComboboxModelTextfieldController 接口的方法。

  • MyFormExample::MyFormExample(): 构造函数,调用了父类 ExampleBase 的构造函数,并设置了示例的名称为 "Form Example",同时初始化了 role_options_ 数组。
  • MyFormExample::~MyFormExample(): 析构函数。
  • MyFormExample::CreateExampleView(View* container): 这个方法负责创建并添加表单相关的控件到 container 中。
    • 首先,它创建了一个垂直布局管理器 BoxLayout,并将其设置给 container,并设置了边距。
    • 定义了一个内部函数 CreateFormRow 用于创建表单行,每一行包含一个标签和一个控件,使用水平布局。
    • 然后,它创建了姓名文本框 name_field_,并设置了占位符文本和控制器为自身。
    • 接着创建了角色下拉框 role_combobox_,并设置了可访问名称,将其模型设置为自身。
    • 然后创建了状态复选框 active_checkbox_,并默认设置为选中状态。
    • 接着创建了一个提交按钮 submit_button,并将其放置在一个水平布局的容器中,并设置了按钮距离左侧的边距。
    • 最后创建了一个状态标签 status_label_,用于显示表单提交后的状态信息。
  • MyFormExample::OnSubmitClicked(): 当 submit_button 被点击时,这个方法会被调用。它会检查姓名文本框是否为空,如果为空则显示错误信息。否则,它会获取表单中各个控件的值,并将其拼接成一个字符串,然后显示在 status_label_ 中。
  • MyFormExample::GetItemCount() const: ComboboxModel 接口的方法,返回下拉框的选项数量。
  • MyFormExample::GetItemAt(size_t index) const: ComboboxModel 接口的方法,返回指定索引处的下拉框选项的文本内容。
  • MyFormExample::ContentsChanged(...): TextfieldController 接口的方法,文本框内容发生改变时被调用,这里更新文本字段显示。
  • MyFormExample::OnAfterUserAction(...): TextfieldController 接口的方法,用户操作后调用,这里没有具体实现。
  • MyFormExample::HandleKeyEvent(...): TextfieldController 接口的方法,处理键盘事件,这里返回 false 表示不处理。

3. 修改 BUILD.gn 文件

为了将我们的表单示例添加到构建系统中,我们需要修改 ui/views/examples/BUILD.gn 文件,将我们新创建的源文件添加到 views_examples_lib 目标的 sources 属性中。

打开 ui/views/examples/BUILD.gn 文件,找到 source_set("views_examples_lib") 部分,并在 sources 列表中添加以下两行:

"my_form_example.cc",
"my_form_example.h",

4. 注册示例

最后,我们需要在 ui/views/examples/create_examples.cc 文件中注册我们的表单示例,这样 views_examples 程序才能找到并显示它。

打开 ui/views/examples/create_examples.cc 文件,找到 CreateExamples 函数,并在 examples 向量中添加以下代码:

examples.push_back(std::make_unique<MyFormExample>());

修改后的 CreateExamples 函数应该类似于这样:

ExampleVector CreateExamples() {ExampleVector examples;// ... 其他示例 ...examples.push_back(std::make_unique<MyCustomButtonExample>());examples.push_back(std::make_unique<CounterExample>());examples.push_back(std::make_unique<MyFormExample>());// ... 其他示例 ...return examples;
}

5. 重新编译并运行

完成以上步骤后,我们需要重新编译 views_examples 目标。在 Chromium 源码的 src 目录下执行以下命令:

autoninja -C out/Default views_examples

编译完成后,运行 views_examples

./out/Default/views_examples

如果一切顺利,你将在 "Views Examples" 窗口中看到一个新的标签页 "Form Example",点击该标签页,你将看到我们自定义的表单,包括姓名文本框、角色下拉框、激活状态复选框、提交按钮和状态标签。填写表单并点击提交按钮,状态标签会显示你填写的信息。

6. 结语

在本篇文章中,我们学习了如何在 views_examples 中添加表单示例,包括创建头文件和源文件、修改 BUILD.gn 文件、注册示例以及重新编译和运行。通过这个过程,我们学习了如何使用 Textfield、Combobox 和 Checkbox 等控件来收集用户信息,并处理表单提交事件。

希望这篇文章能够帮助你更好地理解 Chromium 的 Views 框架和 GN 构建系统。在接下来的文章中,我们将继续探索 Chromium 和 GN 的更多高级用法。


http://www.ppmy.cn/news/1558497.html

相关文章

spring cloud gateway 3

**Spring Cloud Gateway 3** 是 Spring Cloud 生态系统中的一个重要组件&#xff0c;用于构建 API 网关&#xff0c;提供路由、监控、安全等关键功能。以下是关于 Spring Cloud Gateway 3 的详细介绍&#xff1a; ## 1. 什么是 Spring Cloud Gateway&#xff1f; **Spring Clou…

牛客网刷题 ——C语言初阶——BC114 小乐乐排电梯

1.牛客网 &#xff1a;BC114 小乐乐排电梯 题目描述&#xff1a; 小乐乐学校教学楼的电梯前排了很多人&#xff0c;他的前面有n个人在等电梯。电梯每次可以乘坐12人&#xff0c;每次上下需要的时间为4分钟&#xff08;上需要2分钟&#xff0c;下需要2分钟&#xff09;。请帮助…

(补)算法刷题Day26:BM63 跳台阶

题目链接 描述 一只青蛙一次可以跳上1级台阶&#xff0c;也可以跳上2级。求该青蛙跳上一个 n 级的台阶总共有多少种跳法&#xff08;先后次序不同算不同的结果&#xff09;。 数据范围&#xff1a;1≤n≤40 要求&#xff1a;时间复杂度&#xff1a;O(n) &#xff0c;空间复杂度…

【LeetCode】9、回文数

【LeetCode】9、回文数 文章目录 一、数学: 除法和取模1.1 数学: 除法和取模 二、多语言解法 一、数学: 除法和取模 1.1 数学: 除法和取模 例如 15251, offset 也是五位数的 10000 先判断首1和尾1, 再变为 525, offset 变为 100 再判断首5和尾5, 再变为 2, offset 变为 1 整个…

cursor 编程测试,记录写一个全栈完整的crud的过程

一. 初始化工程&#xff08;手动&#xff09; 创建后端项目 intellij idea 创建 backend 项目。 创建前端项目 npm create vuelastest二. 用 Cursor 实现增删改查 Codebase 帮我实现一个基于sqlite的增删改查功能。 在本文件夹的根目录下&#xff1a; 前端工程文件夹是 fro…

《英雄联盟》提示缺少msvcp100.dll快速修复方法,找不到msvcp100.dll文件解决方法

一、msvcp100.dll缺失的深层原因 msvcp100.dll是Microsoft Visual C Redistributable Package中的一个关键组件&#xff0c;它提供了游戏和许多其他应用程序所需的C运行时库功能。当《英雄联盟》提示缺少msvcp100.dll时&#xff0c;通常意味着以下几种可能&#xff1a; Visual…

PDF书籍《手写调用链监控APM系统-Java版》第3章 配置文件系统的建立

本人阅读了 Skywalking 的大部分核心代码&#xff0c;也了解了相关的文献&#xff0c;对此深有感悟&#xff0c;特此借助巨人的思想自己手动用JAVA语言实现了一个 “调用链监控APM” 系统。本书采用边讲解实现原理边编写代码的方式&#xff0c;看本书时一定要跟着敲代码。 作者…

【算法题解】Bindian 山丘信号问题(E. Bindian Signaling)

问题描述 在 Berland 古老的 Bindian 部落中&#xff0c;首都被 nn 座山丘围成一个圆环&#xff0c;每个山丘上都有一名守望者&#xff0c;日夜观察着周围的情况。 如果有危险&#xff0c;守望者可以在山丘上点燃篝火。两座山丘的守望者可以看到彼此的信号&#xff0c;条件是…