Skip to Content
Plugin 開發Widget 型別詳解

Widget 型別詳解

上一章講了 Widget 模式的整體規則。這一章把 15 種控制項一種一種講:什麼時候選它、在兩個版本長什麼樣、 要填哪些欄位、在編輯器裡怎麼設、.nxlay 與 C 端怎麼寫、常見的坑。

每一節都用同一個順序:用途、外觀、欄位與旗標、編輯器裡怎麼設、範例、常見錯誤。

型別名稱。 ABI 4.0 把三個型別改了名字(編號不變):

4.0 名稱(NX_WTYPE_*)3.x 舊名範例檔
TABLEVALUE_LISTwidgets/table.*
METERPROGRESSwidgets/meter.*
CHOICERADIOwidgets/choice.*

.nxlay 的 type= 兩種寫法都讀得進來,新檔案請寫新名稱。執行期的值、選項、資料列、樣本一律用 SetWidgetProp() 的屬性設定,不再有每種型別各自的專用函式。

範例檔都可以下載,而且每一個 .nxlay 範例都由本專案的測試檢查過(能解析、通過驗證、全部控制項在 40 x 12 的面板裡都看得到); C 範例都用 C89 語法檢查過。

外觀截圖取自 PDK 內 widget_demo 範例:每張上面是視窗版、下面是終端機版。

先看共通的部分

旗標

.nxlay 裡用 flags=OPTIONAL,ACTIVATABLE 這樣的寫法(逗號分隔,不分大小寫,不加 NX_WF_ 前綴;寫錯名字是解析錯誤,不會被默默略過)。

旗標效果編輯器 Inspector
OPTIONAL空間不夠時可以被隱藏(由後往前),見Widget 模式深入Optional 勾選
DECORATION純裝飾:不可聚焦、不參與「跨列不可重疊」檢查、畫在其他控制項下面Decoration 勾選
ACTIVATABLE讓唯讀的控制項可以聚焦、可以被點,並送 NX_WEVT_ACTIVATED。只有 METER、CHART、IMAGE、LABEL、VALUE 合法,且不能和 DECORATION 同時用Activatable 勾選(只在這五種型別出現)
DISABLED停用:顯示成灰色、不可聚焦、不可操作沒有勾選框,直接在 .nxlay 寫 flags=DISABLED
PASSWORDTEXTBOX 專用,輸入內容以遮罩字元顯示沒有勾選框,直接寫 flags=PASSWORD
ALIGN_RIGHT內容靠右對齊(數值欄常用)沒有勾選框,直接寫 flags=ALIGN_RIGHT

每個型別都有的欄位

WidgetId、Type、Y、X、MinW、MaxW、Weight、Optional、Decoration 每種型別在 Inspector 裡都看得到; 面板宣告了欄導引線時還會多一個 XGuide。其餘欄位依型別而定,下面每一節會列出。每個欄位的意義與調整方式見編輯器那一章的 Inspector 一節。

標籤(label)不是每種型別都會畫

標籤最多 31 個字元。有畫標籤的型別:LABEL(文字本身)、VALUE(值前面的名稱)、CHECKBOX(勾選框旁的文字)、 DROPDOWN(選單前面的名稱)、BUTTON(按鈕上的字)、TABLE(第一欄的標題)、GROUPBOX(框的標題)、CHART(高度至少 2 列時的標題)。 兩個版本都不畫標籤的型別:TEXTBOX、SPIN、METER、CHOICE、FILEPICK、IMAGE、SEPARATOR。 想說明這些控制項是什麼,請在它前面(同一列,寬度算好)放一個 LABEL。

執行期能改什麼

你想做的事用什麼
改 TEXTBOX/VALUE/FILEPICK 的文字、CHECKBOX、DROPDOWN、CHOICE、SPIN、METER 的值SetWidgetProp(…, NX_WP_VALUE, …)(LABEL 等沒有值的型別回 ERR_INVALID_PARAM)
設定 DROPDOWN/CHOICE 的選項SetWidgetProp(…, NX_WP_ITEMS, …)(blob:每項一個 NxItemHead 加文字)
設定 TABLE 的資料列SetWidgetProp(…, NX_WP_ROWS, …)(blob:每列一個 NxRowHead 加名稱與值)
設定 CHART 的資料點SetWidgetProp(…, NX_WP_SERIES, …)(blob:NxSeriesHead 加 16 位元小端樣本)
讀/預設 FILEPICK 的路徑GetWidgetProp/SetWidgetProp(NX_WP_VALUE,字串)
暫時把某個控制項藏起來SetWidgetProp(…, NX_WP_HIDDEN, …)
讀回目前的值GetWidgetProp

所有函式都帶面板編號參數(目前恆為 0)。屬性值是 NxPropVal(先填 SizeOfStruct 與 kind);字串與 blob 在呼叫返回時已複製。 label、aux 每段最多 31 位元組,超過 SetWidgetProp 回 ERR_FULL(舊值保留);每個選項、每列的名稱與值每段最多 255 位元組,超過回 ERR_INVALID_PARAM(舊值保留)。

LABEL:靜態文字

用途:一段固定的說明文字;加上 ACTIVATABLE 就成為可以點的「連結」。要在執行期改文字請用 VALUE: LABEL 的文字由宣告決定(要改請重新載入版面或改用 NX_WP_LABEL),把 NX_WP_VALUE 設給它會回 ERR_INVALID_PARAM。

視窗版的 LABEL
視窗版
終端機版的 LABEL
終端機版

欄位與旗標:label(文字)、min_w(要夠放下整段文字);ACTIVATABLE 選用。 編輯器:Toolbox 拖出 Label → Inspector 改 Label 與 MinW;要可以點,把 Activatable 設成 True。

; label.nxlay - one plain LABEL and one LABEL that can be clicked.
[panel]
ref_w=40
ref_h=4

[w.1]
type=LABEL
y=0
min_w=24
label=Firmware ready

[w.2]
type=LABEL
y=1
min_w=24
flags=ACTIVATABLE
label=Open datasheet
/**
 * @file    label.c
 * @brief   The clickable LABEL (flags=ACTIVATABLE); a plain LABEL has no runtime API.
 *          A reading aid for the tutorial page "widget-types", not a complete
 *          plugin: the functions below would be wired into an IcPlugin
 *          (on_widget_event) or called from init()/a Worker callback.
 *
 * @note    ASCII-only and C89 on purpose (VC6 / CP950 safe, and it must pass
 *          the syntax-only guard). The widget ids match label.nxlay.
 */

#include <string.h>

#include "nexus_plugin_api.h"

/** @brief The HostAPI table, saved by init() (not shown here). */
static const NxHostApi *s_host = (const NxHostApi *)0;

/** @brief widget id of the clickable label ([w.2] in label.nxlay). */
#define WID_DATASHEET 2

