用于构建用户界面的
C 函数库
一个用于构建跨平台桌面应用程序的开源图形用户界面工具包。
留视界面包含什么?
一些制作用户界面所需的库。
可移植的 UI 库
UI 核心库的依赖 项很少,且无系统 API 依赖。
小巧的图形库
提供用于 UI 渲染所需的基本的图形处理能力。
系统 API
提供平台相关的 API,例如窗口管理、消息循环、剪切板等。
路由管理器
定义路由、映射它们到组件,根据 URL 显示对应的组件。
国际化
配置多个语言的翻译文本,在运行时自由切换语言。
窗口映射
映射组件到系统窗口以让其内容能够同步到窗口内。
预置组件
文本、文本编辑、按钮、滚动条等。
CSS 支持
解析 CSS、选择样式、计算样式。
XML 支持
用 XML 声明用户界面。
从组件创建用户界面
留视界面能让你使用组件来构建用户界面每个部分。创建你自己的界面组件并将它们组合到整个屏幕、页面和应用程序中。
- todolist.xml
 - todolist.css
 - todolist.c
 
<?xml version="1.0" encoding="UTF-8" ?>
<lcui-app>
  <resource type="text/css" src="todolist.css"/>
  <ui>
    <w class="task-item is-completed">
      <w class="task-status" />
      <text class="task-name">Download LCUI source code</text>
      <w class="task-delete" />
    </w>
    <w class="task-item is-completed">
      <w class="task-status" />
      <text class="task-name">Build LCUI</text>
      <w class="task-delete" />
    </w>
    <w class="task-item">
      <w class="task-status" />
      <text class="task-name">Read LCUI tutorials</text>
      <w class="task-delete" />
    </w>
    <w class="task-item">
      <w class="task-status" />
      <text class="task-name">Create my LCUI application</text>
      <w class="task-delete" />
    </w>
  </ui>
</lcui-app>
.task-item:last-child {
  border-bottom: 0;
}
.task-item:hover {
  background-color: #f6fbff;
}
.task-item.is-completed {
  background-color: rgba(74, 206, 163, 0.1);
}
.task-status {
  width: 18px;
  height: 18px;
  cursor: pointer;
  border: 2px solid #bbbdc7;
  border-radius: 9px;
  background-color: #fff;
  background-image: url("check.png");
  background-size: 100%;
  margin-right: 10px;
  flex: none;
  text-align: center;
}
.task-item.is-completed .task-status {
  color: #fff;
  background-color: #4acea3;
  border-color: #38bb90;
}
.task-name {
  flex: 1;
}
.task-delete {
  width: 18px;
  height: 18px;
  display: none;
  flex: none;
  background-size: 100%;
  background-image: url("delete.png");
}
.task-item:hover .task-delete {
  display: block;
}
#include <LCUI.h>
#include <LCUI/main.h>
int main(int argc, char **argv)
{
        ui_widget_t *pack;
        lcui_init();
        pack = ui_load_xml_file("todolist.xml");
        if (!pack) {
                return -1;
        }
        ui_root_append(pack);
        ui_widget_unwrap(pack);
        ui_widget_set_title(ui_root(), L"Todo list");
        return lcui_main();
}
Todo List
Download LCUI source code
Build LCUI
Read LCUI tutorials
Create my LCUI application
在你需要的任何地方添加交互
留视界面是事件驱动的,你可以为组件添加事件处理器,然后在里面做内容变更或样式改动。
- todolist.xml
 - todolist.css
 - todolist.c
 
<?xml version="1.0" encoding="UTF-8" ?>
<lcui-app>
  <resource type="text/css" src="todolist.css"/>
  <ui>
    <w class="app">
      <w class="header">
        <text class="title">Todo list</text>
        <w class="tools">
          <text id="count" class="count" />
          <w id="filters" class="task-filters">
            <text class="task-filter" data-value="all">All</text>
            <text class="task-filter" data-value="active">Active</text>
            <text class="task-filter" data-value="completed">Completed</text>
          </w>
        </w>
      </w>
      <textedit id="input" class="task-input" placeholder="Add a new task..." />
      <w id="list" class="task-list" />
    </w>
  </ui>
