Mercurial > hg > svgui
comparison layer/BoxLayer.cpp @ 1547:e6362cf5ff1d
Pass a y-coord, optionally, to Layer::snapToFeatureFrame. This is necessary for BoxLayer which needs to coordinate its snaps with the box it is highlighting for editing. Then in BoxLayer, merge getPointToDrag and getLocalPoints into a single getLocalPoint and use this throughout.
author | Chris Cannam |
---|---|
date | Thu, 17 Oct 2019 11:12:54 +0100 |
parents | ec837d223bd9 |
children | e95cefd4aa81 |
comparison
equal
deleted
inserted
replaced
1546:ec837d223bd9 | 1547:e6362cf5ff1d |
---|---|
237 } else { | 237 } else { |
238 return false; | 238 return false; |
239 } | 239 } |
240 } | 240 } |
241 | 241 |
242 EventVector | |
243 BoxLayer::getLocalPoints(LayerGeometryProvider *v, int x) const | |
244 { | |
245 auto model = ModelById::getAs<BoxModel>(m_model); | |
246 if (!model) return EventVector(); | |
247 | |
248 sv_frame_t frame = v->getFrameForX(x); | |
249 | |
250 EventVector local = model->getEventsCovering(frame); | |
251 if (!local.empty()) return local; | |
252 | |
253 int fuzz = ViewManager::scalePixelSize(2); | |
254 sv_frame_t start = v->getFrameForX(x - fuzz); | |
255 sv_frame_t end = v->getFrameForX(x + fuzz); | |
256 | |
257 local = model->getEventsStartingWithin(frame, end - frame); | |
258 if (!local.empty()) return local; | |
259 | |
260 local = model->getEventsSpanning(start, frame - start); | |
261 if (!local.empty()) return local; | |
262 | |
263 return {}; | |
264 } | |
265 | |
266 bool | 242 bool |
267 BoxLayer::getPointToDrag(LayerGeometryProvider *v, int x, int y, | 243 BoxLayer::getLocalPoint(LayerGeometryProvider *v, int x, int y, |
268 Event &point) const | 244 Event &point) const |
269 { | 245 { |
270 auto model = ModelById::getAs<BoxModel>(m_model); | 246 auto model = ModelById::getAs<BoxModel>(m_model); |
271 if (!model) return false; | 247 if (!model || !model->isReady()) return false; |
272 | 248 |
273 sv_frame_t frame = v->getFrameForX(x); | 249 sv_frame_t frame = v->getFrameForX(x); |
274 | 250 |
275 EventVector onPoints = model->getEventsCovering(frame); | 251 EventVector onPoints = model->getEventsCovering(frame); |
276 if (onPoints.empty()) return false; | 252 if (onPoints.empty()) return false; |
306 if (bestContaining != Event()) { | 282 if (bestContaining != Event()) { |
307 point = bestContaining; | 283 point = bestContaining; |
308 } else { | 284 } else { |
309 int nearestDistance = -1; | 285 int nearestDistance = -1; |
310 for (const auto &p: onPoints) { | 286 for (const auto &p: onPoints) { |
287 const auto r = getRange(p); | |
311 int distance = std::min | 288 int distance = std::min |
312 (getYForValue(v, p.getValue()) - y, | 289 (getYForValue(v, r.first) - y, |
313 getYForValue(v, p.getValue() + fabsf(p.getLevel())) - y); | 290 getYForValue(v, r.second) - y); |
314 if (distance < 0) distance = -distance; | 291 if (distance < 0) distance = -distance; |
315 if (nearestDistance == -1 || distance < nearestDistance) { | 292 if (nearestDistance == -1 || distance < nearestDistance) { |
316 nearestDistance = distance; | 293 nearestDistance = distance; |
317 point = p; | 294 point = p; |
318 } | 295 } |
341 | 318 |
342 QString | 319 QString |
343 BoxLayer::getFeatureDescription(LayerGeometryProvider *v, | 320 BoxLayer::getFeatureDescription(LayerGeometryProvider *v, |
344 QPoint &pos) const | 321 QPoint &pos) const |
345 { | 322 { |
346 int x = pos.x(); | |
347 | |
348 auto model = ModelById::getAs<BoxModel>(m_model); | 323 auto model = ModelById::getAs<BoxModel>(m_model); |
349 if (!model || !model->getSampleRate()) return ""; | 324 if (!model || !model->getSampleRate()) return ""; |
350 | 325 |
351 EventVector points = getLocalPoints(v, x); | 326 Event box; |
352 | 327 |
353 if (points.empty()) { | 328 if (!getLocalPoint(v, pos.x(), pos.y(), box)) { |
354 if (!model->isReady()) { | 329 if (!model->isReady()) { |
355 return tr("In progress"); | 330 return tr("In progress"); |
356 } else { | 331 } else { |
357 return tr("No local points"); | 332 return tr("No local points"); |
358 } | 333 } |
359 } | 334 } |
360 | 335 |
361 Event box; | |
362 EventVector::iterator i; | |
363 | |
364 for (i = points.begin(); i != points.end(); ++i) { | |
365 | |
366 int y0 = getYForValue(v, i->getValue()); | |
367 int y1 = getYForValue(v, i->getValue() + fabsf(i->getLevel())); | |
368 | |
369 if (pos.y() >= y0 && pos.y() <= y1) { | |
370 box = *i; | |
371 break; | |
372 } | |
373 } | |
374 | |
375 if (i == points.end()) return tr("No local points"); | |
376 | |
377 RealTime rt = RealTime::frame2RealTime(box.getFrame(), | 336 RealTime rt = RealTime::frame2RealTime(box.getFrame(), |
378 model->getSampleRate()); | 337 model->getSampleRate()); |
379 RealTime rd = RealTime::frame2RealTime(box.getDuration(), | 338 RealTime rd = RealTime::frame2RealTime(box.getDuration(), |
380 model->getSampleRate()); | 339 model->getSampleRate()); |
381 | 340 |
382 QString rangeText; | 341 QString rangeText; |
383 | 342 auto r = getRange(box); |
343 | |
384 rangeText = tr("%1 %2 - %3 %4") | 344 rangeText = tr("%1 %2 - %3 %4") |
385 .arg(box.getValue()).arg(getScaleUnits()) | 345 .arg(r.first).arg(getScaleUnits()) |
386 .arg(box.getValue() + fabsf(box.getLevel())).arg(getScaleUnits()); | 346 .arg(r.second).arg(getScaleUnits()); |
387 | 347 |
388 QString text; | 348 QString text; |
389 | 349 |
390 if (box.getLabel() == "") { | 350 if (box.getLabel() == "") { |
391 text = QString(tr("Time:\t%1\nDuration:\t%2\nValue:\t%3\nNo label")) | 351 text = QString(tr("Time:\t%1\nDuration:\t%2\nValue:\t%3\nNo label")) |
407 | 367 |
408 bool | 368 bool |
409 BoxLayer::snapToFeatureFrame(LayerGeometryProvider *v, | 369 BoxLayer::snapToFeatureFrame(LayerGeometryProvider *v, |
410 sv_frame_t &frame, | 370 sv_frame_t &frame, |
411 int &resolution, | 371 int &resolution, |
412 SnapType snap) const | 372 SnapType snap, |
373 int ycoord) const | |
413 { | 374 { |
414 auto model = ModelById::getAs<BoxModel>(m_model); | 375 auto model = ModelById::getAs<BoxModel>(m_model); |
415 if (!model) { | 376 if (!model) { |
416 return Layer::snapToFeatureFrame(v, frame, resolution, snap); | 377 return Layer::snapToFeatureFrame(v, frame, resolution, snap, ycoord); |
417 } | 378 } |
418 | 379 |
419 // SnapLeft / SnapRight: return frame of nearest feature in that | 380 // SnapLeft / SnapRight: return frame of nearest feature in that |
420 // direction no matter how far away | 381 // direction no matter how far away |
421 // | 382 // |
423 // an editing operation, i.e. closest feature in either direction | 384 // an editing operation, i.e. closest feature in either direction |
424 // but only if it is "close enough" | 385 // but only if it is "close enough" |
425 | 386 |
426 resolution = model->getResolution(); | 387 resolution = model->getResolution(); |
427 | 388 |
389 Event containing; | |
390 | |
391 if (getLocalPoint(v, v->getXForFrame(frame), ycoord, containing)) { | |
392 | |
393 switch (snap) { | |
394 | |
395 case SnapLeft: | |
396 case SnapNeighbouring: | |
397 frame = containing.getFrame(); | |
398 return true; | |
399 | |
400 case SnapRight: | |
401 frame = containing.getFrame() + containing.getDuration(); | |
402 return true; | |
403 } | |
404 } | |
405 | |
428 if (snap == SnapNeighbouring) { | 406 if (snap == SnapNeighbouring) { |
429 EventVector points = getLocalPoints(v, v->getXForFrame(frame)); | 407 return false; |
430 if (points.empty()) return false; | |
431 frame = points.begin()->getFrame(); | |
432 return true; | |
433 } | 408 } |
434 | 409 |
435 // Normally we snap to the start frame of whichever event we | 410 // We aren't actually contained (in time) by any single event, so |
436 // find. However here, for SnapRight only, if the end frame of | 411 // seek the next one in the relevant direction |
437 // whichever event we would have snapped to had we been snapping | 412 |
438 // left is closer than the start frame of the next event to the | 413 Event e; |
439 // right, then we snap to that frame instead. Clear? | 414 |
440 | |
441 Event left; | |
442 bool haveLeft = false; | |
443 if (model->getNearestEventMatching | |
444 (frame, [](Event) { return true; }, EventSeries::Backward, left)) { | |
445 haveLeft = true; | |
446 } | |
447 | |
448 if (snap == SnapLeft) { | 415 if (snap == SnapLeft) { |
449 frame = left.getFrame(); | 416 if (model->getNearestEventMatching |
450 return haveLeft; | 417 (frame, [](Event) { return true; }, EventSeries::Backward, e)) { |
451 } | 418 |
452 | 419 if (e.getFrame() + e.getDuration() < frame) { |
453 Event right; | 420 frame = e.getFrame() + e.getDuration(); |
454 bool haveRight = false; | |
455 if (model->getNearestEventMatching | |
456 (frame, [](Event) { return true; }, EventSeries::Forward, right)) { | |
457 haveRight = true; | |
458 } | |
459 | |
460 if (haveLeft) { | |
461 sv_frame_t leftEnd = left.getFrame() + left.getDuration(); | |
462 if (leftEnd > frame) { | |
463 if (haveRight) { | |
464 if (leftEnd - frame < right.getFrame() - frame) { | |
465 frame = leftEnd; | |
466 } else { | |
467 frame = right.getFrame(); | |
468 } | |
469 } else { | 421 } else { |
470 frame = leftEnd; | 422 frame = e.getFrame(); |
471 } | 423 } |
472 return true; | 424 return true; |
473 } | 425 } |
474 } | 426 } |
475 | 427 |
476 if (haveRight) { | 428 if (snap == SnapRight) { |
477 frame = right.getFrame(); | 429 if (model->getNearestEventMatching |
478 return true; | 430 (frame, [](Event) { return true; }, EventSeries::Forward, e)) { |
431 | |
432 frame = e.getFrame(); | |
433 return true; | |
434 } | |
479 } | 435 } |
480 | 436 |
481 return false; | 437 return false; |
482 } | 438 } |
483 | 439 |
605 QPoint localPos; | 561 QPoint localPos; |
606 Event illuminatePoint(0); | 562 Event illuminatePoint(0); |
607 bool shouldIlluminate = false; | 563 bool shouldIlluminate = false; |
608 | 564 |
609 if (v->shouldIlluminateLocalFeatures(this, localPos)) { | 565 if (v->shouldIlluminateLocalFeatures(this, localPos)) { |
610 shouldIlluminate = getPointToDrag(v, localPos.x(), localPos.y(), | 566 shouldIlluminate = getLocalPoint(v, localPos.x(), localPos.y(), |
611 illuminatePoint); | 567 illuminatePoint); |
612 } | 568 } |
613 | 569 |
614 paint.save(); | 570 paint.save(); |
615 paint.setRenderHint(QPainter::Antialiasing, false); | 571 paint.setRenderHint(QPainter::Antialiasing, false); |
616 | 572 |
618 | 574 |
619 for (EventVector::const_iterator i = points.begin(); | 575 for (EventVector::const_iterator i = points.begin(); |
620 i != points.end(); ++i) { | 576 i != points.end(); ++i) { |
621 | 577 |
622 const Event &p(*i); | 578 const Event &p(*i); |
579 const auto r = getRange(p); | |
623 | 580 |
624 int x = v->getXForFrame(p.getFrame()); | 581 int x = v->getXForFrame(p.getFrame()); |
625 int w = v->getXForFrame(p.getFrame() + p.getDuration()) - x; | 582 int w = v->getXForFrame(p.getFrame() + p.getDuration()) - x; |
626 int y = getYForValue(v, p.getValue()); | 583 int y = getYForValue(v, r.first); |
627 int h = getYForValue(v, p.getValue() + fabsf(p.getLevel())) - y; | 584 int h = getYForValue(v, r.second) - y; |
628 int ex = x + w; | 585 int ex = x + w; |
629 int gap = v->scalePixelSize(2); | 586 int gap = v->scalePixelSize(2); |
630 | 587 |
631 EventVector::const_iterator j = i; | 588 EventVector::const_iterator j = i; |
632 ++j; | 589 ++j; |
653 #pragma GCC diagnostic ignored "-Wdeprecated-declarations" | 610 #pragma GCC diagnostic ignored "-Wdeprecated-declarations" |
654 | 611 |
655 if (abs(h) > 2 * fm.height()) { | 612 if (abs(h) > 2 * fm.height()) { |
656 | 613 |
657 QString y0label = QString("%1 %2") | 614 QString y0label = QString("%1 %2") |
658 .arg(p.getValue()) | 615 .arg(r.first) |
659 .arg(getScaleUnits()); | 616 .arg(getScaleUnits()); |
660 | 617 |
661 QString y1label = QString("%1 %2") | 618 QString y1label = QString("%1 %2") |
662 .arg(p.getValue() + fabsf(p.getLevel())) | 619 .arg(r.second) |
663 .arg(getScaleUnits()); | 620 .arg(getScaleUnits()); |
664 | 621 |
665 PaintAssistant::drawVisibleText | 622 PaintAssistant::drawVisibleText |
666 (v, paint, | 623 (v, paint, |
667 x - fm.width(y0label) - gap, | 624 x - fm.width(y0label) - gap, |
675 y1label, PaintAssistant::OutlinedText); | 632 y1label, PaintAssistant::OutlinedText); |
676 | 633 |
677 } else { | 634 } else { |
678 | 635 |
679 QString ylabel = QString("%1 %2 - %3 %4") | 636 QString ylabel = QString("%1 %2 - %3 %4") |
680 .arg(p.getValue()) | 637 .arg(r.first) |
681 .arg(getScaleUnits()) | 638 .arg(getScaleUnits()) |
682 .arg(p.getValue() + fabsf(p.getLevel())) | 639 .arg(r.second) |
683 .arg(getScaleUnits()); | 640 .arg(getScaleUnits()); |
684 | 641 |
685 PaintAssistant::drawVisibleText | 642 PaintAssistant::drawVisibleText |
686 (v, paint, | 643 (v, paint, |
687 x - fm.width(ylabel) - gap, | 644 x - fm.width(ylabel) - gap, |
878 BoxLayer::eraseStart(LayerGeometryProvider *v, QMouseEvent *e) | 835 BoxLayer::eraseStart(LayerGeometryProvider *v, QMouseEvent *e) |
879 { | 836 { |
880 auto model = ModelById::getAs<BoxModel>(m_model); | 837 auto model = ModelById::getAs<BoxModel>(m_model); |
881 if (!model) return; | 838 if (!model) return; |
882 | 839 |
883 if (!getPointToDrag(v, e->x(), e->y(), m_editingPoint)) return; | 840 if (!getLocalPoint(v, e->x(), e->y(), m_editingPoint)) return; |
884 | 841 |
885 if (m_editingCommand) { | 842 if (m_editingCommand) { |
886 finish(m_editingCommand); | 843 finish(m_editingCommand); |
887 m_editingCommand = nullptr; | 844 m_editingCommand = nullptr; |
888 } | 845 } |
902 if (!model || !m_editing) return; | 859 if (!model || !m_editing) return; |
903 | 860 |
904 m_editing = false; | 861 m_editing = false; |
905 | 862 |
906 Event p(0); | 863 Event p(0); |
907 if (!getPointToDrag(v, e->x(), e->y(), p)) return; | 864 if (!getLocalPoint(v, e->x(), e->y(), p)) return; |
908 if (p.getFrame() != m_editingPoint.getFrame() || | 865 if (p.getFrame() != m_editingPoint.getFrame() || |
909 p.getValue() != m_editingPoint.getValue()) return; | 866 p.getValue() != m_editingPoint.getValue()) return; |
910 | 867 |
911 m_editingCommand = new ChangeEventsCommand | 868 m_editingCommand = new ChangeEventsCommand |
912 (m_model.untyped, tr("Erase Box")); | 869 (m_model.untyped, tr("Erase Box")); |
922 BoxLayer::editStart(LayerGeometryProvider *v, QMouseEvent *e) | 879 BoxLayer::editStart(LayerGeometryProvider *v, QMouseEvent *e) |
923 { | 880 { |
924 auto model = ModelById::getAs<BoxModel>(m_model); | 881 auto model = ModelById::getAs<BoxModel>(m_model); |
925 if (!model) return; | 882 if (!model) return; |
926 | 883 |
927 if (!getPointToDrag(v, e->x(), e->y(), m_editingPoint)) { | 884 if (!getLocalPoint(v, e->x(), e->y(), m_editingPoint)) { |
928 return; | 885 return; |
929 } | 886 } |
930 | 887 |
931 m_dragPointX = v->getXForFrame(m_editingPoint.getFrame()); | 888 m_dragPointX = v->getXForFrame(m_editingPoint.getFrame()); |
932 m_dragPointY = getYForValue(v, m_editingPoint.getValue()); | 889 m_dragPointY = getYForValue(v, m_editingPoint.getValue()); |
1006 { | 963 { |
1007 auto model = ModelById::getAs<BoxModel>(m_model); | 964 auto model = ModelById::getAs<BoxModel>(m_model); |
1008 if (!model) return false; | 965 if (!model) return false; |
1009 | 966 |
1010 Event region(0); | 967 Event region(0); |
1011 if (!getPointToDrag(v, e->x(), e->y(), region)) return false; | 968 if (!getLocalPoint(v, e->x(), e->y(), region)) return false; |
1012 | 969 |
1013 ItemEditDialog::LabelOptions labelOptions; | 970 ItemEditDialog::LabelOptions labelOptions; |
1014 labelOptions.valueLabel = tr("Minimum Value"); | 971 labelOptions.valueLabel = tr("Minimum Value"); |
1015 labelOptions.levelLabel = tr("Value Extent"); | 972 labelOptions.levelLabel = tr("Value Extent"); |
1016 labelOptions.valueUnits = getScaleUnits(); | 973 labelOptions.valueUnits = getScaleUnits(); |