/**
 * @brief  Widget event handler.
 * @param[in] ctx       This mount's context.
 * @param[in] panel_id  Always 0 for now.
 * @param[in] widget_id Which widget.
 * @param[in] evt       The event, valid during the call only. For ACTIVATED,
 *                      evt->val is an NX_WKIND_U64 that packs the click
 *                      position (high 16 bits vertical, low 16 bits horizontal,
 *                      0..65535; a key press gives NX_WPOS_KEYBOARD).
 * @note   I2C Worker Thread. A LABEL has no value to set: its text comes from
 *         the layout file.
 */
void NX_CALL label_on_event(NxPluginCtx ctx, NxByte panel_id, NxWord widget_id,
                    const NxWidgetEvent *evt)
{
    NX_UNUSED_ARG(panel_id);
    if (widget_id == WID_DATASHEET && evt->kind == NX_WEVT_ACTIVATED)
    {
        s_host->LogWrite(ctx, NX_LOG_INFO, "label: datasheet link clicked");
    }
}

常見錯誤

  • 對 LABEL 設 NX_WP_VALUE 想改字:回 ERR_INVALID_PARAM。改用 VALUE。
  • min_w 比文字短,文字會被截斷。
  • ACTIVATED 的 evt->val.u.u64 是點擊位置的封裝 (v << 16) | h(各 0~65535,鍵盤觸發為 NX_WPOS_KEYBOARD),是無號數;不需要位置就不必理它。

VALUE:標籤加唯讀數值

用途:顯示一個會變的狀態,格式是「標籤:文字」,例如 Version: 1.2。文字由你的程式用 SetWidgetProp(NX_WP_VALUE,字串)寫入,使用者不能改。

視窗版的 VALUE
視窗版
終端機版的 VALUE
終端機版

欄位與旗標:label(冒號前的名稱)、min_w、weight(讓文字區能長寬);ACTIVATABLE 選用(讓它可以點)。 編輯器:拖出 Value → 改 Label、MinW、Weight。

; value.nxlay - "Version: <text>", the text is set from C.
[panel]
ref_w=40
ref_h=4

[w.1]
type=VALUE
y=0
min_w=20
weight=1
label=Version
/**
 * @file    value.c
 * @brief   Writing the text of a VALUE widget.
 *          A reading aid for the tutorial page "widget-types", not a complete
 *          plugin: the functions below would be wired into an IcPlugin
 *          (on_widget_event) or called from init()/a Worker callback.
 *
 * @note    ASCII-only and C89 on purpose (VC6 / CP950 safe, and it must pass
 *          the syntax-only guard). The widget ids match value.nxlay.
 */

#include <string.h>

#include "nexus_plugin_api.h"

/** @brief The HostAPI table, saved by init() (not shown here). */
static const NxHostApi *s_host = (const NxHostApi *)0;

/** @brief widget id of the VALUE ([w.1] in value.nxlay). */
#define WID_VERSION 1

/**
 * @brief  Shows text after the "Version" label.
 * @param[in] ctx  This mount's context.
 * @param[in] text New text (UTF-8); must not be NULL.
 * @return ERR_OK, or ERR_INVALID_PARAM when the widget is not a VALUE.
 * @note   Any thread; no event is generated for your own write.
 */
NxLong value_show(NxPluginCtx ctx, const char *text)
{
    NxPropVal v;

    memset(&v, 0, sizeof(v));
    v.SizeOfStruct = (NxDword)sizeof(v);
    v.kind         = (NxWord)NX_WKIND_STR;   /* a VALUE takes a string */
    v.u.str.ptr    = text;
    v.u.str.len    = (NxDword)strlen(text);
    return s_host->SetWidgetProp(ctx, 0, WID_VERSION, NX_WP_VALUE, &v);
}

常見錯誤

  • NxPropVal 的 kind 必須是 NX_WKIND_STR,種類不符回 ERR_INVALID_PARAM。
  • 文字長度的上限是 NX_WTEXTBOX_MAX_LENGTH(32767 個碼點),超過會被截斷;影子表另有文字配額(每面板 512 KiB),用盡回 ERR_FULL。
  • 用 memset 把 NxPropVal 清成 0 再填 SizeOfStruct 與 kind。

TEXTBOX:單行文字輸入

用途:讓使用者輸入一段文字(名稱、位址、參數)。加 PASSWORD 旗標會遮罩顯示。

視窗版的 TEXTBOX
視窗版
終端機版的 TEXTBOX
終端機版

欄位與旗標:min_w、weight(記得給,不然只有 min_w 那麼寬);PASSWORD、DISABLED 選用。label 兩個版本都不畫。 編輯器:拖出 Textbox → 調 MinW、Weight;要遮罩就在 .nxlay 加 flags=PASSWORD。

; textbox.nxlay - a single-line text input.
[panel]
ref_w=40
ref_h=4

[w.1]
type=TEXTBOX
y=0
min_w=16
weight=1
label=Name
/**
 * @file    textbox.c
 * @brief   Reading what the user typed into a TEXTBOX, and writing the box.
 *          A reading aid for the tutorial page "widget-types", not a complete
 *          plugin: the functions below would be wired into an IcPlugin
 *          (on_widget_event) or called from init()/a Worker callback.
 *
 * @note    ASCII-only and C89 on purpose (VC6 / CP950 safe, and it must pass
 *          the syntax-only guard). The widget ids match textbox.nxlay.
 */

#include <string.h>

#include "nexus_plugin_api.h"

/** @brief The HostAPI table, saved by init() (not shown here). */
static const NxHostApi *s_host = (const NxHostApi *)0;

/** @brief widget id of the TEXTBOX ([w.1] in textbox.nxlay). */
#define WID_NAME 1

/** @brief The last committed text (copied: evt->val.u.str.ptr is only valid during the call). */
static char s_name[128] = "";

/**
 * @brief  Widget event handler.
 * @note   I2C Worker Thread. CHANGED arrives when the user commits the text
 *         (Enter, or moving focus away), not on every keystroke.
 */
void NX_CALL textbox_on_event(NxPluginCtx ctx, NxByte panel_id, NxWord widget_id,
                      const NxWidgetEvent *evt)
{
    size_t n;

    NX_UNUSED_ARG(ctx);
    NX_UNUSED_ARG(panel_id);
    if (widget_id == WID_NAME && evt->kind == NX_WEVT_CHANGED &&
        evt->val.kind == NX_WKIND_STR && evt->val.u.str.ptr != (const char *)0)
    {
        n = evt->val.u.str.len;
        if (n > sizeof(s_name) - 1)
        {
            /* A TEXTBOX value can be far longer (32767 code points); this demo
             * keeps 127 bytes and may cut a multi-byte character. */
            n = sizeof(s_name) - 1;
        }
        memcpy(s_name, evt->val.u.str.ptr, n);
        s_name[n] = '\0';
    }
}

/**
 * @brief  Replaces the text in the box (e.g. after loading a saved setting).
 * @return ERR_OK or an error code. Does not trigger textbox_on_event().
 */
