makepad_studio/
snapshot.rs

1
2use {
3    crate::{
4        app::{AppData},
5        file_system::file_system::SnapshotImageData,
6        makepad_widgets::*,
7    },
8    makepad_platform::studio::{StudioToApp,StudioScreenshotRequest},
9    std::{
10        env,
11    },
12};
13
14live_design!{
15    use link::shaders::*;
16    use link::widgets::*;
17    use link::theme::*;
18    use makepad_widgets::designer_theme::*;
19    
20    SnapshotItem = <RoundedView> {
21        height: Fit, width: Fill
22        draw_bg:{color:#2}
23        flow:Down
24        align:{x:0.5},
25        
26        message = <Label>{text:"test", width:Fill, height:Fit}
27        run_button = <ButtonFlat> {
28            width: Fit,
29            height: Fit,
30            padding: <THEME_MSPACE_2> {}
31            margin: 0.
32            icon_walk: {
33                width: 12, height: Fit,
34                margin: { left: 10 }
35            }
36                                                                        
37            draw_icon: {
38                color: (THEME_COLOR_U_4),
39                svg_file: dep("crate://self/resources/icons/icon_run.svg"),
40            }
41            icon_walk: { width: 9. }
42        }
43        image = <Image> {
44            width: 200,
45            height: 100
46            margin:{top:10,bottom:10}
47            //min_width: 1920,
48            //min_height: 1080,
49            fit: Horizontal,
50            draw_bg: {
51                instance hover: 0.0
52                instance down: 0.0
53                fn pixel(self) -> vec4 {
54                    let sdf = Sdf2d::viewport(self.pos * self.rect_size)
55                    sdf.box(1, 1, self.rect_size.x - 2, self.rect_size.y - 2, 4.0)
56                    let max_scale = vec2(0.92);
57                    let scale = mix(vec2(1.0), max_scale, self.hover);
58                    let pan = mix(vec2(0.0), (vec2(1.0) - max_scale) * 0.5, self.hover)* self.image_scale;
59                    let color = self.get_color_scale_pan(scale * self.image_scale, pan + self.image_pan) + mix(vec4(0.0), vec4(0.1), self.down);
60                    if color.a<0.0001{
61                        color = #3
62                    }
63                    sdf.fill_keep(color);
64                    sdf.stroke(
65                        mix(mix(#x0000, #x0006, self.hover), #xfff2, self.down),
66                        1.0
67                    )
68                         
69                    return sdf.result
70                }
71            }
72        }
73    }
74    
75    pub Snapshot = {{Snapshot}} <RectView> {
76        height: Fill, width: Fill,
77        //draw_bg: {color: (THEME_COLOR_BG_CONTAINER)}
78        flow: Down,
79        <DockToolbar> {
80            height: Fit
81            content = {
82                height: Fit
83                padding:{top:1}
84                spacing: (THEME_SPACE_2)
85                flow: Down
86                <View>{
87                    height: Fit
88                    spacing: 5
89                    roots_dropdown = <DropDownFlat>{ width: Fit, popup_menu_position: BelowInput }
90                    snapshot_button = <ButtonFlat>{text:"Snapshot"}
91                    <Filler> {}
92                    <ToggleFlat>{text:"Auto"}
93                }
94                message_input = <TextInputFlat>{empty_text:"Description"}
95            }
96        }
97        list = <PortalList> {
98            capture_overload: false,
99            grab_key_focus: false
100            auto_tail: true
101            drag_scrolling: false
102            max_pull_down: 0,
103            height: Fill, width: Fill,
104            flow: Down
105            SnapshotItem = <SnapshotItem> {}
106            Empty = <SolidView> {
107                cursor: Default
108                draw_bg:{color:#44}
109                width: Fill
110                height: 80,
111            }
112        }
113    }
114}
115
116#[derive(Clone, Debug, DefaultNone)]
117pub enum SnapshotAction {
118    Load(String),
119    None
120}
121
122#[derive(Live, LiveHook, Widget)]
123pub struct Snapshot{
124    #[deref] view:View,
125    #[rust] request_id: u64,
126}
127
128impl Snapshot{
129    fn draw_snapshots(&mut self, cx: &mut Cx2d, list:&mut PortalList, scope:&mut Scope, root_id:usize){
130        let data = scope.data.get_mut::<AppData>().unwrap();
131        let file_system = &mut data.file_system;
132        let git_log = file_system.git_logs.get(root_id as usize).unwrap();
133        list.set_item_range(cx, 0, git_log.commits.len());
134        while let Some(item_id) = list.next_visible_item(cx) {
135            let item = if let Some(commit) = git_log.commits.get(item_id){
136                let item = list.item(cx, item_id, live_id!(SnapshotItem)).as_view();
137                item.label(id!(message)).set_text(cx, &commit.message);
138                // lets construct a snapshot image filepath from the commit message
139                // check if we have a image path or not
140                let image = item.image(id!(image));
141                
142                let load = match file_system.snapshot_image_data.borrow().get(&commit.hash){
143                    Some(SnapshotImageData::Loading)=>{
144                        image.set_visible(cx, true);
145                        false
146                    }
147                    Some(SnapshotImageData::Error)=>{
148                        image.set_visible(cx, false);
149                        false
150                    }
151                    Some(SnapshotImageData::Loaded{data, path})=>{
152                        image.set_visible(cx, true);
153                        image.load_image_from_data_async(cx, &path, data.clone()).ok();
154                        false
155                    }
156                    None=>true
157                };
158                if load{ 
159                    file_system.file_client.load_snapshot_image(&git_log.root, &commit.hash);
160                }
161                item
162            }
163            else{
164                list.item(cx, item_id, live_id!(Empty)).as_view()
165            };
166            item.draw_all(cx, &mut Scope::empty());
167        }
168    }
169    
170    fn load_snapshot(&mut self, _cx:&mut Cx, data:&mut AppData, item_id:usize){
171        let root_id = self.drop_down(id!(roots_dropdown)).selected_item();
172        let git_log = data.file_system.git_logs.get(root_id).unwrap();
173        if let Some(commit) = git_log.commits.get(item_id){
174            data.file_system.load_snapshot(git_log.root.clone(), commit.hash.clone());
175        }
176    }
177    
178    fn make_snapshot(&mut self, _cx:&mut Cx, data:&mut AppData){
179        let root_id = self.drop_down(id!(roots_dropdown)).selected_item();
180        let git_log = data.file_system.git_logs.get(root_id).unwrap();
181        // we should find all active build ids with the same root
182                        
183        let mut iter = data.build_manager.active.builds_with_root(git_log.root.clone());
184        if let Some(item) = iter.next(){
185            // we should do a shell git commit at the right path
186            let message = self.view(id!(message_input)).text();
187            if message.len() == 0{
188                return
189            }
190            data.file_system.create_snapshot(git_log.root.clone(), message);
191            data.build_manager.active_build_websockets.lock().unwrap().borrow_mut().send_studio_to_app(*item.0, StudioToApp::Screenshot(StudioScreenshotRequest{
192                kind_id: 0,
193                request_id:self.request_id
194            }));
195            self.request_id += 1;
196        }
197    }
198}
199
200impl Widget for Snapshot {
201    fn draw_walk(&mut self, cx: &mut Cx2d, scope:&mut Scope, walk:Walk)->DrawStep{
202        let data = scope.data.get_mut::<AppData>().unwrap();
203        
204        let dd = self.drop_down(id!(roots_dropdown));
205        let mut i = data.file_system.git_logs.iter();
206        dd.set_labels_with(cx, |label|{
207            i.next().map(|m| label.push_str(&m.root));
208        });
209        let root_id = dd.selected_item();
210        while let Some(step) = self.view.draw_walk(cx, scope, walk).step(){
211            if let Some(mut list) = step.as_portal_list().borrow_mut(){
212                self.draw_snapshots(cx, &mut *list, scope, root_id)
213            }
214        }
215        DrawStep::done()
216    }
217    
218    fn handle_event(&mut self, cx: &mut Cx, event: &Event, scope: &mut Scope){
219        let snapshots = self.view.portal_list(id!(list));
220        self.view.handle_event(cx, event, scope);
221        let data = scope.data.get_mut::<AppData>().unwrap();
222        if let Event::Actions(actions) = event{
223            if self.view.button(id!(snapshot_button)).clicked(actions){
224                self.make_snapshot(cx, data);
225            }
226            if let Some(_search) = self.view.text_input(id!(search_input)).changed(&actions){
227            }
228            for (item_id, _item) in snapshots.items_with_actions(&actions) {
229                if let Some(wa) = actions.widget_action(id!(run_button)){
230                    if wa.widget().as_button().pressed(actions){
231                        self.load_snapshot(cx, data, item_id);
232                    }
233                }
234            }
235        }
236    }
237}
238
239impl SnapshotRef{
240    pub fn set_message(&self, cx:&mut Cx, message:String){
241        if let Some(inner) = self.borrow_mut(){
242            inner.view(id!(message_input)).set_text(cx, &message);
243        }
244    }
245}