Skip to content

Commit 6389066

Browse files
committed
[*] refactor video editor
1 parent 2eeb454 commit 6389066

37 files changed

+509
-85
lines changed

wayshot/ui/panel/def.slint

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import {
55
VEFilter,
66
TransformDetail,
77
CropDetail,
8+
ZoomDetail,
89
FilterType,
910
} from "./desktop/video-editor/filter.slint";
1011
import {
@@ -64,4 +65,4 @@ import {
6465
VideoEditorLayerImage,
6566
} from "../store.slint";
6667

67-
export { Theme, Logic, Store, Util, Icons, VEFilter, StartupTab, TabIndex, PopupIndex, SettingPreference, SettingBackup, SettingDetailIndex, MobileSettingDetailIndex, DeviceType, MobileTabIndex, SettingRecorder, SettingCursorTracker, TransitionType, SettingPlayer, FeatureType, SettingShareScreen, SettingShareScreenClient, ConnectionStatus, SettingPushStream, SettingCamera, MixPositionWithPadding, MixPositionWithPaddingTag, RealtimeImageEffect, BackgroundRemoverModel, Downloader, DownloaderState, Transcribe, TranscribeProgressType, FileType, Subtitle, SettingTranscribe, SettingAiModel, VideoEditorTrackType, VideoEditorTrackSegment, VideoEditorTrack, VideoEditorTracksManager, SelectedTrackIndex, SelectedSegmentIndex, VideoEditorPlaylistItem, MediaType, VideoEditorExportQueueItem, VideoEditorExportVideoConfig, VideoEditorExportAudioConfig, SubtitleType, VideoEditorSubtitle, VideoEditorNewProjectConfig, VideoEditorPreviewConfig, VideoPreviewSize, VideoEditorPreferenceTrackConfig, VideoEditorPreferenceCacheConfig, VideoEditorPreferenceConfig, VideoEditorSegmentMetadata, VideoEditorRecordAudioConfig, VideoEditorLayerImage, TransformDetail, CropDetail, FilterType }
68+
export { Theme, Logic, Store, Util, Icons, VEFilter, StartupTab, TabIndex, PopupIndex, SettingPreference, SettingBackup, SettingDetailIndex, MobileSettingDetailIndex, DeviceType, MobileTabIndex, SettingRecorder, SettingCursorTracker, TransitionType, SettingPlayer, FeatureType, SettingShareScreen, SettingShareScreenClient, ConnectionStatus, SettingPushStream, SettingCamera, MixPositionWithPadding, MixPositionWithPaddingTag, RealtimeImageEffect, BackgroundRemoverModel, Downloader, DownloaderState, Transcribe, TranscribeProgressType, FileType, Subtitle, SettingTranscribe, SettingAiModel, VideoEditorTrackType, VideoEditorTrackSegment, VideoEditorTrack, VideoEditorTracksManager, SelectedTrackIndex, SelectedSegmentIndex, VideoEditorPlaylistItem, MediaType, VideoEditorExportQueueItem, VideoEditorExportVideoConfig, VideoEditorExportAudioConfig, SubtitleType, VideoEditorSubtitle, VideoEditorNewProjectConfig, VideoEditorPreviewConfig, VideoPreviewSize, VideoEditorPreferenceTrackConfig, VideoEditorPreferenceCacheConfig, VideoEditorPreferenceConfig, VideoEditorSegmentMetadata, VideoEditorRecordAudioConfig, VideoEditorLayerImage, TransformDetail, CropDetail, ZoomDetail, FilterType }

wayshot/ui/panel/desktop/video-editor/preview/preview-crop-layers.slint

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import {
66
Icons,
77
VideoPreviewSize,
88
VideoEditorLayerImage,
9-
TransformDetail,
109
CropDetail,
1110
} from "../../../def.slint";
1211
import { VEFilter, FilterType } from "../filter.slint";
Lines changed: 352 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,352 @@
1+
import {
2+
Theme,
3+
Store,
4+
Logic,
5+
Util,
6+
Icons,
7+
VideoPreviewSize,
8+
VideoEditorLayerImage,
9+
ZoomDetail,
10+
} from "../../../def.slint";
11+
import { VEFilter, FilterType } from "../filter.slint";
12+
import { IconBtn, Divider, Label } from "../../../../base/widgets.slint";
13+
import { MoveHandle } from "move-handle.slint";
14+
15+
export component PreviewZoomLayers inherits Rectangle {
16+
in-out property <[VideoEditorLayerImage]> layers;
17+
private property <float> width-hight-rate: self.width / self.height;
18+
private property <FilterType> selected-filter-type <=> VEFilter.selected-filter.ty;
19+
20+
for index in layers.length: rec := Rectangle {
21+
x: 0;
22+
y: 0;
23+
24+
private property <VideoEditorLayerImage> layer: layers[layers.length - 1 - index];
25+
private property <ZoomDetail> crop: layer.crop;
26+
private property <bool> is-edited-track: Store.video-editor-current-edited-track-index == layer.track-index;
27+
28+
container-rec := Rectangle {
29+
width: img.width;
30+
height: img.height;
31+
32+
img := Image {
33+
private property <length> parent-width: rec.width;
34+
private property <length> parent-height: rec.height;
35+
private property <float> width-hight-rate: self.source.width / self.source.height;
36+
37+
changed parent-width => {
38+
self.update-size();
39+
}
40+
41+
changed parent-height => {
42+
self.update-size();
43+
}
44+
45+
function update-size() {
46+
if (root.width-hight-rate > self.width-hight-rate) {
47+
self.height = root.height;
48+
self.width = self.height * self.width-hight-rate;
49+
} else {
50+
self.width = root.width;
51+
self.height = self.width / self.width-hight-rate;
52+
}
53+
}
54+
55+
function apply-resize(diff-x: length, diff-y: length, corner: int, orig-left: float, orig-top: float, orig-width: float, orig-height: float) {
56+
let norm-x = diff-x / self.width;
57+
let norm-y = diff-y / self.height;
58+
let min-width-norm = Theme.icon-size / self.width;
59+
let min-height-norm = Theme.icon-size / self.height;
60+
61+
62+
// corner 1: top-left, 2: top-right, 3: bottom-right, 4: bottom-left
63+
if (corner == 1) { // top-left
64+
let new-left = orig-left + norm-x;
65+
let new-top = orig-top + norm-y;
66+
let new-width = orig-width - norm-x;
67+
let new-height = orig-height - norm-y;
68+
if (new-left >= 0 && new-width >= min-width-norm) {
69+
crop.left = new-left;
70+
crop.width = new-width;
71+
}
72+
if (new-top >= 0 && new-height >= min-height-norm) {
73+
crop.top = new-top;
74+
crop.height = new-height;
75+
}
76+
} else if (corner == 2) { // top-right
77+
let new-width = orig-width + norm-x;
78+
let new-top = orig-top + norm-y;
79+
let new-height = orig-height - norm-y;
80+
if (crop.left + new-width <= 1.0 && new-width >= min-width-norm) {
81+
crop.width = new-width;
82+
}
83+
if (new-top >= 0 && new-height >= min-height-norm) {
84+
crop.top = new-top;
85+
crop.height = new-height;
86+
}
87+
} else if (corner == 3) { // bottom-right
88+
let new-width = orig-width + norm-x;
89+
let new-height = orig-height + norm-y;
90+
if (crop.left + new-width <= 1.0 && new-width >= min-width-norm) {
91+
crop.width = new-width;
92+
}
93+
if (crop.top + new-height <= 1.0 && new-height >= min-height-norm) {
94+
crop.height = new-height;
95+
}
96+
} else if (corner == 4) { // bottom-left
97+
let new-left = orig-left + norm-x;
98+
let new-width = orig-width - norm-x;
99+
let new-height = orig-height + norm-y;
100+
if (new-left >= 0 && new-width >= min-width-norm) {
101+
crop.left = new-left;
102+
crop.width = new-width;
103+
}
104+
if (crop.top + new-height <= 1.0 && new-height >= min-height-norm) {
105+
crop.height = new-height;
106+
}
107+
}
108+
}
109+
110+
init => {
111+
self.update-size();
112+
}
113+
114+
source: layer.original-image;
115+
image-fit: ImageFit.contain;
116+
}
117+
118+
if is-edited-track: Rectangle {
119+
width: img.width;
120+
height: img.height;
121+
122+
private property <length> min-crop-size: Theme.icon-size;
123+
124+
// Display values with minimum size clamping (only for visual display)
125+
private property <length> display-left: crop.left * img.width;
126+
private property <length> display-top: crop.top * img.height;
127+
private property <length> display-width: max(min-crop-size, min(crop.width * img.width, img.width - display-left));
128+
private property <length> display-height: max(min-crop-size, min(crop.height * img.height, img.height - display-top));
129+
private property <length> display-right: display-left + display-width;
130+
private property <length> display-bottom: display-top + display-height;
131+
132+
// Store original crop values when drag starts
133+
private property <float> orig-left;
134+
private property <float> orig-top;
135+
private property <float> orig-width;
136+
private property <float> orig-height;
137+
138+
// Store initial mouse position (relative to img) when drag starts for handles
139+
private property <length> handle-pressed-x;
140+
private property <length> handle-pressed-y;
141+
142+
TouchArea {
143+
width: img.width;
144+
height: img.height;
145+
mouse-cursor: self.pressed ? grabbing : grab;
146+
147+
private property <float> move-orig-left;
148+
private property <float> move-orig-top;
149+
150+
pointer-event(event) => {
151+
if (event.button == PointerEventButton.left && event.kind == PointerEventKind.down) {
152+
move-orig-left = crop.left;
153+
move-orig-top = crop.top;
154+
}
155+
}
156+
157+
moved => {
158+
let norm-x = (self.mouse-x - self.pressed-x) / img.width;
159+
let norm-y = (self.mouse-y - self.pressed-y) / img.height;
160+
161+
let new-left = move-orig-left + norm-x;
162+
let new-top = move-orig-top + norm-y;
163+
164+
crop.left = max(0, min(1 - crop.width, new-left));
165+
crop.top = max(0, min(1 - crop.height, new-top));
166+
}
167+
168+
changed pressed => {
169+
if (!self.pressed) {
170+
VEFilter.modify-crop-filter(VEFilter.selected-filter-index, crop, selected-filter-type);
171+
}
172+
}
173+
}
174+
175+
Rectangle {
176+
x: top-line.x;
177+
y: top-line.y;
178+
width: top-line.width;
179+
height: left-line.height;
180+
background: Theme.hover-background;
181+
opacity: Theme.golden-ratio;
182+
}
183+
184+
top-line := HorizontalLayout {
185+
x: display-left;
186+
y: display-top - 1px;
187+
width: display-width;
188+
height: 2px;
189+
alignment: space-between;
190+
191+
for index in ceil(self.width / Theme.icon-size / 2): Rectangle {
192+
width: Theme.icon-size;
193+
height: 100%;
194+
background: Theme.success-color;
195+
}
196+
}
197+
198+
bottom-line := HorizontalLayout {
199+
x: display-left;
200+
y: display-bottom - 1px;
201+
width: display-width;
202+
height: 2px;
203+
alignment: space-between;
204+
205+
for index in ceil(self.width / Theme.icon-size / 2): Rectangle {
206+
width: Theme.icon-size;
207+
height: 100%;
208+
background: Theme.success-color;
209+
}
210+
}
211+
212+
left-line := VerticalLayout {
213+
x: display-left - 1px;
214+
y: display-top;
215+
width: 2px;
216+
height: display-height;
217+
alignment: space-between;
218+
219+
for index in ceil(self.height / Theme.icon-size / 2): Rectangle {
220+
height: Theme.icon-size;
221+
width: 100%;
222+
background: Theme.success-color;
223+
}
224+
}
225+
226+
// Right line
227+
VerticalLayout {
228+
x: display-right - 1px;
229+
y: display-top;
230+
width: 2px;
231+
height: display-height;
232+
alignment: space-between;
233+
234+
for index in ceil(self.height / Theme.icon-size / 2): Rectangle {
235+
height: Theme.icon-size;
236+
width: 100%;
237+
background: Theme.success-color;
238+
}
239+
}
240+
241+
// Top-left handle
242+
top-left-handle := MoveHandle {
243+
x: display-left - self.width / 2;
244+
y: display-top - self.height / 2;
245+
text: "1";
246+
247+
pressed-changed => {
248+
orig-left = crop.left;
249+
orig-top = crop.top;
250+
orig-width = crop.width;
251+
orig-height = crop.height;
252+
253+
handle-pressed-x = self.x + self.mouse-x;
254+
handle-pressed-y = self.y + self.mouse-y;
255+
}
256+
257+
moved => {
258+
let current-x = self.x + self.mouse-x;
259+
let current-y = self.y + self.mouse-y;
260+
img.apply-resize(current-x - handle-pressed-x, current-y - handle-pressed-y, 1,
261+
orig-left, orig-top, orig-width, orig-height);
262+
}
263+
264+
released => {
265+
VEFilter.modify-crop-filter(VEFilter.selected-filter-index, crop, selected-filter-type);
266+
}
267+
}
268+
269+
// Top-right handle
270+
top-right-handle := MoveHandle {
271+
x: display-right - self.width / 2;
272+
y: display-top - self.height / 2;
273+
text: "2";
274+
275+
pressed-changed => {
276+
orig-left = crop.left;
277+
orig-top = crop.top;
278+
orig-width = crop.width;
279+
orig-height = crop.height;
280+
handle-pressed-x = self.x + self.mouse-x;
281+
handle-pressed-y = self.y + self.mouse-y;
282+
}
283+
284+
moved => {
285+
let current-x = self.x + self.mouse-x;
286+
let current-y = self.y + self.mouse-y;
287+
img.apply-resize(current-x - handle-pressed-x, current-y - handle-pressed-y, 2,
288+
orig-left, orig-top, orig-width, orig-height);
289+
}
290+
291+
released => {
292+
VEFilter.modify-crop-filter(VEFilter.selected-filter-index, crop, selected-filter-type);
293+
}
294+
}
295+
296+
// Bottom-right handle
297+
bottom-right-handle := MoveHandle {
298+
x: display-right - self.width / 2;
299+
y: display-bottom - self.height / 2;
300+
text: "3";
301+
302+
pressed-changed => {
303+
orig-left = crop.left;
304+
orig-top = crop.top;
305+
orig-width = crop.width;
306+
orig-height = crop.height;
307+
handle-pressed-x = self.x + self.mouse-x;
308+
handle-pressed-y = self.y + self.mouse-y;
309+
}
310+
311+
moved => {
312+
let current-x = self.x + self.mouse-x;
313+
let current-y = self.y + self.mouse-y;
314+
img.apply-resize(current-x - handle-pressed-x, current-y - handle-pressed-y, 3,
315+
orig-left, orig-top, orig-width, orig-height);
316+
}
317+
318+
released => {
319+
VEFilter.modify-crop-filter(VEFilter.selected-filter-index, crop, selected-filter-type);
320+
}
321+
}
322+
323+
// Bottom-left handle
324+
bottom-left-handle := MoveHandle {
325+
x: display-left - self.width / 2;
326+
y: display-bottom - self.height / 2;
327+
text: "4";
328+
329+
pressed-changed => {
330+
orig-left = crop.left;
331+
orig-top = crop.top;
332+
orig-width = crop.width;
333+
orig-height = crop.height;
334+
handle-pressed-x = self.x + self.mouse-x;
335+
handle-pressed-y = self.y + self.mouse-y;
336+
}
337+
338+
moved => {
339+
let current-x = self.x + self.mouse-x;
340+
let current-y = self.y + self.mouse-y;
341+
img.apply-resize(current-x - handle-pressed-x, current-y - handle-pressed-y, 4,
342+
orig-left, orig-top, orig-width, orig-height);
343+
}
344+
345+
released => {
346+
VEFilter.modify-crop-filter(VEFilter.selected-filter-index, crop, selected-filter-type);
347+
}
348+
}
349+
}
350+
}
351+
}
352+
}

wayshot/ui/panel/desktop/video-editor/right-panel/filter/audio/compressor.slint

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,5 +116,7 @@ export component Compressor inherits Rectangle {
116116
}
117117
}
118118
}
119+
120+
VerticalLayout { }
119121
}
120122
}

wayshot/ui/panel/desktop/video-editor/right-panel/filter/audio/fade-in.slint

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ export component FadeIn inherits Rectangle {
4040
}
4141
}
4242
}
43+
44+
VerticalLayout { }
4345
}
4446
}
4547

0 commit comments

Comments
 (0)