NxLong textbox_set(NxPluginCtx ctx, const char *text)
{
    NxPropVal v;

    memset(&v, 0, sizeof(v));
    v.SizeOfStruct = (NxDword)sizeof(v);
    v.kind         = (NxWord)NX_WKIND_STR;
    v.u.str.ptr    = text;
    v.u.str.len    = (NxDword)strlen(text);
    return s_host->SetWidgetProp(ctx, 0, WID_NAME, NX_WP_VALUE, &v);
}

常見錯誤

  • CHANGED 只在使用者「提交」時才送:按 Enter,或把焦點移到別處。不是每打一個字送一次。想即時反應請自己設計成別的互動。
  • evt->val.u.str.ptr 只在這次呼叫期間有效,要保留請複製(範例的 s_name)。
  • 字串可以很長。 4.0 起 TEXTBOX 的值最長 32767 個碼點(UTF-8 最多約 128 KB),不再是 3.x 的 127 位元組;範例的 s_name[128] 只留前 127 位元組,這只是示範,要完整保留請依 evt->val.u.str.len 配置足夠的緩衝。讀回目前的值時,先以 buf == NULL、buf_cap == 0 呼叫 GetWidgetProp 問 needed 再配置。
  • 文字長度上限是 NX_WP_MAX_LENGTH(預設也是最大值 32767 個碼點);調小它使目前的字被截短時,會送一次 CHANGED。
  • 視窗版的 TEXTBOX 有焦點時按鍵由它自己吃掉,不會送到 on_key_press。
  • 使用者可以用 Shift+方向鍵選取、Ctrl+A 全選、Ctrl+←/→ 以詞跳(中文每個字算一個詞)、Ctrl+Z 復原一步、Ctrl+C/X/V 複製剪下貼上(終端機版複製用 Ctrl+Shift+C 或 Ctrl+Insert;欄位沒有選取時這兩個鍵改複製 Log 的選取)。視窗版還可以用滑鼠拖曳選取、雙擊選詞、Shift+點擊延伸選取,按右鍵會出現 Undo/Cut/Copy/Paste/Select All 選單(選單文字是英文)。這些都是 Host 端的編輯動作,你的 Plugin 只在「提交」時收到 CHANGED。PASSWORD 欄位不能複製或剪下。

CHECKBOX:開關

用途:一個開/關的選項。

視窗版的 CHECKBOX
視窗版
終端機版的 CHECKBOX
終端機版

欄位與旗標:label(旁邊的文字)、min_w(要放得下勾選框加文字)。 編輯器:拖出 Checkbox → 改 Label、MinW。

checkbox.nxlay下載 checkbox.nxlay
; checkbox.nxlay - an on/off switch.
[panel]
ref_w=40
ref_h=4

[w.1]
type=CHECKBOX
y=0
min_w=14
label=Enable output
/**
 * @file    checkbox.c
 * @brief   Reacting to a CHECKBOX and setting it from code.
 *          A reading aid for the tutorial page "widget-types", not a complete
 *          plugin: the functions below would be wired into an IcPlugin
 *          (on_widget_event) or called from init()/a Worker callback.
 *
 * @note    ASCII-only and C89 on purpose (VC6 / CP950 safe, and it must pass
 *          the syntax-only guard). The widget ids match checkbox.nxlay.
 */

#include <string.h>

#include "nexus_plugin_api.h"

/** @brief The HostAPI table, saved by init() (not shown here). */
static const NxHostApi *s_host = (const NxHostApi *)0;

/** @brief widget id of the CHECKBOX ([w.1] in checkbox.nxlay). */
#define WID_ENABLE 1

/**
 * @brief  Widget event handler.
 * @note   I2C Worker Thread, so ExecSync() is allowed here.
 */
void NX_CALL checkbox_on_event(NxPluginCtx ctx, NxByte panel_id, NxWord widget_id,
                       const NxWidgetEvent *evt)
{
    NX_UNUSED_ARG(panel_id);
    if (widget_id == WID_ENABLE && evt->kind == NX_WEVT_CHANGED &&
        evt->val.kind == NX_WKIND_I64)
    {
        s_host->LogWrite(ctx, NX_LOG_INFO,
                         (evt->val.u.i64 != 0) ? "checkbox: on" : "checkbox: off");
    }
}

/**
 * @brief  Ticks or clears the box.
 * @param[in] on Non-zero = checked.
 */
NxLong checkbox_set(NxPluginCtx ctx, int on)
{
    NxPropVal v;

    memset(&v, 0, sizeof(v));
    v.SizeOfStruct = (NxDword)sizeof(v);
    v.kind         = (NxWord)NX_WKIND_I64;
    v.u.i64        = (on != 0) ? 1 : 0;
    return s_host->SetWidgetProp(ctx, 0, WID_ENABLE, NX_WP_VALUE, &v);
}

常見錯誤

  • evt->val.u.i64 是 0 或 1。寫入時任何非 0 值都算勾選。
  • on_widget_event 在 Worker 上,可以直接 ExecSync 去寫硬體,不必再自己開執行緒。

用途:從固定的選項清單裡選一個。選項由 NX_WP_ITEMS 設定,最多 4096 項(.nxlay 的 item_count 只是 0~8 的版面提示);選項很少、要全部同時看得見時用 CHOICE。

視窗版的 DROPDOWN
視窗版
終端機版的 DROPDOWN
終端機版

欄位與旗標:label(選單前的名稱)、min_w、item_count(選項數,0~8)。選項的文字不寫在 .nxlay,由 C 端以 SetWidgetProp 的 NX_WP_ITEMS 設定。 編輯器:拖出 Dropdown → 改 Label、ItemCount。編輯器預覽用的是預設的一個選項,實際的選項由你的程式在執行期設。

dropdown.nxlay下載 dropdown.nxlay
; dropdown.nxlay - pick one of up to 8 items; the items come from C.
[panel]
ref_w=40
ref_h=4

[w.1]
type=DROPDOWN
y=0
min_w=20
weight=1
item_count=3
label=Mode
/**
 * @file    dropdown.c
 * @brief   Filling a DROPDOWN with items and reading the chosen index.
 *          A reading aid for the tutorial page "widget-types", not a complete
 *          plugin: the functions below would be wired into an IcPlugin
 *          (on_widget_event) or called from init()/a Worker callback.
 *
 * @note    ASCII-only and C89 on purpose (VC6 / CP950 safe, and it must pass
 *          the syntax-only guard). The widget ids match dropdown.nxlay.
 */

#include <string.h>

#include "nexus_plugin_api.h"

/** @brief The HostAPI table, saved by init() (not shown here). */
static const NxHostApi *s_host = (const NxHostApi *)0;

/** @brief widget id of the DROPDOWN ([w.1] in dropdown.nxlay). */
#define WID_MODE 1

