Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ endif()
if(PJ_INSTALL_SDK)
include(CMakePackageConfigHelpers)

set(PJ_PACKAGE_VERSION "0.13.0")
set(PJ_PACKAGE_VERSION "0.14.0")
set(PJ_PACKAGE_CMAKE_DIR ${CMAKE_INSTALL_LIBDIR}/cmake/plotjuggler_sdk)

install(EXPORT plotjuggler_sdkTargets
Expand Down
14 changes: 7 additions & 7 deletions conanfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
plugin_sdk — umbrella for plugin authors (base + dialog SDK + parser SDK)
plugin_host — umbrella for host loaders (data_source/parser/toolbox/dialog)

A consuming Conan recipe declares e.g. `plotjuggler_sdk/0.13.0` and then:
A consuming Conan recipe declares e.g. `plotjuggler_sdk/0.14.0` and then:

find_package(plotjuggler_sdk REQUIRED COMPONENTS plugin_sdk)
target_link_libraries(my_plugin PRIVATE plotjuggler_sdk::plugin_sdk)
Expand All @@ -30,15 +30,15 @@

class PlotjugglerSdkConan(ConanFile):
name = "plotjuggler_sdk"
# UNRELEASED BREAK: 0.13.0 unifies markers + transforms into the single host
# UNRELEASED BREAK: 0.13.0 unified markers + transforms into the single host
# service `pj.data_processors.v1` via a `kind` discriminator (removed the old
# `pj.markers.v1` and the interim `pj.generators.v1`; generalized
# `create_data_processor`/`validate_data_processor_script` with kind/language/flags).
# This is an ABI/API change — normally MAJOR — but no PUBLIC tag ever shipped
# `pj.data_processors.v1`, so no released plugin breaks and 0.13.0 stays a valid
# pre-1.0 step. The FIRST public release that carries the unified
# `pj.data_processors.v1` MUST be tagged 1.0.0. See CHANGELOG.md.
version = "0.13.0"
# 0.14.0 then adds the dialog-protocol additions (radio column + interactive
# sub-panel). All pre-1.0 unreleased — no PUBLIC tag ever shipped
# `pj.data_processors.v1`, so no released plugin breaks. The FIRST public release
# that carries the unified `pj.data_processors.v1` MUST be tagged 1.0.0. See CHANGELOG.md.
version = "0.14.0"
# Apache-2.0 covers the whole SDK (pj_base + pj_plugins). See LICENSE.
license = "Apache-2.0"
url = "https://github.com/PlotJuggler/plotjuggler_sdk"
Expand Down
4 changes: 3 additions & 1 deletion docs/dialog-sdk-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ For the full tutorial, see [dialog-plugin-guide.md](../pj_plugins/docs/dialog-pl
| `setTableRows(name, vector<vector<string>>)` | Set row data |
| `setSelectedRows(name, vector<int>)` | Set selected row indices |
| `setDisabledRows(name, vector<int>)` | Grey out rows (non-selectable) |
| `setTableRadioColumn(name, column, checked_row)` | Render `column` as an exclusive radio group; `checked_row` is selected (-1 = none). Fires `onTableRadioSelected`. |

### QFrame Chart Container

Expand Down Expand Up @@ -152,8 +153,9 @@ Override these in your `DialogPluginTyped` subclass. Return `true` when state ch
| `onClicked(name)` | QPushButton | (no payload) |
| `onFileSelected(name, path)` | QPushButton (file picker or save-file picker) | Selected file path |
| `onFolderSelected(name, path)` | QPushButton (folder picker) | Selected folder path |
| `onSelectionChanged(name, items)` | QListWidget, QTableWidget | Vector of selected item texts |
| `onSelectionChanged(name, items)` | QListWidget, QTableWidget | Vector of selected item texts (table: column-0 text) |
| `onItemDoubleClicked(name, index)` | QListWidget, QTableWidget | Row index of double-clicked item |
| `onTableRadioSelected(name, row)` | QTableWidget radio column | Row whose radio was clicked (see `setTableRadioColumn`) |
| `onCodeChanged(name, code)` | QPlainTextEdit code editor | Edited code |
| `onCodeChangedWithCursor(name, code, cursor)` | QPlainTextEdit code editor | Edited code + caret offset (`cursor < 0` when no opt-in / not reported); defaults to `onCodeChanged` |
| `onItemsDropped(name, items)` | Any widget with `setDropTarget` | Dropped item labels |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,15 @@ class WidgetDataView {
return result;
}

/// Column to render as an exclusive radio-button group (see setTableRadioColumn).
[[nodiscard]] std::optional<int> tableRadioColumn(std::string_view name) const {
return getInt(name, "radio_column");
}
/// Row whose radio is checked in the radio column (-1 = none).
[[nodiscard]] std::optional<int> tableRadioCheckedRow(std::string_view name) const {
return getInt(name, "radio_checked_row");
}

[[nodiscard]] std::optional<std::vector<int>> selectedRows(std::string_view name) const {
const nlohmann::json* w = widget(name);
if (!w) {
Expand Down Expand Up @@ -534,6 +543,27 @@ class WidgetDataView {
return ui_it->get<std::string>();
}

/// Returns the interactive sub-panel UI XML if requestSubPanel was called, or
/// nullopt. The sub-panel is live (events flow back to the plugin); see
/// WidgetData::requestSubPanel. Observed by PanelEngine.
[[nodiscard]] std::optional<std::string> subPanelUi() const {
auto it = data_.find("__request_sub_panel");
if (it == data_.end() || !it->is_object()) {
return std::nullopt;
}
auto ui_it = it->find("ui");
if (ui_it == it->end() || !ui_it->is_string()) {
return std::nullopt;
}
return ui_it->get<std::string>();
}

/// True if WidgetData::closeSubPanel was called (dismiss the interactive sub-panel).
[[nodiscard]] bool subPanelClose() const {
auto it = data_.find("__request_sub_panel_close");
return it != data_.end() && it->is_boolean() && it->get<bool>();
}

/// Returns the close-reason string if WidgetData::requestClose was called, or
/// nullopt. Observed by PanelEngine; ignored by DialogEngine.
[[nodiscard]] std::optional<std::string> requestClose() const {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,13 @@ struct WidgetEventBuilder {
return j.dump();
}

/// QTableWidget: a radio button in the radio column was selected (row index).
[[nodiscard]] static std::string tableRadioSelected(int row) {
nlohmann::json j;
j["table_radio_row"] = row;
return j.dump();
}

/// Code editor: code changed. `cursor` is the caret offset (bytes) in the new
/// text, or negative when unknown; it is serialized only when >= 0, so callers
/// that omit it stay wire-compatible with readers that ignore the field.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,12 @@ class DialogPluginTyped : public DialogPluginBase {
return false;
}

/// QTableWidget: a radio button in the table's radio column was selected
/// (see WidgetData::setTableRadioColumn). `row` is the newly-checked row.
virtual bool onTableRadioSelected(std::string_view /*widget_name*/, int /*row*/) {
return false;
}

virtual bool onCodeChanged(std::string_view /*widget_name*/, std::string_view /*code*/) {
return false;
}
Expand Down Expand Up @@ -168,6 +174,9 @@ class DialogPluginTyped : public DialogPluginBase {
if (auto v = event.headerSection()) {
return onHeaderClicked(widget_name, *v);
}
if (auto v = event.tableRadioRow()) {
return onTableRadioSelected(widget_name, *v);
}
// value: try int first, then double
if (auto v = event.valueInt()) {
return onValueChanged(widget_name, *v);
Expand Down
33 changes: 33 additions & 0 deletions pj_plugins/dialog_protocol/include/pj_plugins/sdk/widget_data.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,18 @@ class WidgetData {
return *this;
}

/// Render column `column` of a QTableWidget as an exclusive radio-button group:
/// one QRadioButton per row, only one on at a time. `checked_row` is the row
/// whose radio is selected (-1 for none). The cells in `column` carry the radio
/// instead of text. Clicking a radio fires onTableRadioSelected(name, row).
/// Re-send on every build to keep the checked row in sync.
WidgetData& setTableRadioColumn(std::string_view name, int column, int checked_row) {
auto& e = entry(name);
e["radio_column"] = column;
e["radio_checked_row"] = checked_row;
return *this;
}

// --- Chart (QFrame used as chart container) ---

/// Set chart series data on a QFrame widget. The host creates or updates a Qwt
Expand Down Expand Up @@ -613,6 +625,27 @@ class WidgetData {
return *this;
}

/// Request that the host open an INTERACTIVE sub-panel with the given UI XML.
/// Unlike requestSubDialog (a one-shot modal that only harvests inputs on OK),
/// the sub-panel is a live, non-blocking child: its widget events flow back to
/// the plugin through the normal handlers (onTextChanged / onSelectionChanged /
/// onItemDoubleClicked / onClicked, keyed by the sub-panel widgets' objectNames),
/// and the plugin keeps pushing WidgetData to it every tick (so previews/lists
/// update live). The host emits a synthetic onClicked("subPanelClosed") when the
/// user dismisses it. Send closeSubPanel() to dismiss it programmatically.
/// Only one sub-panel is open at a time; re-requesting while one is open is ignored.
WidgetData& requestSubPanel(std::string_view ui_xml) {
data_["__request_sub_panel"] = nlohmann::json{{"ui", ui_xml}};
return *this;
}

/// Request that the host close the interactive sub-panel opened by requestSubPanel
/// (e.g. after the user picked an item). No-op if none is open.
WidgetData& closeSubPanel() {
data_["__request_sub_panel_close"] = true;
return *this;
}

/// Request that the host close the panel hosting this plugin (PanelEngine).
/// `reason` is a free-form plugin-defined string (e.g. "import_complete",
/// "user_back", "error") forwarded to the host's onCloseRequested callback.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,11 @@ class WidgetEvent {
return getInt("header_section");
}

/// QTableWidget: a radio button in the radio column was selected (row index)
std::optional<int> tableRadioRow() const {
return getInt("table_radio_row");
}

/// Code editor: code changed
std::optional<std::string> codeChanged() const {
return getString("code_changed");
Expand Down
2 changes: 1 addition & 1 deletion recipe.yaml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
schema_version: 1

context:
version: "0.13.0"
version: "0.14.0"

package:
name: plotjuggler_sdk
Expand Down
Loading