#ifndef XTC_GRAPH_WIDGET_HPP #define XTC_GRAPH_WIDGET_HPP #include #include #include "xtc/xtc_engine.h" class Fl_Box; /* GUI positions/selection only. Domain snapshots and edits stay in the C API. */ class GraphWidget : public Fl_Widget { public: /** * @brief Applies a graph edit using an opaque context and optional added-node output. * * The handler receives the context, edit command, and output identifier in that order. * @return XTC_OK on success, or an edit error. */ using EditHandler = xtc_result (*)(void *, const xtc_graph_edit &, size_t *); /** * @brief Constructs a graph widget bound to a graph and an edit callback. * * The widget stores the graph, handler, context, and feedback box without * taking ownership of any of them. * * @param x Left coordinate in FLTK screen space. * @param y Top coordinate in FLTK screen space. * @param width Widget width in pixels. * @param height Widget height in pixels. * @param graph Borrowed graph snapshot; must outlive the widget's use of it. * @param handler Edit callback invoked for every graph mutation. * @param context Opaque pointer passed back to @p handler; may be NULL. * @param feedback Borrowed label used to report edit results; must outlive the widget. */ GraphWidget(int x, int y, int width, int height, const xtc_graph *graph, EditHandler handler, void *context, Fl_Box *feedback); /** * @brief Clears cached node positions, selection, and interaction state. * * Does not change the borrowed graph pointer or the graph's own layout. */ void reset_layout() { count_ = 0; selected_node_ = SIZE_MAX; edge_selected_ = dragging_ = wiring_ = false; } /** * @brief Replaces the graph snapshot and rebuilds cached node positions. * * Existing positions are preserved by node ID; new nodes are placed on a grid * that avoids overlapping previously known nodes. Selections that no longer * exist are cleared. The widget does not take ownership of @p graph. * * @param graph Borrowed graph that must outlive the widget's use of it. */ void set_graph(const xtc_graph *graph); /** * @brief Returns the graph snapshot currently displayed. * * The returned pointer is borrowed and valid for GUI thread use only. * * @return Borrowed graph pointer; may be NULL when none was supplied. */ const xtc_graph *graph() const { return graph_; } // Borrowed, GUI thread only. /** * @brief Requests creation of a node through the edit handler. * * Selects the new node when the edit succeeds. * * @param type Kind of node to add. * @return Result reported by the edit handler. */ xtc_result add_node(xtc_node_type type); /** * @brief Requests a connection between two ports through the edit handler. * * @param connection Connection describing the source and destination node/port pairs. * @return Result reported by the edit handler. */ xtc_result connect_ports(const xtc_connection_info &connection); /** * @brief Requests removal of the selected wire or node through the edit handler. * * @return Result reported by the edit handler, or XTC_ERR_INVALID_STATE when * nothing is selected. */ xtc_result delete_selected(); /** * @brief Selects a node by ID and clears any wire selection. * * @param id Stable node identifier. * @return True when the node has a cached position, false otherwise. */ bool select_node(size_t id); /** * @brief Selects a wire between two nodes and clears the node selection. * * @param source Source node identifier. * @param destination Destination node identifier. * @return True when a matching connection exists, false otherwise. */ bool select_connection(size_t source, size_t destination); /** * @brief Moves a node within the widget, clamped to the visible bounds. * * @param id Stable node identifier. * @param left Requested left position in widget-local coordinates. * @param top Requested top position in widget-local coordinates. * @return True when the node has a cached position, false otherwise. */ bool move_node(size_t id, int left, int top); /** * @brief Looks up the cached position of a node. * * @param id Stable node identifier. * @param left Receives the left position in widget-local coordinates. * @param top Receives the top position in widget-local coordinates. * @return True when the node has a cached position, false otherwise. */ bool node_position(size_t id, int &left, int &top) const; /** * @brief Handles mouse and keyboard events for selection, dragging, and wiring. * * @param event FLTK event code. * @return 1 when the event was consumed, 0 for unsupported left-button input, * or the result of Fl_Widget::handle() for unhandled events. */ int handle(int event) override; protected: /** * @brief Draws the background, wires, nodes, ports, and any pending wire. */ void draw() override; private: struct Position { size_t id = SIZE_MAX; int left = 0, top = 0; }; std::array positions_{}; size_t count_ = 0U; const xtc_graph *graph_; EditHandler handler_; void *context_; Fl_Box *feedback_; size_t selected_node_ = SIZE_MAX; xtc_connection_info selected_edge_{}; bool edge_selected_ = false; bool dragging_ = false, wiring_ = false; size_t wire_node_ = SIZE_MAX, wire_port_ = 0U; int drag_x_ = 0, drag_y_ = 0, mouse_x_ = 0, mouse_y_ = 0; /** * @brief Shows the outcome of an edit in the feedback label. * * @param result Result returned by the edit handler. * @param success Text shown when @p result is XTC_OK; must be valid then and is ignored otherwise. */ void feedback(xtc_result result, const char *success); /** * @brief Finds the port nearest to a widget-local point. * * Searches nodes in reverse draw order and accepts points within 9 pixels of a * port center. * * @param left Widget-local x coordinate to test. * @param top Widget-local y coordinate to test. * @param node Receives the matching node ID when true is returned. * @param port Receives the matching port index (0 input, 1 output) when true is returned. * @return True when a port is within range, false otherwise. */ bool hit_port(int left, int top, size_t &node, size_t &port) const; /** * @brief Computes the widget-local center of a node port. * * @param id Stable node identifier. * @param port Port index; 0 selects the input and 1 the output. * @param left Receives the port center x coordinate. * @param top Receives the port center y coordinate. * @return True when the node and port exist, false otherwise. */ bool port_position(size_t id, size_t port, int &left, int &top) const; }; #endif