/** @brief The items, static because the host copies them during the call only. */
static const char *const s_items[3] = { "Off", "Slow", "Fast" };

/**
 * @brief  Call once from init() (UI Thread).
 * @return ERR_OK or an error code.
 * @note   NX_WP_ITEMS is a BLOB: one NxItemHead per item, followed by the item
 *         text, each record padded to a multiple of 4 bytes. At most 4096 items.
 *         The value of the widget is the item INDEX. The host copies the blob
 *         during the call only.
 */
NxLong dropdown_setup(NxPluginCtx ctx)
{
    unsigned char buf[3 * 24];
    NxPropVal     v;
    NxItemHead    h;
    NxDword       pos = 0;
    NxDword       rec;
    int           i;

    for (i = 0; i < 3; i++)
    {
        h.text_len   = (NxWord)strlen(s_items[i]);
        h.detail_len = 0;
        h.item_id    = (NxDword)i;
        h.flags      = 0;
        rec = ((NxDword)sizeof(h) + h.text_len + 3u) & ~3u;
        memset(buf + pos, 0, rec);
        memcpy(buf + pos, &h, sizeof(h));
        memcpy(buf + pos + sizeof(h), s_items[i], h.text_len);
        pos += rec;
    }
    memset(&v, 0, sizeof(v));
    v.SizeOfStruct = (NxDword)sizeof(v);
    v.kind         = (NxWord)NX_WKIND_BLOB;
    v.u.blob.ptr   = buf;
    v.u.blob.len   = pos;
    return s_host->SetWidgetProp(ctx, 0, WID_MODE, NX_WP_ITEMS, &v);
}

/**
 * @brief  Widget event handler: evt->val.u.i64 is the chosen index (0-based).
 */
void NX_CALL dropdown_on_event(NxPluginCtx ctx, NxByte panel_id, NxWord widget_id,
                       const NxWidgetEvent *evt)
{
    NX_UNUSED_ARG(panel_id);
    if (widget_id == WID_MODE && evt->kind == NX_WEVT_CHANGED &&
        evt->val.kind == NX_WKIND_I64 && evt->val.u.i64 >= 0 && evt->val.u.i64 < 3)
    {
        s_host->LogWrite(ctx, NX_LOG_INFO, s_items[(int)evt->val.u.i64]);
    }
}

/**
 * @brief  Selects an item from code; index must be smaller than the item count.
 */
NxLong dropdown_select(NxPluginCtx ctx, NxDword index)
{
    NxPropVal v;

    memset(&v, 0, sizeof(v));
    v.SizeOfStruct = (NxDword)sizeof(v);
    v.kind         = (NxWord)NX_WKIND_I64;
    v.u.i64        = (NxI64)index;
    return s_host->SetWidgetProp(ctx, 0, WID_MODE, NX_WP_VALUE, &v);
}

常見錯誤

  • 忘了設 NX_WP_ITEMS:選單是空的。它在 init(UI Thread)設一次即可。
  • 選項是屬性,不會重建面板,但不要在輪詢裡反覆改。
  • 每個選項文字最多 255 位元組(NX_WDROPDOWN_ITEM_TEXT_MAX),超過回 ERR_INVALID_PARAM;每面板的文字配額(512 KiB)以實際長度計,255 位元組的選項約放 2016 項。選項另受整個行程共用的 1 MiB 套件 arena 限制(每項 24 + 文字長度 位元組,255 位元組的選項每項 279 位元組,全體約 3758 項),兩個面板各放 2016 項會超過,第二個回 ERR_FULL;每筆 blob 記錄要補到 4 的倍數((head + body + 3) & ~3,範例的 dropdown_setup 示範)。
  • 值(索引)要小於選項數,否則回 ERR_INVALID_PARAM。
  • 選項變少時,在途的 CHANGED(索引已超出新範圍)會被丟棄,不會把錯誤的索引送給你。

BUTTON:按鈕

用途:觸發一個動作(讀一次、送一個指令、重設)。按鈕沒有持續的狀態。

視窗版的 BUTTON
視窗版
終端機版的 BUTTON
終端機版

欄位與旗標:label(按鈕上的字)、min_w;DISABLED 選用(灰色、不能按)。 編輯器:拖出 Button → 改 Label、MinW。

; button.nxlay - an action button.
[panel]
ref_w=40
ref_h=4

[w.1]
type=BUTTON
y=0
min_w=12
label=Read
/**
 * @file    button.c
 * @brief   Reacting to a BUTTON press with a hardware read.
 *          A reading aid for the tutorial page "widget-types", not a complete
 *          plugin: the functions below would be wired into an IcPlugin
 *          (on_widget_event) or called from init()/a Worker callback.
 *
 * @note    ASCII-only and C89 on purpose (VC6 / CP950 safe, and it must pass
 *          the syntax-only guard). The widget ids match button.nxlay.
 */

#include <string.h>

#include "nexus_plugin_api.h"

/** @brief The HostAPI table, saved by init() (not shown here). */
static const NxHostApi *s_host = (const NxHostApi *)0;

/** @brief widget id of the BUTTON ([w.1] in button.nxlay). */
#define WID_READ 1

/**
 * @brief  Widget event handler.
 * @note   I2C Worker Thread: read the hardware right here with ExecSync().
 *         A BUTTON has no state; evt->val is the click position (ignored here).
 */
void NX_CALL button_on_event(NxPluginCtx ctx, NxByte panel_id, NxWord widget_id,
                     const NxWidgetEvent *evt)
{
    NxJobRequest req;
    NxByte       reg;

    NX_UNUSED_ARG(panel_id);
    if (widget_id != WID_READ || evt->kind != NX_WEVT_ACTIVATED)
    {
        return;
    }
    memset(&req, 0, sizeof(req));
    req.size_of_struct = (NxDword)sizeof(req);
    req.op     = NX_JOB_READ;
    req.offset = 0x00;
    req.length = 1;
    if (s_host->ExecSync(ctx, &req, &reg, (NxWord)sizeof(reg)) == ERR_OK)
    {
        s_host->LogWrite(ctx, NX_LOG_INFO, "button: read ok");
    }
}

常見錯誤

  • 事件是 NX_WEVT_ACTIVATED(不是 CHANGED);evt->val 是點擊位置的封裝(滑鼠點擊帶位置,鍵盤觸發為 NX_WPOS_KEYBOARD),用不到就忽略。
  • 對 BUTTON 設 NX_WP_VALUE 回 ERR_INVALID_PARAM:按鈕沒有值可以設。要改按鈕的狀態用 NX_WP_ENABLED(停用)或 NX_WP_HIDDEN(隱藏)。
  • 處理函式在 Worker 上,可以做硬體存取,但不要在裡面做很久的事,否則這個後端的其他存取都被擋住。

TABLE:名稱與值的表格

用途:多列「名稱 + 值」的表格,例如暫存器語意表。每個 TABLE 最多 4096 列(整個面板的文字另受 512 KiB 配額限制)。 資料列由 C 端以 NX_WP_ROWS 整批寫入;捲動由主程式負責,Plugin 不用管也無法指定捲動位置。

