range markers in kdenlive
jun – aug 2025 · 11 min read · gsoc · kdenlive · c++ · qml
i'm ajay chauhan (matrix), and for google summer of code 2025 i worked on improving kdenlive's timeline markers. i'd worked on kdenlive once before, through season of kde '24.
kdenlive only supported single-point timeline markers, which limits workflows that need to mark a time range — highlight editing, collaborative annotations, section-based organization. this project added duration-based markers that define a clear start and end time. the plan was to extend the marker data model with a duration attribute while keeping full backward compatibility, and update the ui to visualize range markers as colored regions with interactive handles for resizing and editing. the markers would also tie into zone-to-marker conversion, search and navigation, rendering specific ranges, and import/export.
it grew out of issue #614 — the need for efficient range-based marking in kdenlive's timeline. my mentor was jean-baptiste mardelle.
duration handling in CommentedTime
the CommentedTime class represents an individual marker. i extended it to carry duration, which is what enables range markers across the rest of the app. i added:
duration()— returns the marker's duration as aGenTimeobjectsetDuration()— sets the durationhasRange()— a boolean check for whether a marker is a range (duration > 0)endTime()— the end position (start + duration)
plus a new constructor that accepts a duration, while keeping the existing point-marker constructor working:
// New constructor with duration support
CommentedTime(const GenTime &time, QString comment, int markerType, const GenTime &duration);
// New member variable
GenTime m_duration{GenTime(0)}; // Defaults to 0 for point markersa point marker is simply a range of zero duration, so none of the old behaviour changes. commit ↗
range support in MarkerListModel
with duration in the data model, i extended MarkerListModel to actually create and manage ranges:
addRangeMarker()— a public method that creates a marker with both position and durationaddOrUpdateRangeMarker_lambda()— an internal helper that handles both creating new range markers and updating existing ones- an overloaded
editMarker()that preserves duration when editing
// New method signature for range markers
bool addRangeMarker(GenTime pos, GenTime duration, const QString &comment, int type = -1);
// Extended edit method with duration support
bool editMarker(GenTime oldPos, GenTime pos, QString comment, int type, GenTime duration);it uses a lambda-based approach so range operations plug into kdenlive's existing undo/redo command pattern, and the model emits change signals for the new roles — DurationRole, EndPosRole, HasRangeRole — so the ui stays in sync. commit ↗
all existing marker functionality keeps working exactly as before — point markers are just range markers with zero duration. with the backend in place, the next phase was the ui.
update — august 2025
hello again! over the following months the work moved from backend to frontend — turning the data model into something you can see and drag.
building the marker dialog
i started with the marker dialog, the window where you create and edit a marker. the challenge was keeping point markers simple while adding range controls. i added three elements:
- a checkbox to toggle between point and range markers
- an 'end time' field for the marker's end position
- a 'duration' field that automatically calculates and displays the span
the trickiest part was keeping them synchronized — change the start time and the duration updates; change the duration and the end time adjusts. three interconnected gears that always move together. commit ↗
visual magic in the monitor
next came the monitor ruler — the strip above the video preview, where range markers work against the footage. i implemented a semi-transparent colored rectangle for the span, vertical lines for the start and end, and kept each marker type its own color across the whole range. the core of it is the rangeSpan rectangle:
Rectangle {
id: rangeSpan
visible: guideRoot.isRangeMarker
x: (model.frame * root.timeScale) - ruler.rulerZoomOffset
width: Math.max(1, guideRoot.markerDuration * root.timeScale)
height: parent.height
color: Qt.rgba(model.color.r, model.color.g, model.color.b, 0.5)
}it only shows for range markers, the width maps duration to pixels (with a one-pixel minimum), and the color is the marker category color at 50% opacity. commit ↗
timeline integration
the timeline ruler — where you spend most of your editing time — got the same visualization and the same drag-to-resize, plus extra constraints so a marker can't extend beyond its clip boundaries. commit ↗
drag-to-resize
this was the most technically challenging feature. dragging the left or right edge changes the start position and duration in real time, with live preview, a minimum-one-frame constraint, and careful binding management to restore qt's automatic updates once a resize finishes. the handles only appear when the marker is wide enough, and give feedback through color and opacity. here's the left handle:
Rectangle {
id: leftResizeHandle
visible: guideRoot.isRangeMarker && rangeSpan.width > 10
width: 4
height: parent.height
x: rangeSpan.x
color: Qt.darker(model.color, 1.3)
opacity: leftResizeArea.containsMouse || leftResizeArea.isResizing ? 0.8 : 0.5
MouseArea {
id: leftResizeArea
anchors.fill: parent
anchors.margins: -2 // Extends clickable area
cursorShape: Qt.SizeHorCursor
preventStealing: true
onPositionChanged: {
if (isResizing) {
var globalCurrentX = mapToGlobal(Qt.point(mouseX, 0)).x
var realDeltaX = globalCurrentX - globalStartX
var deltaFrames = Math.round(realDeltaX / root.timeScale)
var newStartPosition = Math.max(0, startPosition + deltaFrames)
// Live preview updates
rangeSpan.x = (newStartPosition * root.timeScale) - ruler.rulerZoomOffset
rangeSpan.width = Math.max(1, newDuration * root.timeScale)
}
}
}
}anchors.margins: -2 widens the clickable area beyond the visible handle, preventStealing: true stops other mouse areas from grabbing the drag, and tracking global coordinates keeps the resize accurate across zoom levels. commit ↗
zone-to-marker conversion
i also bridged kdenlive's existing zones and the new markers: define a zone in the monitor and turn it straight into a range marker, instead of building one by hand. it works two ways — a "create range marker from zone" entry in the monitor ruler's right-click menu, and a quick action from the main window. the conversion lives in a new createRangeMarkerFromZone method on MonitorProxy:
bool MonitorProxy::createRangeMarkerFromZone(const QString &comment, int type)
{
// Validate zone boundaries
if (m_zoneIn <= 0 || m_zoneOut <= 0 || m_zoneIn >= m_zoneOut) {
return false;
}
std::shared_ptr<MarkerListModel> markerModel;
// ... pick the marker model based on monitor type (clip vs project) ...
if (!markerModel) {
return false;
}
// Convert zone to range marker
GenTime startPos(m_zoneIn, pCore->getCurrentFps());
GenTime duration(m_zoneOut - m_zoneIn, pCore->getCurrentFps());
QString markerComment = comment.isEmpty() ? i18n("Zone marker") : comment;
if (type == -1) {
type = KdenliveSettings::default_marker_type();
}
return markerModel->addRangeMarker(startPos, duration, markerComment, type);
}it validates the zone (start before end, no zero-duration), auto-names the marker "zone marker" if you don't, and gives status feedback on success or error. timeline controller methods like resizeGuide and suggestSnapPoint let all of this cooperate with kdenlive's existing timeline operations, and the backend resize goes through a resizeMarker method that re-applies the minimum-one-frame constraint before committing the edit. commit ↗
what this means for kdenlive users
before, you could only place markers at points, and to mark a section you needed several of them and had to remember which belonged together. now you can:
- mark complete sections — one marker that spans an entire intro, chapter, or highlight
- see at a glance which parts of a project are marked, and how long each section runs
- resize markers to adjust section boundaries without recreating them
- share projects with clear, visual section markers
this gsoc was an incredible journey — from the idea of extending the marker system to a fully featured range-marker interface, every step taught me something. i'm grateful to my mentor jean-baptiste mardelle and the whole kdenlive community for the guidance and feedback throughout the merge request.