aboutsummaryrefslogtreecommitdiff
path: root/main/src/ui/conversation_content_view/file_transmission_progress.vala
blob: a69b547f62ed37469bebdbe44c539664ee21d554 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
using Gee;
using Gdk;
using Gtk;
using Xmpp;

using Dino.Entities;

namespace Dino.Ui {

    public class FileTransmissionProgress : Adw.Bin {

        public enum State {
            UNKNOWN_SOURCE,
            DOWNLOAD_NOT_STARTED,
            DOWNLOAD_NOT_STARTED_FAILED_BEFORE,
            DOWNLOADING,
            UPLOADING
        }

        public int64 file_size { get; set; }
        public int64 transferred_size { get; set; }
        public State state { get; set; }

        private const int LINE_WIDTH = 4;

        private Button button = new Button();

        private Adw.TimedAnimation progress_animation;

        construct {
            add_css_class("circular-osd");
            button.add_css_class("circular");
            button.margin_start = button.margin_end = button.margin_top = button.margin_bottom = LINE_WIDTH;
            this.set_child(button);

            this.button.clicked.connect(on_button_clicked);

            this.notify["transferred-size"].connect(update_progress);
            this.notify["state"].connect(on_state_changed);
            on_state_changed();

            setup_animation();
        }

        private void setup_animation() {
            progress_animation = new Adw.TimedAnimation(this, 0.0, 0.0, 250, new Adw.CallbackAnimationTarget(queue_draw)) { easing = Adw.Easing.LINEAR };
            progress_animation.done.connect(update_progress);
        }

        private void on_state_changed() {
            sensitive = state != UNKNOWN_SOURCE;

            switch (this.state) {
                case UNKNOWN_SOURCE:
                case DOWNLOAD_NOT_STARTED:
                    button.icon_name = "document-save-symbolic";
                    break;
                case DOWNLOADING:
                case UPLOADING:
                    button.icon_name = "small-x-symbolic";
                    break;
                case DOWNLOAD_NOT_STARTED_FAILED_BEFORE:
                    button.icon_name = "dialog-warning-symbolic";
                    break;
            }
        }

        private void update_progress() {
            if (file_size == 0 || progress_animation == null) return;
            double next_value = (double)transferred_size / (double)file_size;
            if (progress_animation != null && progress_animation.value_to != next_value) {
                progress_animation.value_from = progress_animation.value;
                progress_animation.value_to = next_value;
                progress_animation.reset();
                progress_animation.play();
            }
        }

        private static extern Gsk.Path create_progress_arc(Gsk.Path circle, float percentage);

        public override void snapshot(Gtk.Snapshot snapshot) {
            base.snapshot(snapshot);

            if (state == State.DOWNLOADING || state == State.UPLOADING) {
                Gdk.RGBA fg_color = get_color();
                get_style_context().lookup_color("accent_color", out fg_color);

                float radius = float.max(int.min(get_width(), get_height()) / 2, 1);
                float line_width = (float) LINE_WIDTH;

                var stroke = new Gsk.Stroke(line_width);
                stroke.set_line_cap(Gsk.LineCap.ROUND);

                snapshot.translate({get_width() / 2, get_height() / 2});

                var builder = new Gsk.PathBuilder();
                builder.add_circle({0, 0}, radius - line_width / 2);
                var circle_path = builder.to_path();
                if (progress_animation.value > 0.01) {
                    var arc_path = create_progress_arc(circle_path, (float) progress_animation.value);
                    snapshot.append_stroke(arc_path, stroke, fg_color);
                }
            }
        }

        private void on_button_clicked() {
            switch (this.state) {
                case UNKNOWN_SOURCE:
                    break;
                case DOWNLOAD_NOT_STARTED_FAILED_BEFORE:
                case DOWNLOAD_NOT_STARTED:
                    this.activate_action("file.download", null);
                    break;
                case DOWNLOADING:
                case UPLOADING:
                    this.activate_action("file.cancel", null);
                    break;
            }
        }

        public override void dispose() {
            progress_animation = null;
            base.dispose();
        }
    }
}