視窗版的 TABLE
視窗版
終端機版的 TABLE
終端機版

欄位與旗標:label(名稱那一欄的標題)、min_w、weight。高度不由 h 決定,見下。 編輯器:拖出 Value List → 改 Label、MinW、Weight;高度靠調整下一列的 Y 來控制(Inspector 有 H 欄,但對它沒有作用)。

; table.nxlay - a table of name/value rows.
; The list is as tall as the distance to the next row's y (here 6 rows:
; y=0..5); its own h is ignored.
[panel]
ref_w=40
ref_h=8

[w.1]
type=TABLE
y=0
min_w=24
weight=1
label=Registers

[w.2]
type=SEPARATOR
y=6
min_w=0
weight=1
/**
 * @file    table.c
 * @brief   Filling a TABLE with rows.
 *          A reading aid for the tutorial page "widget-types", not a complete
 *          plugin: the functions below would be wired into an IcPlugin
 *          (on_widget_event) or called from init()/a Worker callback.
 *
 * @note    ASCII-only and C89 on purpose (VC6 / CP950 safe, and it must pass
 *          the syntax-only guard). The widget ids match table.nxlay.
 */

#include <string.h>

#include "nexus_plugin_api.h"

/** @brief The HostAPI table, saved by init() (not shown here). */
static const NxHostApi *s_host = (const NxHostApi *)0;

/** @brief widget id of the TABLE ([w.1] in table.nxlay). */
#define WID_REGS 1

/**
 * @brief  Appends one record of an NX_WP_ROWS blob: an NxRowHead, the name, the
 *         value, padded to a multiple of 4 bytes.
 * @return The offset just after the record.
 */
static NxDword add_row(unsigned char *buf, NxDword pos, const char *name,
                       const char *value, NxDword style)
{
    NxRowHead h;
    NxDword   rec;

    h.name_len  = (NxWord)strlen(name);
    h.value_len = (NxWord)strlen(value);
    h.style     = style;
    rec = ((NxDword)sizeof(h) + h.name_len + h.value_len + 3u) & ~3u;
    memset(buf + pos, 0, rec);
    memcpy(buf + pos, &h, sizeof(h));
    memcpy(buf + pos + sizeof(h), name, h.name_len);
    memcpy(buf + pos + sizeof(h) + h.name_len, value, h.value_len);
    return pos + rec;
}

/**
 * @brief  Replaces all rows of the list.
 * @return ERR_OK; ERR_FULL when the blob is over the capacity limit: the whole
 *         batch is refused, never truncated.
 * @note   Any thread. NX_WP_ROWS is a BLOB of row records (NxRowHead + name +
 *         value). Row data is state: it does not rebuild the panel, so calling
 *         this from a poll is fine. Scrolling belongs to the host.
 */
NxLong table_fill(NxPluginCtx ctx)
{
    unsigned char buf[2 * 32];
    NxPropVal     v;
    NxDword       pos;

    pos = add_row(buf, 0, "REG[00]", "0x1A", (NxDword)NX_COLOR_DEFAULT);
    pos = add_row(buf, pos, "REG[01]", "0xFF", (NxDword)NX_COLOR_WARN);
    memset(&v, 0, sizeof(v));
    v.SizeOfStruct = (NxDword)sizeof(v);
    v.kind         = (NxWord)NX_WKIND_BLOB;
    v.u.blob.ptr   = buf;
    v.u.blob.len   = pos;
    return s_host->SetWidgetProp(ctx, 0, WID_REGS, NX_WP_ROWS, &v);
}

常見錯誤

  • 高度=到下一列 y 的距離,宣告的 h 被忽略;最少 3 列(標題、分隔線、至少一列資料)。放不下 3 列就整個隱藏。 下一列緊接著排(例如 y 只差 1 或 2),清單就只剩標題,看起來像空的。範例的 TABLE 在 y=0,下一個 SEPARATOR 在 y=6,所以清單有 6 列高。 它是最後一列時,高度延伸到面板底。
  • NX_WP_ROWS 的列數或容量超過上限回 ERR_FULL;整批拒絕、舊資料保留、不會截斷,所以要看回傳值。
  • 每列的名稱與值各最多 255 位元組(NX_WTABLE_ROW_TEXT_MAX),超過同樣回 ERR_INVALID_PARAM;每面板的文字配額(512 KiB)以實際長度計,名稱與值各 255 位元組的列約放 1014 列;每筆記錄要補到 4 的倍數。
  • 列資料是狀態,不會使面板重建,可以從輪詢裡常常更新。

SEPARATOR:分隔線

用途:畫一條橫線分開上下兩塊。沒有互動、沒有值。

視窗版的 SEPARATOR
視窗版
終端機版的 SEPARATOR
終端機版

欄位與旗標:weight=1(或給 min_w)。編輯器:拖出 Separator,Toolbox 的預設已經有 Weight = 1。沒有 C 端程式,因為沒有任何執行期操作。

separator.nxlay下載 separator.nxlay
; separator.nxlay - a rule between two labels.
; A SEPARATOR needs weight (or min_w) to have any width.
[panel]
ref_w=40
ref_h=4

[w.1]
type=LABEL
y=0
min_w=20
label=Above

[w.2]
type=SEPARATOR
y=1
min_w=0
weight=1

[w.3]
type=LABEL
y=2
min_w=20
label=Below

常見錯誤

  • min_w = 0 又沒有 weight:寬度是 0,什麼都畫不出來。分隔線一定要給 weight=1。

METER:進度條

用途:把一個數值畫成填充的長條與百分比。對使用者唯讀。

視窗版的 METER
視窗版
終端機版的 METER
終端機版

欄位與旗標:num_min、num_max(滿刻度)、min_w、weight。ACTIVATABLE 選用(讓它可以點)。label 不畫。 編輯器:拖出 Progress(Toolbox 預設已填 NumMax = 100)→ 需要別的範圍就改 NumMin、NumMax。

; meter.nxlay - a read-only bar, num_max must not be 0.
[panel]
ref_w=40
ref_h=4

[w.1]
type=METER
y=0
min_w=20
weight=1
num_min=0
num_max=100
label=Loading
/**
 * @file    meter.c
 * @brief   Moving a METER bar.
 *          A reading aid for the tutorial page "widget-types", not a complete
 *          plugin: the functions below would be wired into an IcPlugin
 *          (on_widget_event) or called from init()/a Worker callback.
 *
 * @note    ASCII-only and C89 on purpose (VC6 / CP950 safe, and it must pass
 *          the syntax-only guard). The widget ids match meter.nxlay.
 */

#include <string.h>

#include "nexus_plugin_api.h"

/** @brief The HostAPI table, saved by init() (not shown here). */
static const NxHostApi *s_host = (const NxHostApi *)0;