</lcui-app>
root {
  display: flex;
  align-items: center;
  justify-content: center;
  background-color: #adcdfc;
}
* {
  box-sizing: border-box;
}
.app {
  max-width: 400px;
  width: 100%;
  background-color: #fff;
  border-radius: 16px;
  box-shadow: 0 20px 80px rgba(0, 0, 0, 0.3);
}
text {
  font-size: 14px;
  color: #455963;
}
.title {
  font-size: 20px;
  font-weight: 600;
  padding: 20px 20px 6px 20px;
}
.tools {
  display: flex;
  align-items: center;
  margin-bottom: 10px;
  padding: 0 20px;
}
.count {
  color: #8a9ca5;
  display: inline-block;
}
.task-filters {
  margin-left: auto;
  display: inline-block;
}
.task-input {
  color: #455963;
  padding: 10px 20px;
  border-top-width: 0;
  border-left-width: 0;
  border-right-width: 0;
  width: 100%;
}
.task-input:focus {
  box-shadow: none;
  border-top-width: 0;
  border-left-width: 0;
  border-right-width: 0;
}
.task-filter {
  padding: 3px 8px;
  color: #8a9ca5;
  border-radius: 10px;
  display: inline-block;
}
.task-filter:hover {
  background-color: rgba(121, 150, 165, 0.1);
}
.task-filter.is-active {
  background-color: #7996a5;
  color: #fff;
}
.task-item {
  display: flex;
  align-items: center;
  padding: 12px 20px;
  border-top: 1px solid #eef0f5;
}
.task-item:last-child {
  border-bottom: 0;
}
.task-item:hover {
  background-color: #f6fbff;
}
.task-item.is-completed {
  background-color: rgba(74, 206, 163, 0.1);
}
.task-status {
  width: 18px;
  height: 18px;
  cursor: pointer;
  border: 2px solid #bbbdc7;
  border-radius: 9px;
  background-color: #fff;
  background-image: url("check.png");
  background-size: 100%;
  margin-right: 10px;
  flex: none;
  text-align: center;
}
.task-item.is-completed .task-status {
  color: #fff;
  background-color: #4acea3;
  border-color: #38bb90;
}
.task-name {
  flex: 1;
}
.task-delete {
  width: 18px;
  height: 18px;
  display: none;
  flex: none;
  background-size: 100%;
  background-image: url("delete.png");
}
.task-item:hover .task-delete {
  display: block;
}
#include <stdio.h>
#include <LCUI.h>
#include <LCUI/main.h>
typedef struct task_t {
        unsigned id;
        wchar_t *name;
        const char *status;
} task_t;
struct todolist_app_t {
        unsigned id;
        list_t tasks;
} app = { 0 };
ui_widget_t *ui_task_item_create(task_t *task)
{
        char id[32] = { 0 };
        ui_widget_t *item = ui_create_widget(NULL);
        ui_widget_t *status = ui_create_widget(NULL);
        ui_widget_t *del = ui_create_widget(NULL);
        ui_widget_t *name = ui_create_widget("text");
        snprintf(id, 32, "%u", task->id);
        ui_text_set_content_w(name, task->name);
        ui_widget_set_attribute(item, "data-id", id);
        ui_widget_add_class(item, "task-item");
        if (strcmp(task->status, "completed") == 0) {
                ui_widget_add_class(item, "is-completed");
        }
        ui_widget_add_class(status, "task-status");
        ui_widget_add_class(del, "task-delete");
        ui_widget_append(item, status);
        ui_widget_append(item, name);
        ui_widget_append(item, del);
        return item;
}
void ui_todolist_update_count(void)
{
        wchar_t text[32];
        ui_widget_t *count = ui_get_widget("count");
        swprintf(text, 32, app.tasks.length > 1 ? L"%u tasks" : L"%u task",
                 app.tasks.length);
        ui_text_set_content_w(count, text);
}
void update_filter_status(ui_widget_t *w, void *activeStatus)
{
        const char *status = ui_widget_get_attr_val(w, "data-value");
        if (status && strcmp(status, activeStatus) == 0) {
                ui_widget_add_class(w, "is-active");
        } else {
                ui_widget_remove_class(w, "is-active");
        }
}
void ui_todolist_filter(const char *status)
{
        task_t *task;
        list_node_t *node;
        ui_widget_t *list = ui_get_widget("list");
        ui_widget_empty(list);
        for (list_each(node, &app.tasks)) {
                task = node->data;
                if (strcmp(status, "all") != 0 &&
                    strcmp(task->status, status) != 0) {
                        continue;
                }
                ui_widget_append(list, ui_task_item_create(task));
        }
        ui_widget_each(ui_get_widget("filters"), update_filter_status,
                       (void *)status);
        ui_todolist_update_count();
}
void ui_todolist_add(const wchar_t *name, const char *status)
{
        task_t *task = malloc(sizeof(task_t));
        task->id = ++app.id;
        task->name = wcsdup2(name);
        task->status = status ? status : "active";
        list_append(&app.tasks, task);
        ui_widget_append(ui_get_widget("list"), ui_task_item_create(task));
        ui_todolist_update_count();
}
void on_input_keydown(ui_widget_t *w, ui_event_t *e, void *arg)
{
        wchar_t name[256];
        if (e->key.code == KEY_ENTER) {
                ui_textedit_get_text_w(w, 0, 255, name);
                ui_todolist_add(name, "active");
                ui_textedit_clear_text(w);
        }
}
void on_filter_click(ui_widget_t *w, ui_event_t *e, void *arg)
{
        const char *status = ui_widget_get_attr_val(e->target, "data-value");
        if (status) {
                ui_todolist_filter(status);
        }
}
void on_task_list_click(ui_widget_t *w, ui_event_t *e, void *arg)
{
        const char *id_str;
        unsigned id;
        task_t *task;
        list_node_t *node;
        ui_widget_t *item = e->target->parent;
        for (item = e->target; !ui_widget_has_class(item, "task-item");
             item = item->parent)
                ;
        id_str = ui_widget_get_attr_val(item, "data-id");
        if (!id_str || sscanf(id_str, "%u", &id) != 1) {
                return;
        }
        if (ui_widget_has_class(e->target, "task-delete")) {
                ui_widget_remove(item);
                for (list_each(node, &app.tasks)) {
                        task = node->data;
                        if (task->id == id) {
                                list_delete_node(&app.tasks, node);
                                break;
                        }
                }
                ui_todolist_update_count();
                return;
        }
        if (!ui_widget_has_class(e->target, "task-status")) {
                return;
        }
        for (list_each(node, &app.tasks)) {
                task = node->data;
                if (task->id != id) {
                        continue;
                }
                if (strcmp(task->status, "completed") == 0) {
                        task->status = "active";
                        ui_widget_remove_class(item, "is-completed");
                        break;
                }
                task->status = "completed";
                ui_widget_add_class(item, "is-completed");
                break;
        }
}
void ui_todolist_init(void)
{
        ui_widget_on(ui_get_widget("input"), "keydown", on_input_keydown, NULL);
        ui_widget_on(ui_get_widget("filters"), "click", on_filter_click, NULL);
        ui_widget_on(ui_get_widget("list"), "click", on_task_list_click, NULL);
        ui_todolist_filter("all");
}
int main(int argc, char **argv)
{
        ui_widget_t *pack;
        lcui_init();
        pack = ui_load_xml_file("todolist.xml");
        if (!pack) {
                return -1;
        }
        ui_root_append(pack);
        ui_widget_unwrap(pack);
        ui_widget_set_title(ui_root(), L"Todo list");
        ui_todolist_init();
        ui_todolist_add(L"Download LCUI source code", "completed");
        ui_todolist_add(L"Build LCUI", "completed");
        ui_todolist_add(L"Read LCUI tutorials", "active");
        ui_todolist_add(L"Create my LCUI application", "active");
        return lcui_main();
}
Todo list
Todo list
4 tasks
all
active
completed
Download LCUI source code
Build LCUI
Read LCUI tutorials
Create my LCUI application