2626#include < QPushButton>
2727#include < QSpinBox>
2828#include < QToolButton>
29+ #include < QToolTip>
2930#include < QVBoxLayout>
3031#include < QWheelEvent>
3132
@@ -55,6 +56,66 @@ bool matchesFloor(const std::string& elementFloorId, const QString& floorId) {
5556 return floorId.isEmpty () || elementFloorId.empty () || QString::fromStdString (elementFloorId) == floorId;
5657}
5758
59+ QString formatConnectionBlockTooltip (const safecrowd::domain::ConnectionBlockDraft& block) {
60+ if (block.connectionId .empty ()) {
61+ return {};
62+ }
63+
64+ QString text = QStringLiteral (" Block schedule" );
65+ if (block.intervals .empty ()) {
66+ text.append (" \n - Always" );
67+ return text;
68+ }
69+
70+ for (const auto & interval : block.intervals ) {
71+ const auto start = std::max (0.0 , interval.startSeconds );
72+ const auto end = std::max (start, interval.endSeconds );
73+ text.append (QString (" \n - %1s ~ %2s" ).arg (start, 0 , ' f' , 1 ).arg (end, 0 , ' f' , 1 ));
74+ }
75+ return text;
76+ }
77+
78+ std::optional<std::size_t > hoveredConnectionBlockIndex (
79+ const safecrowd::domain::FacilityLayout2D& layout,
80+ const std::vector<safecrowd::domain::ConnectionBlockDraft>& blocks,
81+ const LayoutCanvasTransform& transform,
82+ const QString& currentFloorId,
83+ const QPointF& screenPosition) {
84+ constexpr double kHoverRadiusPixels = 14.0 ;
85+
86+ std::optional<std::size_t > closestIndex;
87+ double closestDistanceSq = kHoverRadiusPixels * kHoverRadiusPixels ;
88+
89+ for (std::size_t index = 0 ; index < blocks.size (); ++index) {
90+ const auto & block = blocks[index];
91+ if (block.connectionId .empty ()) {
92+ continue ;
93+ }
94+
95+ const auto it = std::find_if (layout.connections .begin (), layout.connections .end (), [&](const auto & connection) {
96+ return connection.id == block.connectionId ;
97+ });
98+ if (it == layout.connections .end ()) {
99+ continue ;
100+ }
101+ if (!matchesFloor (it->floorId , currentFloorId)) {
102+ continue ;
103+ }
104+
105+ const auto center = transform.map ({.x = (it->centerSpan .start .x + it->centerSpan .end .x ) * 0.5 ,
106+ .y = (it->centerSpan .start .y + it->centerSpan .end .y ) * 0.5 });
107+ const auto dx = center.x () - screenPosition.x ();
108+ const auto dy = center.y () - screenPosition.y ();
109+ const auto distanceSq = (dx * dx) + (dy * dy);
110+ if (distanceSq <= closestDistanceSq) {
111+ closestDistanceSq = distanceSq;
112+ closestIndex = index;
113+ }
114+ }
115+
116+ return closestIndex;
117+ }
118+
58119QString defaultFloorId (const safecrowd::domain::FacilityLayout2D& layout) {
59120 if (!layout.floors .empty () && !layout.floors .front ().id .empty ()) {
60121 return QString::fromStdString (layout.floors .front ().id );
@@ -699,6 +760,12 @@ void ScenarioCanvasWidget::keyReleaseEvent(QKeyEvent* event) {
699760 QWidget::keyReleaseEvent (event);
700761}
701762
763+ void ScenarioCanvasWidget::leaveEvent (QEvent* event) {
764+ hoveredConnectionBlockId_.clear ();
765+ QToolTip::hideText ();
766+ QWidget::leaveEvent (event);
767+ }
768+
702769void ScenarioCanvasWidget::mouseDoubleClickEvent (QMouseEvent* event) {
703770 if (event->button () == Qt::LeftButton) {
704771 camera_.reset ();
@@ -716,17 +783,46 @@ void ScenarioCanvasWidget::mouseMoveEvent(QMouseEvent* event) {
716783 }
717784
718785 if (dragging_) {
786+ if (!hoveredConnectionBlockId_.isEmpty ()) {
787+ hoveredConnectionBlockId_.clear ();
788+ QToolTip::hideText ();
789+ }
719790 dragCurrent_ = event->position ();
720791 update ();
721792 event->accept ();
722793 return ;
723794 }
724795 if (selectionDragging_) {
796+ if (!hoveredConnectionBlockId_.isEmpty ()) {
797+ hoveredConnectionBlockId_.clear ();
798+ QToolTip::hideText ();
799+ }
725800 selectionDragCurrent_ = event->position ();
726801 update ();
727802 event->accept ();
728803 return ;
729804 }
805+
806+ if (const auto bounds = collectBounds (); bounds.has_value ()) {
807+ const auto transform = currentTransform (*bounds);
808+ const auto hoveredIndex = hoveredConnectionBlockIndex (layout_, connectionBlocks_, transform, currentFloorId_, event->position ());
809+ if (!hoveredIndex.has_value ()) {
810+ if (!hoveredConnectionBlockId_.isEmpty ()) {
811+ hoveredConnectionBlockId_.clear ();
812+ QToolTip::hideText ();
813+ }
814+ } else {
815+ const auto & block = connectionBlocks_[*hoveredIndex];
816+ const auto tooltip = formatConnectionBlockTooltip (block);
817+ if (!tooltip.isEmpty ()) {
818+ const auto hoveredId = QString::fromStdString (block.id .empty () ? block.connectionId : block.id );
819+ if (hoveredId != hoveredConnectionBlockId_) {
820+ hoveredConnectionBlockId_ = hoveredId;
821+ QToolTip::showText (event->globalPosition ().toPoint (), tooltip, this );
822+ }
823+ }
824+ }
825+ }
730826 QWidget::mouseMoveEvent (event);
731827}
732828
0 commit comments