/** @brief widget id of the METER ([w.1] in meter.nxlay). */
#define WID_LOADING 1

/**
 * @brief  Sets the bar; values outside num_min..num_max are clamped.
 * @param[in] percent 0..100 here because the declaration says num_max=100.
 * @note   Any thread. The bar is read-only for the user (no event), unless
 *         you declare flags=ACTIVATABLE.
 */
NxLong meter_set(NxPluginCtx ctx, NxDword percent)
{
    NxPropVal v;

    memset(&v, 0, sizeof(v));
    v.SizeOfStruct = (NxDword)sizeof(v);
    v.kind         = (NxWord)NX_WKIND_I64;
    v.u.i64        = (NxI64)percent;
    return s_host->SetWidgetProp(ctx, 0, WID_LOADING, NX_WP_VALUE, &v);
}

常見錯誤

  • num_max 一定要非 0,而且要大於 num_min(有號比較);否則 LoadLayout 回 ERR_INVALID_PARAM,編輯器的錯誤清單也會報。
  • 超出範圍的值會被夾進 num_min~num_max,不會出錯。
  • 想讓使用者調整數值請用 SPIN,不是 METER。

SPIN:數值微調

用途:讓使用者用上下按鈕(或方向鍵)調整一個整數。

視窗版的 SPIN
視窗版
終端機版的 SPIN
終端機版

欄位與旗標:num_min、num_max(必須非 0)、num_step(每次增減多少,0 視為 1)、min_w。label 不畫。 編輯器:拖出 Spin(預設 0~100、步進 1)→ 改 NumMin、NumMax、NumStep。

; spin.nxlay - an integer stepper, -10..10 in steps of 1.
[panel]
ref_w=40
ref_h=4

[w.1]
type=SPIN
y=0
min_w=14
num_min=-10
num_max=10
num_step=1
label=Gain
/**
 * @file    spin.c
 * @brief   Reading a SPIN as a signed number and setting it.
 *          A reading aid for the tutorial page "widget-types", not a complete
 *          plugin: the functions below would be wired into an IcPlugin
 *          (on_widget_event) or called from init()/a Worker callback.
 *
 * @note    ASCII-only and C89 on purpose (VC6 / CP950 safe, and it must pass
 *          the syntax-only guard). The widget ids match spin.nxlay.
 */

#include <string.h>

#include "nexus_plugin_api.h"

/** @brief The HostAPI table, saved by init() (not shown here). */
static const NxHostApi *s_host = (const NxHostApi *)0;

/** @brief widget id of the SPIN ([w.1] in spin.nxlay). */
#define WID_GAIN 1

/** @brief The last value the user chose. */
static long s_gain = 0;

/**
 * @brief  Widget event handler.
 * @note  evt->val is an NX_WKIND_I64: a SPIN value is already signed, no cast
 *         needed (the range here is -10..10).
 */
void NX_CALL spin_on_event(NxPluginCtx ctx, NxByte panel_id, NxWord widget_id,
                   const NxWidgetEvent *evt)
{
    NX_UNUSED_ARG(ctx);
    NX_UNUSED_ARG(panel_id);
    if (widget_id == WID_GAIN && evt->kind == NX_WEVT_CHANGED &&
        evt->val.kind == NX_WKIND_I64)
    {
        s_gain = (long)evt->val.u.i64;
    }
}

/**
 * @brief  Sets the spin; the value is clamped to num_min..num_max.
 */
NxLong spin_set(NxPluginCtx ctx, long gain)
{
    NxPropVal v;

    memset(&v, 0, sizeof(v));
    v.SizeOfStruct = (NxDword)sizeof(v);
    v.kind         = (NxWord)NX_WKIND_I64;
    v.u.i64        = (NxI64)gain;
    return s_host->SetWidgetProp(ctx, 0, WID_GAIN, NX_WP_VALUE, &v);
}

常見錯誤

  • 數值是有號的:evt->val.u.i64 的範圍可以是負的(例如 -10~10),不要先轉成無號數再運算。
  • num_max 為 0 或不大於 num_min 會被驗證擋下。
  • 超出範圍的值會被夾進範圍內。

CHOICE:單選群組

用途:從幾個選項裡選一個,全部選項同時看得見(選項少、要一眼看到全部時比 DROPDOWN 合適)。最多 8 個選項。

視窗版的 CHOICE
視窗版
終端機版的 CHOICE
終端機版

欄位與旗標:item_count(選項數)、min_w。選項文字由 NX_WP_ITEMS 設定(跟 DROPDOWN 共用同一個屬性)。label 不畫。 編輯器:拖出 Radio → 改 ItemCount。

; choice.nxlay - pick exactly one of 3 options; the options come from C.
[panel]
ref_w=40
ref_h=4

[w.1]
type=CHOICE
y=0
min_w=30
item_count=3
label=Speed
/**
 * @file    choice.c
 * @brief   Filling a CHOICE group and reading the chosen option.
 *          A reading aid for the tutorial page "widget-types", not a complete
 *          plugin: the functions below would be wired into an IcPlugin
 *          (on_widget_event) or called from init()/a Worker callback.
 *
 * @note    ASCII-only and C89 on purpose (VC6 / CP950 safe, and it must pass
 *          the syntax-only guard). The widget ids match choice.nxlay.
 */

#include <string.h>

#include "nexus_plugin_api.h"

/** @brief The HostAPI table, saved by init() (not shown here). */
static const NxHostApi *s_host = (const NxHostApi *)0;

/** @brief widget id of the CHOICE ([w.1] in choice.nxlay). */
#define WID_SPEED 1

/** @brief Option texts; a CHOICE group fills its options the same way as a DROPDOWN (NX_WP_ITEMS). */
static const char *const s_speeds[3] = { "Slow", "Normal", "Fast" };

/**
 * @brief  Call once from init() (UI Thread).
 * @return ERR_OK or an error code.
 * @note   NX_WP_ITEMS is a BLOB: one NxItemHead per item, followed by the item
 *         text, each record padded to a multiple of 4 bytes. At most 8 items.
 *         The value of the widget is the item INDEX. The host copies the blob
 *         during the call only.
 */
NxLong choice_setup(NxPluginCtx ctx)
{
    unsigned char buf[3 * 24];
    NxPropVal     v;
    NxItemHead    h;
    NxDword       pos = 0;
    NxDword       rec;
    int           i;

    for (i = 0; i < 3; i++)
    {
        h.text_len   = (NxWord)strlen(s_speeds[i]);
        h.detail_len = 0;
        h.item_id    = (NxDword)i;
        h.flags      = 0;
        rec = ((NxDword)sizeof(h) + h.text_len + 3u) & ~3u;
        memset(buf + pos, 0, rec);
        memcpy(buf + pos, &h, sizeof(h));
        memcpy(buf + pos + sizeof(h), s_speeds[i], h.text_len);
        pos += rec;
    }
    memset(&v, 0, sizeof(v));
    v.SizeOfStruct = (NxDword)sizeof(v);
    v.kind         = (NxWord)NX_WKIND_BLOB;
    v.u.blob.ptr   = buf;
    v.u.blob.len   = pos;
    return s_host->SetWidgetProp(ctx, 0, WID_SPEED, NX_WP_ITEMS, &v);
}

