Урок 13.1. FACES. UI Поле ввода & поле вывода



  • Цель урока

    Привет! Сегодня мы научимся создавать и подключать собственные элементы графического пользовательского интерфейса для M5 FACES. (рис. 1).

    Рисунок 1

    Необходимо реализовать два элемента графического пользовательского интерфейса (UI) - поле ввода текстовой информации (inputbox) и поле вывода текстовой информации (textbox). Элементы должны работать со строковым типом данных. Процесс подключения должен быть максимально простым для пользователя.

    В качестве наглядного примера применения UI запросим у пользователя данные Wi-Fi подключения, адрес хоста и выполним GET-запрос в inputbox, а результат будем выводить в textbox.

    Краткая справка

    Графический интерфейс представляет собой совокупность функциональных элементов, необходимых для взаимодействия с пользователем. В качестве таких элементов выступают всевозможные поля ввода/вывода текста, кнопки, чекбоксы, ползунки, переключатели и многие другие. В качестве примера графического интерфейса давайте посмотрим на рисунок 2.

    Рисунок 2. Macintosh System 6

    Inputbox представляет собой область ввода текстовой информации на экране с фиксированной высотой 50 px. Ширина может быть задана пользователем, но не может быть меньше 32 px. В верхней части располагается надпись (например: Enter user name), обратите внимание на то, что в конце автоматически будет добавлен символ ':'. В нижней части располагается прямоугольная область, в которую пользователь может вводить данные с клавиатуры. При наведении фокуса на данный элемент - происходит подсветка нижней части.

    Textbox представляет собой область вывода текстовой информации на экране. Размеры могут быть заданы пользователем, но не могут быть меньше размеров одного символа. Состоит данный элемент из одной части. Текст умещается по всей площади и не выходит за пределы. Наведение фокуса на данный элемент не предусмотрено.

    Перечень компонентов для урока

    • M5STACK;
    • FACES;
    • FACES Keyboard
    • кабель USB-C из стандартного набора;

    Начнём!

    Шаг 1. Знакомство со структурой

    Все графические элементы будут описаны одной структурой, но характер у каждого элемента свой. Давайте посмотрим на саму структуру:

    struct UIElement 
    {
      bool focus;
      int x;
      int y;
      int width;
      int height;
      int color;
      String type;
      String layer;
      String label;
      String *rootVar;
    };
    

    Поле focus - определяет может ли обращаться пользователь к данному элементу или нет;

    Поле type - определяет тип элемента, а как следствие характер поведения;

    Поле label - текстовый заголовок (используется не в каждом элементе);

    Поле *rootVar является указателем на область памяти, где расположены ключевые данные элемента;

    Особенное внимание стоит обратить на поле layer. Данное поле определяет слой, на котором расположен элемент.

    Рисунок 3. Layer's

    Если вызвать новый слой, то элементы открытого слоя будут стерты с экрана.

    Шаг 2. Кто главный?

    Если Вы обращали внимание - FACES Keyboard подключен к M5 по интерфейсу I2C. При поступлении новых данных, а именно кодов нажатых клавиш будем выполнять те или иные действия: добавлять символы, изменять фокус и др.

    Напишем функцию, которая будет запущена из отельного потока, чтобы не мешать нам в void loop():

    void UIController(void *pvParameters) {
      while (true)
      {
        if (digitalRead(KEYBOARD_INT) == LOW) {
          Wire.requestFrom(KEYBOARD_I2C_ADDR, 1);  
          while (Wire.available())
          {
            uint8_t key_val = Wire.read();                  
            if (key_val != 0)
            {
              if (key_val > 20 && key_val < 0x7F) UIAddChar(key_val);
              if (key_val == 0x08) UIBackspace(); // Backspace
              if (key_val == 0xba) UITab(); // TAB
              if (key_val == 0x0d) (*UIEnter)(); // Enter
              //M5.Lcd.printf("0x%02X ",key_val);
            }
          }
        }
      }
    }
    

    Запуск UIController(void*) отдельным потоком осуществим из функции UIBegin():

    void UIBegin() {
      for (int i = 0; i < UIElementsSize; i++)
      {
        UIElements[i].layer = "";
      }
      Wire.begin();
      pinMode(KEYBOARD_INT, INPUT_PULLUP);
      M5.Lcd.fillScreen(0xffff);
      xTaskCreatePinnedToCore(UIController, "UIController", 2048, NULL, 1, NULL, 0);  
    }
    

    Шаг 3. Как работает Tab, например?

    Клавиша Tab позволяет изменять фокус между элементами слоя.
    Для того, чтобы произошел вызов Tab необходимо воспользоваться функциональной клавишей на FACES Keyboard. Нажмите клавишу Fn, после чего увидите свечение правого светодиода на Keyboard, затем нажмите клавишу TAB (красного цвета наклейка) (рис. 4).

    Рисунок 4. Использование функциональных клавиш

    Если Вам необходимо зафиксировать клавишу Fn, то нажмите ее быстро дважды, после чего светодиод справа должен быстро мигать.

    void UITab() {
      int i = 0;
      int j = -2;
      while (true)
      {
        if (j >= 0)
        {
          i = j + 1;
          j = -1;
        }
        for ( ; i < UIElementsSize; i++)
        {
          if ((UIElements[i].layer == activeLayer) && (UIElements[i].focus > -1))
          {
            if (j == -2)
            {
              if (UIElements[i].focus == 1)
              {
                UIElements[i].focus = 0;
                j = i;
              }
            }
            else if (j == -1)
            {
              UIElements[i].focus = 1;
              UIUpdate();
              return;
            }
          }
        }
        if (j == -2)
        {
          i = 0;
          j = -1;
        }
        else if (j == -1)
        {
          i = 0;
        }
      }
    }
    

    Шаг 4. Enter - что? O_o

    Клавиша Enter нужна больше пользователю, чем графическому интерфейсу. Поэтому стоит вопрос передачи ее в аренду пользователю. Как это сделать? ... Через указатель на функцию, именно так.

    Вы можете посмотреть исходный код, но уверяю - не найдёте реализации функции для Enter.

    Сделаем функцию заглушку:

    void UIEmpty(){}
    

    А теперь заткнем этой функцией указатель на функцию для Enter:

    pfunc UIEnter = UIEmpty;
    

    Если пользователь нажмёт Enter, то ничего особо не произойдёт. Чтобы что-то произошло пользователю необходимо арендовать клавишу с помощью несложной строчки кода:

    UIEnter = WifiConnectPage;
    

    Не пугайтесь слову WifiConnectPage - это всего лишь имя пользовательской функции. Вы можете изменить на любое свое, главное - чтобы сигнатура была такая void myFunc();.

    Шаг 5. На десерт рассмотрим WifiConnectPage:

    Здесь я рекомендую просто посмотреть на пользовательскую функцию. Попытайтесь разобраться и найти что-то знакомое:

    void WifiConnectPage() {
      UIValue(&result, "WiFi starting...");
      WiFi.begin(strToChar(ssid), strToChar(pswd));
      for (int i = 0; i < 20; i++) // timeout
      {
        UIValue(&result, (result + "."));
        if (WiFi.status() == WL_CONNECTED)
        {
          UIValue(&result, "WiFi connected");
          delay(1000);
          UIValue(&result, "");
          UIAddInputbox(10, 10, 300, "web", "Enter HOST", &host);
          UIValue(&host, "http://m5stack.com/?");
          UIAddTextbox(10, 70, 300, 160, "web", &result, 0x7bef);
          UIEnter = WebPage;
          UIOpenLayer("web");
          return;
        }
        delay(1000);
      }
      UIValue(&result, "WiFi error");
    }
    

    Шаг 6. Настало время void setup()

    Запустим нашу библиотеку UI - UIBegin();. Построим слой, где пользователь сможет подключиться к Wi-Fi сети - WiFiAuthPage();:

    void setup() {
      M5.begin();
      UIBegin();
      WiFiAuthPage();
    }
    

    И всё? - да.

    Шаг 7. Создадим слой. Hello, layer!

    Посмотрите на код:

    void WiFiAuthPage() {
      UIAddInputbox(10, 10, 300, "wifi", "Please, enter SSID here", &ssid);
      UIAddInputbox(10, 80, 300, "wifi", "Please, enter PSWD here", &pswd);
      UIAddTextbox(35, 150, 250, 80, "wifi", &result, 0x7bef);
      UIEnter = WifiConnectPage;
      UIOpenLayer("wifi");
    }
    

    А теперь поговорим подробней. Для того, чтобы добавить поле ввода используйте UIAddInputbox(x, y, ширина, "имя слоя", "надпись", &область_памяти);. Область памяти - это обычная строковая переменная, созданная пользователем. Весь текст введенный в данном элементе будет немедленно записан в эту область памяти.

    Аналогичным образом поступим с UIAddTextbox(x, y, ширина, высота, "имя слоя", &область_памяти, цвет текста);

    UIEnter = WifiConnectPage; - с этом мы уже знакомы. Пропускаем.

    UIOpenLayer("wifi"); не трудно догадаться, чтоб вызвать надо чтобы увидеть слой.

    Шаг 8. Внесение изменений

    Если вам необходимо изменить значения внутри графического элемента на новое, то используйте UIValue(&область_памяти, "новое значение");

    Если необходимо дописать, то: UIValue(&область_памяти, область_памяти + "новое значение");

    Шаг 9. Запуск!

    В разделе "Download" прилагается видео с демонстрацией работы. На этом урок завершён.

    Downloads


登录后回复
 

与 M5Stack Community 的连接断开,我们正在尝试重连,请耐心等待