Loading...
Searching...
No Matches
TextBox.hpp
1#pragma once
2#include <Process/Commands/Properties.hpp>
3#include <Process/Commands/SetControlValue.hpp>
4#include <Process/Dataflow/Port.hpp>
5#include <Process/Dataflow/PortFactory.hpp>
6#include <Process/Dataflow/PortItem.hpp>
7#include <Process/Process.hpp>
8
9#include <Effect/EffectLayer.hpp>
10#include <Effect/EffectLayout.hpp>
11
12#include <score/application/ApplicationContext.hpp>
13#include <score/command/Dispatchers/CommandDispatcher.hpp>
14#include <score/command/Dispatchers/SingleOngoingCommandDispatcher.hpp>
15#include <score/command/PropertyCommand.hpp>
16#include <score/graphics/TextItem.hpp>
17#include <score/model/Skin.hpp>
18
19#include <ossia/network/value/format_value.hpp>
20#include <ossia/network/value/value_conversion.hpp>
21
22#include <QApplication>
23#include <QClipboard>
24#include <QElapsedTimer>
25#include <QMenu>
26#include <QPainter>
27#include <QTextCursor>
28#include <QTextDocument>
29#include <QTextOption>
30
31#include <halp/controls.hpp>
32#include <halp/meta.hpp>
33namespace Ui::TextBox
34{
49inline void setupDocument(QTextDocument& doc, double width)
50{
51 QTextOption opt = doc.defaultTextOption();
52 opt.setFlags(opt.flags() | QTextOption::IncludeTrailingSpaces);
53 doc.setDefaultTextOption(opt);
54 doc.setTextWidth(width > 0. ? width : -1.);
55}
56
57struct Node
58{
59 halp_meta(name, "Text Box")
60 halp_meta(c_name, "Display")
61 halp_meta(category, "Control/Basic")
62 halp_meta(author, "ossia score")
63 halp_meta(manual_url, "")
64 halp_meta(description, "Basic text box")
65 halp_meta(uuid, "7be51631-fb4b-4152-9ca7-86fdafa8a989")
66 halp_flag(fully_custom_item);
67 halp_flag(no_background);
68 struct
69 {
70 struct : halp::lineedit<"in", "(Double-click to edit)">
71 {
72 enum widget
73 {
74 control
75 };
76 } port;
77
80 halp::toggle<"Auto-reflow", halp::toggle_setup{.init = false}> reflow;
81 } inputs;
82
84 {
85 public:
86 const Process::ProcessModel& m_process;
87 const Process::Context& m_context;
89 Process::ControlInlet* value_inlet;
90 Process::ControlInlet* reflow_inlet{};
91 score::TextItem* m_textItem{};
92 Layer(
93 const Process::ProcessModel& process, const Process::Context& doc,
94 QGraphicsItem* parent)
96 , m_process{process}
97 , m_context{doc}
98 , m_dispatcher{m_context.commandStack}
99 {
100 setAcceptedMouseButtons(Qt::LeftButton);
101 setFlag(ItemHasNoContents, true);
102 this->setAcceptHoverEvents(true);
103
104 value_inlet = static_cast<Process::ControlInlet*>(process.inlets()[0]);
105 if(process.inlets().size() > 1)
106 reflow_inlet = qobject_cast<Process::ControlInlet*>(process.inlets()[1]);
107
108 m_textItem = new score::TextItem{"", this};
109 applyReflow();
110 this->setToolTip(
111 tr("Comment box\nPut the text you want in here by double-clicking !"));
112
113 connect(m_textItem->document(), &QTextDocument::contentsChanged, this, [&]() {
114 this->prepareGeometryChange();
115
116 auto other = m_textItem->toHtml().toStdString();
117 if(auto v = value_inlet->value(); ossia::convert<std::string>(v) != other)
118 m_dispatcher.submit(*value_inlet, other);
119
120 // Wrapping to the box is the box's business, not the text's: only
121 // grow the box to the text when the text is free to be as wide as it
122 // likes.
123 if(!reflowEnabled())
124 {
125 QSizeF sz = m_textItem->boundingRect().size();
126 sz.rwidth() += 2.;
127 ((Process::ProcessModel&)m_process).setSize(sz);
128 }
129 }, Qt::QueuedConnection);
130
131 // const Process::PortFactoryList& portFactory
132 // = doc.app.interfaces<Process::PortFactoryList>();
133 // auto fact = portFactory.get(value_inlet->concreteKey());
134 // auto port = fact->makePortItem(*value_inlet, doc, this, this);
135 // port->setPos(0, 5);
136
137 connect(
138 value_inlet, &Process::ControlInlet::executionValueChanged, this,
139 [this](const ossia::value& v) { update(); });
140 connect(
141 value_inlet, &Process::ControlInlet::valueChanged, this, &Layer::updateText);
142 updateText(value_inlet->value());
143
144 connect(m_textItem, &score::TextItem::focusOut, this, &Layer::focusOut);
145 focusOut();
146
147 if(reflow_inlet)
148 connect(
149 reflow_inlet, &Process::ControlInlet::valueChanged, this,
150 [this](const ossia::value&) { applyReflow(); });
151 connect(
152 &process, &Process::ProcessModel::sizeChanged, this,
153 [this](QSizeF) { applyReflow(); });
154
155 m_lastClick.start();
156 }
157
158 void updateText(const ossia::value& v)
159 {
160 const auto& str = ossia::convert<std::string>(v);
161 const auto& qstr = QString::fromUtf8(str);
162
163 if(qstr != m_textItem->toHtml())
164 this->m_textItem->setHtml(qstr);
165 }
166
167 bool reflowEnabled() const
168 {
169 return reflow_inlet && ossia::convert<bool>(reflow_inlet->value());
170 }
171
172 void applyReflow()
173 {
174 prepareGeometryChange();
175 setupDocument(
176 *m_textItem->document(),
177 reflowEnabled() ? m_process.size().width() - 2. : -1.);
178 }
179
180 void reset() { update(); }
181
182 void paint_impl(QPainter* p) const override { }
183
184 void mousePressEvent(QGraphicsSceneMouseEvent* event) override
185 {
186 if(event->button() == Qt::MouseButton::LeftButton)
187 {
188 auto t = m_lastClick.elapsed();
189 if(t > QApplication::doubleClickInterval())
190 {
191 event->ignore();
192 m_lastClick.restart();
193 return;
194 }
195 else
196 {
197 event->accept();
198 m_lastClick.restart();
199 focusOnText();
200 return;
201 }
202 }
203 }
204
205 void mouseMoveEvent(QGraphicsSceneMouseEvent* event) override { event->accept(); }
206 void mouseReleaseEvent(QGraphicsSceneMouseEvent* event) override { event->accept(); }
207 void mouseDoubleClickEvent(QGraphicsSceneMouseEvent* evt) override { focusOnText(); }
208
209 void focusOnText()
210 {
211 if(m_textItem->textInteractionFlags() == Qt::NoTextInteraction)
212 {
213 m_textItem->setTextInteractionFlags(Qt::TextEditorInteraction);
214 m_textItem->setFocus(Qt::MouseFocusReason);
215 QTextCursor c = m_textItem->textCursor();
216 c.select(QTextCursor::Document);
217 m_textItem->setTextCursor(c);
218 }
219 }
220
221 void focusOut()
222 {
223 m_textItem->setTextInteractionFlags(Qt::NoTextInteraction);
224 QTextCursor c = m_textItem->textCursor();
225 c.clearSelection();
226 m_textItem->setTextCursor(c);
227 clearFocus();
228 m_dispatcher.commit();
229 }
230
231 QElapsedTimer m_lastClick{};
232 };
233};
234}
Definition Port.hpp:218
Definition EffectLayer.hpp:17
The Process class.
Definition score-lib-process/Process/Process.hpp:75
The SingleOngoingCommandDispatcher class.
Definition SingleOngoingCommandDispatcher.hpp:17
Definition TextItem.hpp:13
Definition ProcessContext.hpp:12
Definition TextBox.hpp:84
Definition TextBox.hpp:58
halp::toggle<"Auto-reflow", halp::toggle_setup{.init=false}> reflow
Definition TextBox.hpp:80