/**
 * @brief  Widget event handler: evt->val.u.i64 is the chosen option (0-based).
 */
void NX_CALL choice_on_event(NxPluginCtx ctx, NxByte panel_id, NxWord widget_id,
                    const NxWidgetEvent *evt)
{
    NX_UNUSED_ARG(panel_id);
    if (widget_id == WID_SPEED && evt->kind == NX_WEVT_CHANGED &&
        evt->val.kind == NX_WKIND_I64 && evt->val.u.i64 >= 0 && evt->val.u.i64 < 3)
    {
        s_host->LogWrite(ctx, NX_LOG_INFO, s_speeds[(int)evt->val.u.i64]);
    }
}

/**
 * @brief  Selects an option; index must be smaller than the number of options.
 */
NxLong choice_select(NxPluginCtx ctx, NxDword index)
{
    NxPropVal v;

    memset(&v, 0, sizeof(v));
    v.SizeOfStruct = (NxDword)sizeof(v);
    v.kind         = (NxWord)NX_WKIND_I64;
    v.u.i64        = (NxI64)index;
    return s_host->SetWidgetProp(ctx, 0, WID_SPEED, NX_WP_VALUE, &v);
}

常見錯誤

  • min_w 要夠寬。 選項一多、min_w 太小,視窗版的選項標籤會被下一個圓鈕蓋住(上面截圖裡的 Normal 就被蓋掉一個字), 終端機版則把後面的選項截成 …。選項文字要短,min_w 至少要放得下所有選項並排的寬度。
  • 值(索引)要小於宣告的 item_count(比 DROPDOWN 嚴格),否則回 ERR_INVALID_PARAM。
  • 忘了設 NX_WP_ITEMS:只有圓鈕,沒有文字。

GROUPBOX:群組框

用途:用一個帶標題的框把相關的控制項視覺上圍在一起。沒有互動、沒有值。

視窗版的 GROUPBOX
視窗版:外框是很淡的閒置邊框色,在截圖裡幾乎看不出來,標題「Extras」嵌在上緣
終端機版的 GROUPBOX
終端機版

欄位與旗標:label(框的標題)、min_w、h(一定要給,涵蓋要圍住的列數)、flags=DECORATION。 編輯器:拖出 Group Box(Toolbox 預設 H = 5 且 Decoration 已勾)→ 調 H 使它涵蓋要框的列數;被框住的控制項放在它上面的列裡。沒有 C 端程式。

groupbox.nxlay下載 groupbox.nxlay
; groupbox.nxlay - a frame around other widgets.
; h is mandatory. flags=DECORATION lets it overlap the widgets it frames.
[panel]
ref_w=40
ref_h=6

[w.1]
type=GROUPBOX
y=0
min_w=30
h=3
flags=DECORATION
label=Output

[w.2]
type=CHECKBOX
y=1
x=2
min_w=16
label=Enable

常見錯誤

  • h = 0 會被拒絕(規則 10),GROUPBOX 必須明確給高度。
  • 要圍住別的控制項就必須設 DECORATION,否則群組框與框內控制項在垂直方向重疊,會被「跨列不可重疊」規則擋下(規則 4),整份宣告都被拒絕。
  • 群組框本身不能聚焦;DECORATION 讓它畫在其他控制項的下面。
  • 兩個版本外觀不同:終端機版是圓角框加標題,視窗版是一圈細框線,標題嵌在上緣,顏色是很淡的閒置邊框色,不像終端機版那麼醒目。框裡的 widget(SPIN、CHOICE 等)畫在框的上面。

CHART:簡易長條圖

用途:把一串數值畫成一列高低不同的長條(趨勢圖)。目前只有終端機版畫得出來,視窗版沒有圖表繪製器,只會顯示 [?]。 所以要在兩個版本都看得到,請改用 METER/VALUE,或用 Canvas 模式自己畫。

視窗版的 CHART,只顯示 [?]
視窗版:沒有圖表繪製器,顯示 [?]
終端機版的 CHART
終端機版

欄位與旗標:num_min、num_max(縱軸範圍。num_max 非 0 時必須大於 num_min,否則宣告被驗證拒絕;num_max 留 0 雖然通得過驗證,但所有長條都會畫成最低一階,所以一定要填)、min_w、h(h 至少 2 列且 label 非空才會畫標題)。 編輯器:拖出 Chart(Toolbox 預設 H = 4、範圍 0~100)。Inspector 對 CHART 只有 H 與 Activatable,沒有 NumMin/NumMax/Label; 要改範圍或標題,用文字編輯器直接改 .nxlay(編輯器重新存檔時會保留這些欄位)。

; chart.nxlay - a small bar chart; the samples come from C (0..65535).
; num_min/num_max define the vertical range.
[panel]
ref_w=40
ref_h=6

[w.1]
type=CHART
y=0
min_w=24
weight=1
h=3
num_min=0
num_max=100
label=Signal
/**
 * @file    chart.c
 * @brief   Feeding a CHART with samples.
 *          A reading aid for the tutorial page "widget-types", not a complete
 *          plugin: the functions below would be wired into an IcPlugin
 *          (on_widget_event) or called from init()/a Worker callback.
 *
 * @note    ASCII-only and C89 on purpose (VC6 / CP950 safe, and it must pass
 *          the syntax-only guard). The widget ids match chart.nxlay.
 */

#include <string.h>

#include "nexus_plugin_api.h"

/** @brief The HostAPI table, saved by init() (not shown here). */
static const NxHostApi *s_host = (const NxHostApi *)0;

/** @brief widget id of the CHART ([w.1] in chart.nxlay). */
#define WID_SIGNAL 1

/**
 * @brief  Replaces the samples (0..65535 each, at most 256).
 * @param[in] samples Array of count values; NULL clears the chart.
 * @param[in] count   Number of samples; more than 256 refuses the whole batch.
 * @return ERR_OK, ERR_NOT_FOUND, or ERR_FULL.
 * @note   NX_WP_SERIES is a BLOB: an NxSeriesHead (format, count) followed by
 *         the samples as 16-bit little-endian values, written byte by byte so
 *         it does not depend on the host's byte order. Samples are state and do
 *         not rebuild the panel. The bars are scaled by the declared
 *         num_min..num_max.
 */
