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
127
128
129
130
131
132
133
134
mod choose_something;
mod colors;
mod lasso;
mod load;
mod popup;
mod prompt_input;
pub(crate) mod screenshot;
mod url;
pub(crate) mod warper;

use anyhow::Result;

pub use choose_something::ChooseSomething;
pub use colors::{ColorLegend, ColorScale, DivergingScale};
pub use lasso::{Lasso, PolyLineLasso};
pub use load::{FileLoader, FutureLoader, RawBytes};
pub use popup::PopupMsg;
pub use prompt_input::PromptInput;
pub use url::URLManager;

use crate::{Color, GfxCtx};
use geom::Polygon;

/// Store a cached key/value pair, only recalculating when the key changes.
pub struct Cached<K: PartialEq + Clone, V> {
    contents: Option<(K, V)>,
}

impl<K: PartialEq + Clone, V> Cached<K, V> {
    pub fn new() -> Cached<K, V> {
        Cached { contents: None }
    }

    /// Get the current key.
    pub fn key(&self) -> Option<K> {
        self.contents.as_ref().map(|(k, _)| k.clone())
    }

    /// Get the current value.
    pub fn value(&self) -> Option<&V> {
        self.contents.as_ref().map(|(_, v)| v)
    }

    /// Get the current value, mutably.
    pub fn value_mut(&mut self) -> Option<&mut V> {
        self.contents.as_mut().map(|(_, v)| v)
    }

    /// Update the value if the key has changed.
    pub fn update<F: FnMut(K) -> V>(&mut self, key: Option<K>, mut produce_value: F) {
        if let Some(new_key) = key {
            if self.key() != Some(new_key.clone()) {
                self.contents = Some((new_key.clone(), produce_value(new_key)));
            }
        } else {
            self.contents = None;
        }
    }

    /// `update` is preferred, but sometimes `produce_value` needs to borrow the same struct that
    /// owns this `Cached`. In that case, the caller can manually check `key` and call this.
    pub fn set(&mut self, key: K, value: V) {
        self.contents = Some((key, value));
    }

    pub fn clear(&mut self) {
        self.contents = None;
    }

    /// Clears the current pair and returns it.
    pub fn take(&mut self) -> Option<(K, V)> {
        self.contents.take()
    }
}

impl<K: PartialEq + Clone, V> Default for Cached<K, V> {
    fn default() -> Self {
        Cached::new()
    }
}

pub fn open_browser<I: AsRef<str>>(url: I) {
    let _ = webbrowser::open(url.as_ref());
}

fn grey_out_map(g: &mut GfxCtx) {
    // This is a copy of grey_out_map from map_gui, with no dependencies on App
    g.fork_screenspace();
    g.draw_polygon(
        Color::BLACK.alpha(0.6),
        Polygon::rectangle(g.canvas.window_width, g.canvas.window_height),
    );
    g.unfork();
}

/// Only works on native
pub fn set_clipboard(x: String) {
    #[cfg(not(target_arch = "wasm32"))]
    {
        use clipboard::{ClipboardContext, ClipboardProvider};
        if let Err(err) =
            ClipboardProvider::new().and_then(|mut ctx: ClipboardContext| ctx.set_contents(x))
        {
            error!("Copying to clipboard broke: {}", err);
        }
    }

    #[cfg(target_arch = "wasm32")]
    {
        let _ = x;
    }
}

pub fn get_clipboard() -> Result<String> {
    #[cfg(not(target_arch = "wasm32"))]
    {
        use clipboard::{ClipboardContext, ClipboardProvider};
        // TODO The clipboard crate uses old nightly Errors. Converting to anyhow is weird.
        let mut ctx: ClipboardContext = match ClipboardProvider::new() {
            Ok(ctx) => ctx,
            Err(err) => bail!("{}", err),
        };
        let contents = match ctx.get_contents() {
            Ok(contents) => contents,
            Err(err) => bail!("{}", err),
        };
        Ok(contents)
    }

    #[cfg(target_arch = "wasm32")]
    {
        bail!("Unsupported on web");
    }
}