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

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::vectorstd::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 row
  auto 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 field
  auto* 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 dropdown
  auto* role_row = CreateFormRow(container, u"Role:");
  role_combobox_ = role_row->AddChildView(std::make_unique<Combobox>(this));
  role_combobox_->SetAccessibleName(u"Select Role");

  // Status checkbox
  auto* status_row = CreateFormRow(container, u"Status:");
  active_checkbox_ = status_row->AddChildView(
      std::make_unique<Checkbox>(u"Is Active"));
  active_checkbox_->SetChecked(true);

  // Submit button
  auto* 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 label
  status_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 display
  sender->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 的更多高级用法。

相关推荐
Joker`s smile11 小时前
Chrome安装老版本、不同版本,自制便携版本用于前端调试
前端·chrome
weixin_4166399711 小时前
爬虫工程师Chrome开发者工具简单介绍
前端·chrome·爬虫
我是如子啊11 小时前
【解决“此扩展可能损坏”】Edge浏览器(chrome系列通杀))扩展损坏?一招保留数据快速修复
前端·chrome·edge
shimly12345620 小时前
bash 脚本比较 100 个程序运行时间,精确到毫秒,脚本
开发语言·chrome·bash
秃了也弱了。1 天前
Chrome谷歌浏览器插件ModHeader,修改请求头,开发神器
前端·chrome
叶常落1 天前
chrome插件合集
chrome
蓝天白云下遛狗1 天前
goole chrome变更默认搜索引擎为百度
前端·chrome
代码讲故事1 天前
多种方法实现golang中实现对http的响应内容生成图片
开发语言·chrome·http·golang·图片·快照·截图
进击的小白兔vl1 天前
VUE admin-element 后台管理系统三级菜单实现缓存
vue.js·chrome·缓存
PeterJXL2 天前
Chrome 下载文件时总是提示“已阻止不安全的下载”的解决方案
前端·chrome·安全