diff --git a/src/app/ui/timeline/timeline.cpp b/src/app/ui/timeline/timeline.cpp index d0dd5e8ce..9c03d9148 100644 --- a/src/app/ui/timeline/timeline.cpp +++ b/src/app/ui/timeline/timeline.cpp @@ -1068,7 +1068,8 @@ bool Timeline::onProcessMessage(Message* msg) if (hasCapture()) { switch (m_state) { - case STATE_MOVING_RANGE: { + case STATE_DRAGGING_EXTERNAL_RANGE: + case STATE_MOVING_RANGE: { col_t newFrame; if (m_range.type() == Range::kLayers) { // If we are moving only layers we don't change the @@ -2065,11 +2066,15 @@ void Timeline::setCursor(ui::Message* msg, const Hit& hit) } // Moving. else if (m_state == STATE_MOVING_RANGE) { - if (is_copy_key_pressed(msg)) + if (msg && is_copy_key_pressed(msg)) ui::set_mouse_cursor(kArrowPlusCursor); else ui::set_mouse_cursor(kMoveCursor); } + // Dragging elements from external source. + else if (m_state == STATE_DRAGGING_EXTERNAL_RANGE) { + ui::set_mouse_cursor(kArrowPlusCursor); + } // Normal state. else if (hit.part == PART_HEADER_ONIONSKIN_RANGE_LEFT || m_state == STATE_MOVING_ONIONSKIN_RANGE_LEFT) { @@ -2080,7 +2085,7 @@ void Timeline::setCursor(ui::Message* msg, const Hit& hit) ui::set_mouse_cursor(kSizeECursor); } else if (hit.part == PART_RANGE_OUTLINE) { - if (is_copy_key_pressed(msg)) + if (msg && is_copy_key_pressed(msg)) ui::set_mouse_cursor(kArrowPlusCursor); else ui::set_mouse_cursor(kMoveCursor); @@ -2823,6 +2828,43 @@ void Timeline::drawTagBraces(ui::Graphics* g, } } +void Timeline::paintDropFrameDeco(ui::Graphics* g, PaintWidgetPartInfo info, gfx::Rect dropBounds) +{ + auto& styles = skinTheme()->styles; + int w = 5 * guiscale(); // TODO get width from the skin info + + if (m_dropTarget.hhit == DropTarget::Before) + dropBounds.x -= w / 2; + else if (m_dropRange == m_range) + dropBounds.x = dropBounds.x + getRangeBounds(m_range).w - w / 2; + else + dropBounds.x = dropBounds.x + dropBounds.w - w / 2; + + dropBounds.w = w; + + info.styleFlags = 0; + theme()->paintWidgetPart(g, styles.timelineDropFrameDeco(), dropBounds, info); +} + +void Timeline::paintDropLayerDeco(ui::Graphics* g, + const PaintWidgetPartInfo& info, + gfx::Rect dropBounds) +{ + auto& styles = skinTheme()->styles; + int h = 5 * guiscale(); // TODO get height from the skin info + + if (m_dropTarget.vhit == DropTarget::Top) + dropBounds.y -= h / 2; + else if (m_dropRange == m_range) + dropBounds.y = dropBounds.y + getRangeBounds(m_range).h - h / 2; + else + dropBounds.y = dropBounds.y + dropBounds.h - h / 2; + + dropBounds.h = h; + + theme()->paintWidgetPart(g, styles.timelineDropLayerDeco(), dropBounds, info); +} + void Timeline::drawRangeOutline(ui::Graphics* g) { auto& styles = skinTheme()->styles; @@ -2842,42 +2884,24 @@ void Timeline::drawRangeOutline(ui::Graphics* g) switch (m_dropRange.type()) { case Range::kCels: { - dropBounds = dropBounds.enlarge(outlineWidth()); + gfx::Rect outlineBounds(dropBounds); + outlineBounds.enlarge(outlineWidth()); info.styleFlags = ui::Style::Layer::kFocus; - theme()->paintWidgetPart(g, styles.timelineRangeOutline(), dropBounds, info); + theme()->paintWidgetPart(g, styles.timelineRangeOutline(), outlineBounds, info); + + if (m_state == STATE_DRAGGING_EXTERNAL_RANGE) + paintDropLayerDeco(g, info, getSelectedLayersBounds(m_dropRange)); + break; } case Range::kFrames: { - int w = 5 * guiscale(); // TODO get width from the skin info - - if (m_dropTarget.hhit == DropTarget::Before) - dropBounds.x -= w / 2; - else if (m_dropRange == m_range) - dropBounds.x = dropBounds.x + getRangeBounds(m_range).w - w / 2; - else - dropBounds.x = dropBounds.x + dropBounds.w - w / 2; - - dropBounds.w = w; - - info.styleFlags = 0; - theme()->paintWidgetPart(g, styles.timelineDropFrameDeco(), dropBounds, info); + paintDropFrameDeco(g, info, dropBounds); break; } case Range::kLayers: { - int h = 5 * guiscale(); // TODO get height from the skin info - - if (m_dropTarget.vhit == DropTarget::Top) - dropBounds.y -= h / 2; - else if (m_dropRange == m_range) - dropBounds.y = dropBounds.y + getRangeBounds(m_range).h - h / 2; - else - dropBounds.y = dropBounds.y + dropBounds.h - h / 2; - - dropBounds.h = h; - - theme()->paintWidgetPart(g, styles.timelineDropLayerDeco(), dropBounds, info); + paintDropLayerDeco(g, info, dropBounds); break; } } @@ -3172,6 +3196,27 @@ gfx::Rect Timeline::getPartBounds(const Hit& hit) const return gfx::Rect(); } +gfx::Rect Timeline::getSelectedFramesBounds(const Range& range) const +{ + gfx::Rect rc; + for (frame_t frame : range.selectedFrames()) { + rc |= getPartBounds(Hit(PART_HEADER_FRAME, 0, col_t(frame))); + rc |= getPartBounds(Hit(PART_CEL, 0, col_t(frame))); + } + return rc; +} + +gfx::Rect Timeline::getSelectedLayersBounds(const Range& range) const +{ + gfx::Rect rc; + for (auto* layer : range.selectedLayers()) { + layer_t layerIdx = getLayerIndex(layer); + rc |= getPartBounds(Hit(PART_ROW_TEXT, layerIdx)); + rc |= getPartBounds(Hit(PART_CEL, layerIdx, lastFrame())); + } + return rc; +} + gfx::Rect Timeline::getRangeBounds(const Range& range) const { gfx::Rect rc; @@ -3180,26 +3225,15 @@ gfx::Rect Timeline::getRangeBounds(const Range& range) const // Return empty rectangle break; case Range::kCels: - for (auto layer : range.selectedLayers()) { + for (auto* layer : range.selectedLayers()) { layer_t layerIdx = getLayerIndex(layer); for (frame_t frame : range.selectedFrames()) rc |= getPartBounds(Hit(PART_CEL, layerIdx, col_t(frame))); } break; - case Range::kFrames: { - for (frame_t frame : range.selectedFrames()) { - rc |= getPartBounds(Hit(PART_HEADER_FRAME, 0, col_t(frame))); - rc |= getPartBounds(Hit(PART_CEL, 0, col_t(frame))); - } - break; - } - case Range::kLayers: - for (auto layer : range.selectedLayers()) { - layer_t layerIdx = getLayerIndex(layer); - rc |= getPartBounds(Hit(PART_ROW_TEXT, layerIdx)); - rc |= getPartBounds(Hit(PART_CEL, layerIdx, lastFrame())); - } - break; + case Range::kFrames: return getSelectedFramesBounds(range); + + case Range::kLayers: return getSelectedLayersBounds(range); } return rc; } @@ -3209,7 +3243,12 @@ gfx::Rect Timeline::getRangeClipBounds(const Range& range) const gfx::Rect celBounds = getCelsBounds(); gfx::Rect clipBounds, unionBounds; switch (range.type()) { - case Range::kCels: clipBounds = celBounds; break; + case Range::kCels: { + clipBounds = celBounds; + if (m_state == STATE_DRAGGING_EXTERNAL_RANGE) + clipBounds |= getLayerHeadersBounds(); + break; + } case Range::kFrames: { clipBounds = getFrameHeadersBounds(); @@ -4186,7 +4225,7 @@ void Timeline::updateDropRange(const gfx::Point& pt) m_dropTarget.vhit = DropTarget::VNone; m_dropTarget.outside = false; - if (m_state != STATE_MOVING_RANGE) { + if (m_state != STATE_MOVING_RANGE && m_state != STATE_DRAGGING_EXTERNAL_RANGE) { m_dropRange.clearRange(); return; } @@ -4482,7 +4521,8 @@ void Timeline::onDragEnter(ui::DragEvent& e) { Widget::onDragEnter(e); - m_state = STATE_MOVING_RANGE; + m_state = STATE_DRAGGING_EXTERNAL_RANGE; + setCursor(nullptr, Hit()); } void Timeline::onDragLeave(ui::DragEvent& e) @@ -4495,6 +4535,7 @@ void Timeline::onDragLeave(ui::DragEvent& e) invalidate(); flushRedraw(); flushMessages(); + updateByMousePos(nullptr, e.position()); } void Timeline::onDrag(ui::DragEvent& e) @@ -4504,6 +4545,7 @@ void Timeline::onDrag(ui::DragEvent& e) m_range.clearRange(); setHot(hitTest(nullptr, e.position())); switch (m_hot.part) { + case PART_NOTHING: invalidate(); case PART_ROW: case PART_ROW_EYE_ICON: case PART_ROW_CONTINUOUS_ICON: @@ -4537,6 +4579,7 @@ void Timeline::onDrop(ui::DragEvent& e) layer_t layerIndex = getLayerIndex(m_layer); InsertionPoint insert = InsertionPoint::BeforeLayer; DroppedOn droppedOn = DroppedOn::Unspecified; + TRACE("m_dropRange.type() %d\n", m_dropRange.type()); switch (m_dropRange.type()) { case Range::kCels: frame = m_hot.frame; @@ -4545,6 +4588,11 @@ void Timeline::onDrop(ui::DragEvent& e) droppedOn = DroppedOn::Cel; insert = (m_dropTarget.vhit == DropTarget::Top ? InsertionPoint::AfterLayer : InsertionPoint::BeforeLayer); + TRACE("m_hot part=%d veryBottom=%d frame=%d layer=%d\n", + m_hot.part, + m_hot.veryBottom, + m_hot.frame, + m_hot.layer); break; case Range::kFrames: frame = m_dropRange.firstFrame(); diff --git a/src/app/ui/timeline/timeline.h b/src/app/ui/timeline/timeline.h index 956555556..3793ed0dd 100644 --- a/src/app/ui/timeline/timeline.h +++ b/src/app/ui/timeline/timeline.h @@ -48,6 +48,7 @@ namespace ui { class Graphics; class TooltipManager; class DragEvent; +struct PaintWidgetPartInfo; } // namespace ui namespace app { @@ -103,6 +104,8 @@ public: STATE_DISABLING_CONTINUOUS_LAYERS, STATE_EXPANDING_LAYERS, STATE_COLLAPSING_LAYERS, + // Drag & drop handling state + STATE_DRAGGING_EXTERNAL_RANGE, }; enum DropOp { kMove, kCopy }; @@ -311,6 +314,10 @@ private: gfx::Color tagColor, const gfx::Rect& bounds, const gfx::Rect& clipBounds); + void paintDropFrameDeco(ui::Graphics* g, ui::PaintWidgetPartInfo info, gfx::Rect dropBounds); + void paintDropLayerDeco(ui::Graphics* g, + const ui::PaintWidgetPartInfo& info, + gfx::Rect dropBounds); void drawRangeOutline(ui::Graphics* g); void drawPaddings(ui::Graphics* g); bool drawPart(ui::Graphics* g, int part, layer_t layer, col_t frame); @@ -320,6 +327,8 @@ private: gfx::Rect getOnionskinFramesBounds() const; gfx::Rect getCelsBounds() const; gfx::Rect getPartBounds(const Hit& hit) const; + gfx::Rect getSelectedFramesBounds(const Range& range) const; + gfx::Rect getSelectedLayersBounds(const Range& range) const; gfx::Rect getRangeBounds(const Range& range) const; gfx::Rect getRangeClipBounds(const Range& range) const; int getFrameXPos(const col_t frame) const;