W801学习笔记十四:掌机系统——菜单——尝试打造自己的UI

未来将会有诸多应用,这些应用将通过菜单进行有序组织和管理。因此,我们需要率先打造好菜单。

LCD 驱动通常是直接写屏的,虽然速度较快,但用于界面制作则不太适宜。所以,最好能拥有一套 UI 框架。如前所述,我们的目标是学习,故而不采用如 LVGL 之类的框架,一切都需亲力亲为。

构建一个简易的 UI 框架:

每个页面,我们称之为场景(scean),其中包含众多控件。所有页面存放在链表中,每次仅执行最后一个页面的操作。若删除页面,则会退回到前一个页面。

每个页面可以拥有多个控件。这些控件放置在一个链表中。每次仅执行最后一个控件的操作。当前掌机没有触摸功能,故暂不考虑多个控件同时接收事件。若仅有一个控件,也可不使用链表。

之前已设计好,大循环放置在主 main 函数内。每次通过 tick 方法将运行间隔传递给下面的场景(scean),再传递给下面的用户控件(usercontrol)。

好了,开始我们的实操:

菜单分为多个级别,每级都有自己的页面,同时每个应用也有自己的页面。我们将每个页面用场景(SCEAN)来表示。所有实例化的场景都存放在场景列表(sceanList)中进行管理,以便于页面的前进和后退。

一:写个scean基类,或者说是接口也行。

个人比较习惯JAVA的面向对象技术,所以编码风格带有JAVA的色彩。

IScean.h

#ifdef __cplusplusenum SceanResult{SceanResult_EXIT = 1, SceanResult_Done = 2	
};class IScean {public:IScean();virtual ~IScean();// 纯虚函数virtual SceanResult tick(u32 ticks) = 0;virtual int scean_init(cJSON* param) {return 0;};virtual int scean_resume(void){return 0;};virtual int scean_pause(void){return 0;};protected:map_t iconMap = NULL;unsigned char* getImageResource(char* fn);
};#endif

scean中有几个重要的方法:初始化,暂停,恢复,执行(tick)

原则上没有使用系统中断、钩子之类的东西。而是在main函数中进行循环,处理系统级任务,每每次循环的时间消耗通过tick函数传递给当前scean。

main.cpp

void UserMain(void)
{
。。。while(1){handleMainScreanTask();IScean* cur= ((IScean*)sceanList->prev->data);_tmp_Run_Ticks = tls_os_get_time();keyAdepterProc(_tmp_Run_Ticks - _last_Run_Ticks);res = cur->tick(_tmp_Run_Ticks - _last_Run_Ticks);_last_Run_Ticks = _tmp_Run_Ticks;switch (res) {case SceanResult_EXIT:delete cur;ListPopBack(sceanList);((IScean*)sceanList->prev->data)->scean_resume();break;case SceanResult_Done:break;}。。。}
}

scean实现中,加入了scean图标的读取。

IScean.cpp

#include "IScean.h"
#include "../driver/psram.h"int iterate(any_t item, any_t data){psram_heap_free((unsigned char *)data);return MAP_OK;
}IScean::IScean(){iconMap = hashmap_new();
}IScean::~IScean(){any_t val;hashmap_iterate(iconMap, iterate, val);hashmap_free(iconMap);
}unsigned char* IScean::getImageResource(char* fn){int error;unsigned int w,h;unsigned char *val;error= hashmap_get(iconMap, fn, (void**)(&val));if(error == MAP_MISSING){lodepng_decode32_file(&val, &w, &h, fn);hashmap_put(iconMap, fn, val);}return val;
}

二:写个用户控件的基类,或者说是接口也行。

随着东西的增多,我们不能每次都直接写屏,所以需要适当抽一些用户控件以方便页面交互。比如说菜单就需要一个List控件。

IUserControl.h

class IUserControl
{public:IUserControl();virtual ~IUserControl();void setPosi(u16 _x, u16 _y, u16 _width, u16 _height){x=_x;y=_y;width = _width;height= _height;};virtual void update(void) = 0;virtual int tick(u32 ticks) = 0;u8 src;const char *statusInfo;protected:u16 x,y, width, height;
};

三:写个List用户控件类。这应该是比较常用的一种用户控件类了。

里面用一个链表保存所有的选项,然后处理各种按键事件,实现显示效果。

CtlList.h