NxLong chart_feed(NxPluginCtx ctx, const NxWord *samples, NxWord count)
{
    unsigned char buf[4 + 2 * NX_WSERIES_MAX_SAMPLES];
    NxPropVal     v;
    NxWord        i;

    if (samples == (const NxWord *)0)
    {
        count = 0;
    }
    if (count > NX_WSERIES_MAX_SAMPLES)
    {
        return ERR_FULL;
    }
    buf[0] = (unsigned char)NX_WSERIES_FMT_U16;
    buf[1] = 0;
    buf[2] = (unsigned char)(count & 0xFF);
    buf[3] = (unsigned char)((count >> 8) & 0xFF);
    for (i = 0; i < count; i++)
    {
        buf[4 + 2 * i]     = (unsigned char)(samples[i] & 0xFF);
        buf[4 + 2 * i + 1] = (unsigned char)((samples[i] >> 8) & 0xFF);
    }
    memset(&v, 0, sizeof(v));
    v.SizeOfStruct = (NxDword)sizeof(v);
    v.kind         = (NxWord)NX_WKIND_BLOB;
    v.u.blob.ptr   = buf;
    v.u.blob.len   = (count == 0) ? 0 : (NxDword)(4 + 2 * count);   /* zero length = no samples */
    return s_host->SetWidgetProp(ctx, 0, WID_SIGNAL, NX_WP_SERIES, &v);
}

常見錯誤

  • 樣本是 16 位元(0~65535),最多 256 個,以 NX_WP_SERIES blob(NxSeriesHead 加小端樣本)寫入;超過整批拒絕(ERR_FULL)。
  • 樣本數多於可用欄數時,終端機版等距取樣,頭尾都會畫到。
  • 視窗版畫不出來時,資料照樣寫得進去,只是畫面是 [?],也沒有任何錯誤碼告訴你(4.0 起沒有 Chart 專用的能力位元可查)。
  • 樣本是狀態,不會使面板重建。

IMAGE:點陣圖

用途:顯示一張圖(logo、示意圖)。視窗版載入並拉伸到控制項的矩形;終端機版一律只畫 [image: 檔名] 這樣的占位文字。

視窗版的 IMAGE,找不到圖時的占位文字
視窗版:載不到圖時就畫占位文字
終端機版的 IMAGE
終端機版:永遠是占位文字

欄位與旗標:aux(圖檔名,BMP 或 ICO)、min_w、h。ACTIVATABLE 選用。label 不畫。 編輯器:拖出 Image(預設 Aux = image.bmp、H = 3)→ 改 Aux、H。沒有 C 端程式:圖檔在面板建立時載入。

; image.nxlay - a bitmap. aux is a plain file name (BMP or ICO) that lives
; next to the plugin DLL; no path separators, no "..".
[panel]
ref_w=40
ref_h=6

[w.1]
type=IMAGE
y=0
min_w=10
h=3
aux=logo.bmp

常見錯誤

  • 圖檔要放在 Plugin DLL 所在的資料夾,aux 只寫檔名。aux 裡不能有 \、/、: 或 ..,否則整份宣告被拒絕(規則 10)。
  • 只支援 BMP 與 ICO。載入失敗(找不到或格式不對)時畫占位文字,並在 Log 記一則警告 IMAGE widget N: could not load ...,面板照常運作。
  • 終端機版永遠看不到圖,所以圖不能是唯一的資訊來源。

FILEPICK:檔案選擇

用途:讓使用者選一個檔案。視窗版是一個輸入框加「Browse…」按鈕,按下開標準的開檔對話框(只能選存在的檔案,篩選條件來自 aux); 終端機版沒有對話框,退化成一個文字框,使用者直接打路徑。

視窗版的 FILEPICK
視窗版
終端機版的 FILEPICK
終端機版

欄位與旗標:aux(篩選字串,例如 *.bin)、min_w、weight。label 不畫。 編輯器:拖出 File Picker(預設 Aux = *.txt)→ 改 Aux。

filepick.nxlay下載 filepick.nxlay
; filepick.nxlay - a file chooser; aux is the filter shown in the dialog.
[panel]
ref_w=40
ref_h=4

[w.1]
type=FILEPICK
y=0
min_w=24
weight=1
aux=*.bin
label=Firmware
/**
 * @file    filepick.c
 * @brief   Reading the path a user picked in a FILEPICK.
 *          A reading aid for the tutorial page "widget-types", not a complete
 *          plugin: the functions below would be wired into an IcPlugin
 *          (on_widget_event) or called from init()/a Worker callback.
 *
 * @note    ASCII-only and C89 on purpose (VC6 / CP950 safe, and it must pass
 *          the syntax-only guard). The widget ids match filepick.nxlay.
 */

#include <string.h>

#include "nexus_plugin_api.h"

/** @brief The HostAPI table, saved by init() (not shown here). */
static const NxHostApi *s_host = (const NxHostApi *)0;

/** @brief widget id of the FILEPICK ([w.1] in filepick.nxlay). */
#define WID_FIRMWARE 1

/**
 * @brief  Reads the currently chosen path.
 * @param[in]  ctx  This mount's context.
 * @param[out] path Receives the path (empty string when nothing was chosen).
 * @param[in]  cap  Capacity of path; 264 bytes always fit (the path is at most 263 bytes plus the NUL).
 * @return ERR_OK; ERR_BUFFER_TOO_SMALL; ERR_NOT_FOUND when the widget does not exist.
 * @note   The path is the VALUE of the widget (a string). Picking a file also
 *         sends NX_WEVT_CHANGED, but reading it on demand, for example in the
 *         handler of a "Start" button, is just as good.
 */
NxLong filepick_path(NxPluginCtx ctx, char *path, NxDword cap)
{
    NxPropVal out;
    NxDword   needed = 0;

    memset(&out, 0, sizeof(out));
    out.SizeOfStruct = (NxDword)sizeof(out);
    return s_host->GetWidgetProp(ctx, 0, WID_FIRMWARE, NX_WP_VALUE, &out, path, cap, &needed);
}

/**
 * @brief  Presets the path shown in the box (e.g. the last used file).
 */
NxLong filepick_preset(NxPluginCtx ctx, const char *path)
{
    NxPropVal v;

    memset(&v, 0, sizeof(v));
    v.SizeOfStruct = (NxDword)sizeof(v);
    v.kind         = (NxWord)NX_WKIND_STR;
    v.u.str.ptr    = path;
    v.u.str.len    = (NxDword)strlen(path);
    return s_host->SetWidgetProp(ctx, 0, WID_FIRMWARE, NX_WP_VALUE, &v);
}

常見錯誤

  • 路徑就是控制項的值(字串):選了檔案會送 NX_WEVT_CHANGED,也可以在需要的時候(例如「開始」按鈕的處理函式裡)用 GetWidgetProp(…, NX_WP_VALUE, …) 讀。
  • 路徑最多 263 位元組。GetWidgetProp 的緩衝區用 264 位元組就一定放得下;太小回 ERR_BUFFER_TOO_SMALL,needed 會告訴你需要多少。
  • 沒選過檔案時讀到空字串,不是錯誤。
  • aux 也受規則 10 限制:不能含 \、/、:、..。

接下來