用于构建用户界面的
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