typedef struct ListItem
{const char *text;unsigned char *image;void* tag;
}ListItem;class CtlList : public IUserControl
{public:CtlList(void);   ~CtlList(void);  int tick(u32 ticks);void addItem(ListItem *item);void clear(void);void update(void);void up(void);void down(void);u16 value(void){return cntIdx;};ListItem* selectItem(void){return (ListItem*)(ListGetNodeAt(items, cntIdx)->data);};private:u16 cntIdx=0;ListNode *items;u8 zk_num;DisplayOption optionC = {FONT_SIZE_1516, WHITE, BLUE,0,1};DisplayOption optionD = {FONT_SIZE_1516, WHITE, BLACK,0,1};};

CtlList.cpp

#include "CtlList.h"CtlList::CtlList(void)
{items =ListCreate();
}CtlList::~CtlList(void)
{printf("Destory CtlList items.\n");clear();items = NULL;
}#define ItemHeifht 34void CtlList::update(void){ListNode *node;u16 showc = height / ItemHeifht;u16 ty = 0;u16 count = ListCount(items);if(count==0) return;u16 itemH = height / count;Display_Fill_Rectangle(x + width-10, y, x + width-1, y + height, GRAY);for(int i= cntIdx - showc /2 ;i<= cntIdx + showc /2;i++){Display_Fill_Rectangle(x, y + ty * ItemHeifht, x + width-11, y + (ty+1) * ItemHeifht - 2, BLACK);if(i>=0 && i< count){node = ListGetNodeAt(items,i);if(i == cntIdx){Display_Fill_Rectangle(x+32, y + ty * ItemHeifht, x + width-11, y + (ty+1) * ItemHeifht - 2, BLUE);Display_String(x+38, y + ty * ItemHeifht + 6, &optionC, (const char*) ((ListItem *)node->data)->text);		}else{Display_String(x+38, y + ty * ItemHeifht + 6, &optionD, (const char*) ((ListItem *)node->data)->text);}if(((ListItem *)node->data)->image !=NULL){Display_Show_Picture_RGBA(x, y + ty * ItemHeifht, 32, 32, (const unsigned char*) ((ListItem *)node->data)->image);}Display_Fill_Rectangle(x + width-9, y + i*itemH, x + width-2, y + + (i+1)*itemH, GREEN);}ty++;}
}void CtlList::addItem(ListItem *item){ListPushBack(items, (LTDataType)item);
}void CtlList::up(void){if(cntIdx > 0 ) cntIdx--; else cntIdx = ListCount(items) -1;update();}void CtlList::down(void){if(cntIdx < ListCount(items) -1  ) cntIdx++; else cntIdx = 0;update();
}void CtlList::clear(void){Display_Fill_Rectangle(x, y, x + width-1, y + height, BLACK);if(items !=NULL){for(ListNode *cur = items->next; cur != items; cur=cur->next){delete (ListItem*)cur->data;}	}ListDestory(items);items=ListCreate();cntIdx = 0;
}int CtlList::tick(u32 ticks){if(KEY_UP) {up();}if(KEY_DOWN) {down();}return 0;
}

四:写Menu类。

menu要实现ISCEAN,里面有个CtlList。

menu.h

class Menu : public IScean
{public:Menu(void);~Menu(void);  SceanResult tick(u32 ticks);int scean_init(u32 param);int scean_resume(void);int scean_pause(void){return 0;};int  id = 100;private:void showMenu();IScean* createScean(u32 idx);unsigned char* getMenuImage(char* fn);ListNode *menuList;CtlList *ctlList;u8 gameMode=0;cJSON *menuRoot;};

menu.cpp

