PatternInspector.hpp
1 #pragma once
2 #include <Process/Inspector/ProcessInspectorWidgetDelegate.hpp>
3 #include <Process/Inspector/ProcessInspectorWidgetDelegateFactory.hpp>
4 
5 #include <score/command/Dispatchers/OngoingCommandDispatcher.hpp>
6 #include <score/document/DocumentContext.hpp>
7 #include <score/tools/Bind.hpp>
8 #include <score/widgets/DoubleSlider.hpp>
9 #include <score/widgets/SignalUtils.hpp>
10 
11 #include <ossia/detail/math.hpp>
12 
13 #include <QFormLayout>
14 #include <QSpinBox>
15 
16 #include <Patternist/Commands/PatternProperties.hpp>
17 #include <Patternist/PatternModel.hpp>
18 
19 namespace Patternist
20 {
21 class InspectorWidget final : public Process::InspectorWidgetDelegate_T<ProcessModel>
22 {
23 public:
24  explicit InspectorWidget(
25  const ProcessModel& obj, const score::DocumentContext& doc, QWidget* parent)
26  : InspectorWidgetDelegate_T{obj, parent}
27  , m_dispatcher{doc.dispatcher}
28  , m_channel{this}
29  , m_currentPattern{this}
30  , m_lanes{this}
31  , m_duration{this}
32  , m_rate{this}
33  {
34  m_duration.setRange(4, 32);
35  m_rate.setRange(1, 64);
36  m_channel.setRange(1, 16);
37  m_lanes.setRange(1, 127);
38 
39  m_channel.setValue(obj.channel());
40 
41  if(!ossia::valid_index(obj.currentPattern(), obj.patterns()))
42  return;
43  const Pattern& pat = obj.patterns()[obj.currentPattern()];
44  m_lanes.setValue(pat.lanes.size());
45  m_duration.setValue(pat.length);
46  m_rate.setValue(pat.division);
47 
48  auto lay = new QFormLayout{this};
49 
50  con(process(), &ProcessModel::channelChanged, this, [&](int c) {
51  if(c != m_channel.value())
52  m_channel.setValue(c);
53  });
54  con(process(), &ProcessModel::currentPatternChanged, this, [&](int c) {
55  if(c == m_currentPattern.value())
56  return;
57 
58  m_currentPattern.setValue(c);
59 
60  const Pattern& pat = obj.patterns()[c];
61  m_lanes.blockSignals(true);
62  m_duration.blockSignals(true);
63  m_rate.blockSignals(true);
64 
65  m_lanes.setValue(pat.lanes.size());
66  m_duration.setValue(pat.length);
67  m_rate.setValue(pat.division);
68 
69  m_lanes.blockSignals(false);
70  m_duration.blockSignals(false);
71  m_rate.blockSignals(false);
72  });
73  con(process(), &ProcessModel::patternsChanged, this, [&] {
74  if(!ossia::valid_index(obj.currentPattern(), obj.patterns()))
75  return;
76 
77  const Pattern& pat = obj.patterns()[obj.currentPattern()];
78  m_lanes.blockSignals(true);
79  m_duration.blockSignals(true);
80  m_rate.blockSignals(true);
81 
82  m_lanes.setValue(pat.lanes.size());
83  m_duration.setValue(pat.length);
84  m_rate.setValue(pat.division);
85 
86  m_lanes.blockSignals(false);
87  m_duration.blockSignals(false);
88  m_rate.blockSignals(false);
89  });
90 
91  con(m_channel, qOverload<int>(&QSpinBox::valueChanged), this, [&](int v) {
92  if(v != obj.channel())
93  m_dispatcher.submit<SetPatternChannel>(obj, v);
94  });
95  con(m_channel, &QSpinBox::editingFinished, this, [&] { m_dispatcher.commit(); });
96 
97  con(m_lanes, qOverload<int>(&QSpinBox::valueChanged), this, [&](int nn) {
98  if(nn <= 0)
99  return;
100 
101  if(!ossia::valid_index(obj.currentPattern(), obj.patterns()))
102  return;
103 
104  const std::size_t n = nn;
105 
106  auto p = obj.patterns()[obj.currentPattern()];
107  if(n == p.lanes.size())
108  {
109  return;
110  }
111  else if(n < p.lanes.size())
112  {
113  p.lanes.resize(n);
114  }
115  else
116  {
117  auto last_lane = p.lanes.back();
118  while(p.lanes.size() < n)
119  p.lanes.push_back(last_lane);
120  }
121 
122  m_dispatcher.submit<UpdatePattern>(obj, obj.currentPattern(), p);
123  });
124 
125  con(m_lanes, &QSpinBox::editingFinished, this, [&]() { m_dispatcher.commit(); });
126 
127  con(m_currentPattern, qOverload<int>(&QSpinBox::valueChanged), this, [&](int v) {
128  if(v != obj.currentPattern())
129  m_dispatcher.submit<SetCurrentPattern>(obj, v);
130  });
131  con(m_currentPattern, &QSpinBox::editingFinished, this,
132  [&]() { m_dispatcher.commit(); });
133 
134  con(m_duration, qOverload<int>(&QSpinBox::valueChanged), this, [&]() {
135  int n = m_duration.value();
136  if(n <= 0)
137  return;
138 
139  auto p = obj.patterns()[obj.currentPattern()];
140  if(p.length == n)
141  return;
142 
143  p.length = n;
144  if(p.length > int64_t(p.lanes[0].pattern.size()))
145  {
146  for(auto& lane : p.lanes)
147  {
148  lane.pattern.resize(n);
149  }
150  }
151 
152  m_dispatcher.submit<UpdatePattern>(obj, obj.currentPattern(), p);
153  });
154  con(m_duration, &QSpinBox::editingFinished, this, [&]() { m_dispatcher.commit(); });
155 
156  con(m_rate, &QDoubleSpinBox::editingFinished, this, [&] {
157  auto p = obj.patterns()[obj.currentPattern()];
158  if(p.division != m_rate.value())
159  p.division = m_rate.value();
160  m_dispatcher.submit<UpdatePattern>(obj, obj.currentPattern(), p);
161  m_dispatcher.commit();
162  });
163 
164  lay->addRow(tr("Channel"), &m_channel);
165  lay->addRow(tr("Current pattern"), &m_currentPattern);
166  lay->addRow(tr("Lanes"), &m_lanes);
167  lay->addRow(tr("Steps"), &m_duration);
168  lay->addRow(tr("Rate"), &m_rate);
169  }
170 
171 private:
172  OngoingCommandDispatcher& m_dispatcher;
173 
174  QSpinBox m_channel;
175  QSpinBox m_currentPattern;
176  QSpinBox m_lanes;
177  QSpinBox m_duration;
178  QDoubleSpinBox m_rate;
179 };
180 class InspectorFactory final
181  : public Process::InspectorWidgetDelegateFactory_T<ProcessModel, InspectorWidget>
182 {
183  SCORE_CONCRETE("03d55730-fc4a-42a7-b573-35c330c5bad2")
184 };
185 }
The OngoingCommandDispatcher class.
Definition: OngoingCommandDispatcher.hpp:27
void submit(Args &&... args)
Definition: OngoingCommandDispatcher.hpp:37
void commit()
Definition: OngoingCommandDispatcher.hpp:61
Definition: PatternInspector.hpp:182
Definition: PatternInspector.hpp:22
Definition: PatternModel.hpp:36
Definition: PatternProperties.hpp:16
Definition: ProcessInspectorWidgetDelegate.hpp:13
Definition: ProcessInspectorWidgetDelegateFactory.hpp:53
Definition: PatternModel.hpp:24
Definition: DocumentContext.hpp:18