#include "menu.h"
#include "wm_osal.h"
#include "../driver/KeyAdepter.h"
#include "NetConfig.h"
#include "About.h"
#include "Tetris.h"
#include "yingyu.h"
#include "YuWenTs.h"extern ListNode *sceanList;#define SceanId_About 102
#define SceanId_NetConfig 103#define SceanId_YingYu 301
#define SceanId_YuWen 302#define SceanId_Nes 400#define SceanId_Tetris 501
#define SceanId_Plane 502Menu::Menu(void)
{menuList = ListCreate(); ctlList = new CtlList();ctlList->setPosi(100, 70, 280, 170);unsigned char *readBuffer;size_t readsize = fatfs_readFile("menu/menu.txt", &readBuffer);printf("load menu finished! %d\n", readsize );menuRoot = cJSON_Parse((char *)readBuffer);
}Menu::~Menu(void)
{ListDestory(menuList);delete menuList;delete ctlList;cJSON_Delete((cJSON*)menuRoot);}
//×○△□
const char *topMenuInfo="              SEL:选择";
const char *subMenuInfo="EXIT:返回     SEL:选择";int Menu::scean_init(u32 param){scean_resume();					return 0;
}int Menu::scean_resume(void){clear_screen();ListDestory(menuList);menuList = ListCreate(); ListPushBack(menuList, menuRoot);showMenu();setKeyAdepterIntervalAll(200);setKeyAdepterInterval(KEY_GPIO_SEL, 65535);setKeyAdepterInterval(KEY_GPIO_EXIT, 65535);return 0;
}unsigned char* Menu::getMenuImage(char* fn){if(fn[0] == 45){return NULL;}int error;unsigned int w,h;unsigned char *val;error= hashmap_get(iconMap, fn, (void**)(&val));if(error == MAP_MISSING){lodepng_decode32_file(&val, &w, &h, fn);hashmap_put(iconMap, fn, val);}return val;
}void Menu::showMenu(){ctlList->clear();cJSON*  currentMenu =  (cJSON *)menuList->prev->data;int count =	cJSON_GetArraySize(currentMenu);printf("menu count=%d\n", count);for(u8 i=0;i< count;i++){cJSON* cj = 	cJSON_GetArrayItem(currentMenu, i);ListItem *_item = new ListItem();_item->text = cJSON_GetObjectItem(cj,"t")->valuestring;if(cJSON_GetObjectItem(cj,"i")->valuestring[0] != '-')_item->image = getImageResource(cJSON_GetObjectItem(cj,"i")->valuestring);_item->tag= (void *) cj;ctlList->addItem(_item);}ctlList->update();if(currentMenu == menuRoot)show_status_info(topMenuInfo);elseshow_status_info(subMenuInfo);
}SceanResult Menu::tick(u32 ticks){if(KEY_EXIT){ // 返回cJSON*  currentMenu =  (cJSON *)menuList->prev->data;if(	currentMenu == menuRoot) return SceanResult_Done;ListPopBack(menuList);showMenu ();return SceanResult_Done;}if(KEY_SEL){ //进入cJSON* item =  (cJSON*) ctlList->selectItem()->tag;int sceanId = cJSON_GetObjectItem(item,"d")->valueint;if(sceanId!= 0){IScean *scean =createScean(sceanId);if(scean !=NULL){scean->scean_init(cJSON_GetObjectItem(item,"g"));ListPushBack(sceanList, scean);}return SceanResult_Done;}cJSON* sitem =  cJSON_GetObjectItem(item,"s");if(cJSON_GetArraySize(sitem) > 0){ListPushBack(menuList, sitem);			showMenu();return SceanResult_Done;}return SceanResult_Done;}ctlList->tick(ticks);ran_max(10);return SceanResult_Done;
}IScean* Menu::createScean(u32 idx){switch (idx) {case SceanId_About:return new About();case SceanId_Tetris:return new Tetris();case SceanId_NetConfig:return new NetConfig();case SceanId_YingYu:return new YingYu();case SceanId_YuWen:return new YuWenTS();}return NULL;
}

在其实例化的过程中完成菜单JSON的读取。JSON文件放在SD卡中:menu/menu.txt

五:定义菜单项的JSON结构

{"t": "三分钟限时挑战","i": "menu/yuwen.png","d": 0,"g": {},"s": [{"t": "语文","i": "menu/yuwen.png","g": {},"s": [{"t": "唐诗三百首","i": "-","d": 301,"g": {"w":1, "m":1},"s": []},{"t": "宋词三百首","i": "-","d": 301,"g": {"w":1, "m":2},"s": []}]}]
}

其中:

t为菜单项标题

i为菜单项的图标

d为需要启动的应用编号,根据此编号启动对应的应用

g为将传递给应用的各种参数

s为下一级菜单

六:在main函数中,实例化menu的scean,并把它加入sceanList中。

void UserMain(void)
{
。。。sceanList = ListCreate();Menu *menu =  new Menu();menu->scean_init(0);ListPushBack(sceanList, menu); SceanResult res;u32 _last_Run_Ticks = tls_os_get_time();u32 _tmp_Run_Ticks = tls_os_get_time();while(1){handleMainScreanTask();IScean* cur= ((IScean*)sceanList->prev->data);_tmp_Run_Ticks = tls_os_get_time();keyAdepterProc(_tmp_Run_Ticks - _last_Run_Ticks);res = cur->tick(_tmp_Run_Ticks - _last_Run_Ticks);_last_Run_Ticks = _tmp_Run_Ticks;switch (res) {case SceanResult_EXIT:delete cur;ListPopBack(sceanList);((IScean*)sceanList->prev->data)->scean_resume();break;case SceanResult_Done:break;}FrameCount++;}
}

我们看看效果:

W801学习笔记十四:掌机系统——菜单——尝试打造自己的UI

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/2703.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【linux】编译器使用

目录 1. gcc &#xff0c;g 编译器使用 a. 有关gcc的指令&#xff08;g同理&#xff09; 2. .o 文件和库的链接方式 a. 链接方式 b. 动态库 和 静态库 优缺点对比 c. debug 版本 和 release 版本 1. gcc &#xff0c;g 编译器使用 a. 有关gcc的指令&#xff08;g同理&…

设计模式-创建型-抽象工厂模式-Abstract Factory

UML类图 工厂接口类 public interface ProductFactory {Phone phoneProduct();//生产手机Router routerProduct();//生产路由器 } 小米工厂实现类 public class XiaomiFactoryImpl implements ProductFactory {Overridepublic Phone phoneProduct() {return new XiaomiPhone…

Node.js -- fs模块

文章目录 1. 写入文件1.1 写入文件1.2 同步和异步1.3 文件追加写入1.4 流式写入1.5 文件写入的场景 2. 读取文件2.1 异步和同步读取2.2 读取文件应用场景2.3 流式读取2.4 fs 练习 -- 文件复制 3. 文件重命名和移动4. 文件删除5. 文件夹操作5.1 创建文件夹5.2 读取文件夹5.3 删除…

crossover和wine哪个好 wine和crossover有什么本质区别 苹果电脑运行Windows crossover24

CrossOver是Wine的延伸产品&#xff0c;CrossOver可以简单的理解为类虚拟机&#xff0c;那么wine是什么&#xff0c;许多小伙伴就可能有些一知半解。CrossOver和wine哪个好&#xff0c;wine和CrossOver有什么本质区别呢&#xff1f;下文将围绕着这两个问题展开。 一、CrossOve…

tcp inflight 守恒算法的几何解释

接上文&#xff1a;tcp inflight 守恒算法背后的哲学 在 tcp inflight 守恒算法正确性 中&#xff0c;E bw / srtt 的公平最优解是算出来的&#xff0c;如果自然可以用数学描述&#xff0c;那能算出来的东西反过来也一定能通过直感看出来&#xff0c;我倾向于用几何和力学描述…

力扣HOT100 - 199. 二叉树的右视图

解题思路&#xff1a; 相当于层序遍历&#xff0c;然后取每一层的最后一个节点。 class Solution {public List<Integer> rightSideView(TreeNode root) {if (root null) return new ArrayList<Integer>();Queue<TreeNode> queue new LinkedList<>…

Pushmall智能AI数字名片— —寻求商机合作的营销推广平台

Pushmall智能AI数字名片— —寻求商机合作的营销推广平台 开发计划 2024年2月开发计划&#xff1a; 1、优化名片注册、信息完善业务流程&#xff1b; 2、重构商机信息&#xff1a;供应信息、需求信息发布。 3、会员名片服务优化 4、企业名片&#xff1a;员工管理优化 5、CRM客…

【计算机网络】网络模型

OSI七层网络模型 七层模型如图所示 每层的概念和功能 物理层 职责&#xff1a;将数据以比特为单位&#xff0c;通过不同的传输介质将数据传输出去。 主要协议&#xff1a;物理媒介相关的协议&#xff0c;如RS232&#xff0c;V.35&#xff0c;以太网等。 数据链路层 职责&…

【WSL报错】执行:wsl --list --online;错误:0x80072ee7

【WSL报错】执行:wsl --list --online&#xff1b;错误:0x80072ee7 问题情况解决方法详细过程 问题情况 C:\Users\17569>wsl --list --online 错误: 0x80072ee7 解决方法 开系统代理&#xff0c;到外网即可修复&#xff01;&#xff01;&#xff01;&#xff01;&#x…

电商市场已饱和,各大平台都在卷价格?那是你不了解视频号小店

大家好&#xff0c;我是电商笨笨熊 现在做电商除了疯狂内卷就是疯狂内卷&#xff1b; 各大平台都是内卷价格&#xff0c;嘴上说着不想成为拼多多一样的平台&#xff0c;实际做的却是低价再低价&#xff0c;都希望能通过低价吸引用户提高转化&#xff1b; 因此现在很多玩家对…

第十四章大数据和数据科学4分

14.1 引言 14.1.3 科学理念 1.数据科学 数据科学将数据挖掘、统计分析和机器学习与数据集成整合&#xff0c;结合数据建模能力&#xff0c;去构建预测模型、探索数据内容模式。 数据科学依赖于&#xff1a; 1&#xff09;丰富的数据源。具有能够展示隐藏在组织或客户行为中不…

jpa分页插件对象Pageable出现了错误异常如何解决?

jpa分页插件对象Pageable出现了错误异常如何解决&#xff1f;&#xff01; 一般来说&#xff0c;遇到这种的错误异常情况&#xff0c;通常情况 下&#xff0c;都是因为程序员把传递的分页页码数字写错了。 正常情况下&#xff0c;分页页码起始数字应该是0&#xff1b;而不是1…

区块链钱包开发——专业区块链开发

随着区块链技术的发展&#xff0c;钱包开发成为了一项至关重要的任务。本文将探讨区块链钱包开发的重要性&#xff0c;分析当前面临的挑战&#xff0c;并展望未来的发展趋势。 一、区块链钱包概述 区块链钱包是一种用于存储和管理数字货币的软件工具。它为用户提供了一个安全的…

揭开ChatGPT面纱(2):OpenAI主类源码概览

文章目录 〇、使用OpenAI的两个步骤一、初始化方法__init__()1.源码2.参数解析 二、提供的接口1.源码2.接口说明主要接口说明 OpenAI版本1.6.1 〇、使用OpenAI的两个步骤 在上一篇博客中&#xff0c;我实现并运行了一个OpenAI的demo&#xff0c;我们可以发现&#xff0c;想要使…

Unity的旋转实现一些方法总结(案例:通过输入,玩家进行旋转移动)

目录 1. Transform.Rotate 方法 使用 2. Transform.rotation 或 Transform.localRotation 属性与四元数 使用方式&#xff1a; 小案例 &#xff1a;目标旋转角度计算&#xff1a;targetRotation&#xff08;Quaternion类型&#xff09; 玩家发现敌人位置&#xff0c;玩家…

ubuntu20 中设置桌面背景任务

1. 下载conky 使用 Conky 在 Ubuntu 中显示信息&#xff0c;例如你的阅读计划&#xff0c;可以分几个步骤来完成。Conky 是一款灵活的轻量级系统监视器&#xff0c;能够在桌面上显示各种信息。以下是基本的设置步骤&#xff1a; 安装 Conky 首先&#xff0c;你需要在 Ubuntu…

【学习记录】autoware标定相机与激光雷达外参

一、autoware选择 这里踩了好几个坑&#xff0c;首先autoware作为一个无人驾驶知名框架&#xff0c;其内部实际上是有两套标定的东西的&#xff0c;这一点绝大多数博客没有提到。其中最常用的是一个叫标定工具箱的东西&#xff0c;这个ros包已经在1.10往后的版本中被删掉了&am…

Spring 1、初识Spring

初识Spring 了解Spring家族了解Spring发展史Spring Framework系统架构学习路线 了解Spring家族 官网spring.io Spring形成了一种开发的生态圈&#xff0c;Spring提供了若干个项目&#xff0c;每个项目用于完成特定的功能。 Spring Framework&#xff08;最基础的东西&#x…

SpringCloud Alibaba--nacos配置中心

目录 一.基础介绍 1.1概念 1.2 功能 二.实现 2.1 依赖 2.2 新建配置文件 2.3 克隆 2.4 配置bootstap.yml文件 三.测试 一.基础介绍 1.1概念 在微服务架构中&#xff0c;配置中心就是统一管理各个微服务配置文件的服务。把传统的单体jar包拆分成多个微服务后&#xf…

跨越未知,拥抱挑战——新征程

在浩瀚的IT领域里&#xff0c;每一位开发工程师都如同一位探险家&#xff0c;不断地探索、挑战和成长。作为一名新入职的Java开发工程师&#xff0c;我面临着全新的技术栈和业务领域&#xff0c;这是一次跨越未知的征程&#xff0c;也是一次自我提升的机会。 新入职 初入公司…