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
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
use glutin::dpi::LogicalSize;

/// Configuration for "advanced" use cases, when [`gotta_go_fast`][crate::gotta_go_fast] isn't doing
/// what you need.
///
/// The following pattern is recommended when creating a config:
///
/// ```
/// use mini_gl_fb::config;
/// use mini_gl_fb::glutin::dpi::LogicalSize;
///
/// let config = config! {
///     /* specify whichever fields you need to set, for example: */
///     window_size: LogicalSize::new(100.0, 100.0),
///     resizable: true,
/// };
/// ```
///
/// Since `Config` is `#[non_exhaustive]`, you cannot construct it directly, and can only obtain one
/// from a trait like [`Default`]. The [`config!`][config] macro makes it much less tedious to
/// construct custom configs. See its documentation for more information.
///
/// Alternatively, you can choose to use the builder pattern instead:
///
/// ```
/// use mini_gl_fb::ConfigBuilder;
///
/// let config = ConfigBuilder::default()
///     .invert_y(false)
///     .build();
/// ```
///
/// If there's a config option you want to see or think is missing, please open an issue!
#[non_exhaustive]
#[derive(Clone, PartialEq, Debug, Builder)]
#[builder(default)]
#[builder(build_fn(skip))]
pub struct Config {
    /// Sets the pixel dimensions of the buffer. The buffer will automatically stretch to fill the
    /// whole window. By default this will be the same as the window_size.
    pub buffer_size: Option<LogicalSize<u32>>,
    /// If this is true, the window created by mini_gl_fb will be set to resizable. This can be
    /// changed later. Please note that the buffer itself will not be automatically resized, only
    /// the viewport.
    pub resizable: bool,
    /// The title of the window that will be created.
    pub window_title: String,
    /// The logical size of the window that gets created. On HiDPI screens the actual size may be
    /// larger than this
    pub window_size: LogicalSize<f64>,
    /// By default, the origin of the buffer is the bottom-left. This is known as "inverted Y", as
    /// most screen-space coordinate systems begin from the top-left. By explicitly setting this
    /// option to `false`, you can switch to screen-space coordinates rather than OpenGL
    /// coordinates. Otherwise, you will have to invert all mouse events received from winit/glutin.
    pub invert_y: bool
}

impl ConfigBuilder {
    /// Builds a new [`Config`].
    pub fn build(&self) -> Config {
        let mut config = Config::default();

        macro_rules! fields {
            ($($n:ident),+) => {
                $(
                if let Some($n) = &self.$n {
                    config.$n = $n.clone();
                }
                )+
            }
        }

        // I guess this is better than implementing the entire builder by hand
        fields!(buffer_size, resizable, window_title, window_size, invert_y);

        config
    }
}

impl Default for Config {
    fn default() -> Self {
        Config {
            buffer_size: None,
            resizable: false,
            // :^)
            window_title: String::from("Super Mini GL Framebufferer 3!"),
            window_size: LogicalSize::new(600.0, 480.0),
            invert_y: true
        }
    }
}


/// The `config!` macro is intended to make it easy for us to add new fields in the future while
/// staying backwards-compatible. This is done by making [`Config`] `#[non_exhaustive]` but still
/// providing [`Default`], so that users can obtain the defaults and modify it to their liking. The
/// `config!` macro automates this, and makes custom configs just as easy as constructing `Config`
/// directly would be.
///
/// You can use the macro like this:
///
/// ```
/// # use mini_gl_fb::config;
/// #
/// let config = config! {
///     resizable: true,
///     invert_y: false
/// };
///
/// assert_eq!(config.resizable, true);
/// assert_eq!(config.invert_y, false);
/// ```
///
/// As you can see, it's almost identical to a struct construction. You just use this macro in place
/// of `Config`. As such, it has a minimal impact on user code. That invocation roughly expands to:
///
/// ```
/// # use mini_gl_fb::Config;
/// #
/// let config = {
///     let mut config = Config::default();
///     config.resizable = true;
///     config.invert_y = false;
///     config
/// };
/// ```
///
/// This way, adding new fields will not affect existing code.
///
/// You can also create a copy of an existing config, with only a couple options changed:
///
/// ```
/// # use mini_gl_fb::config;
/// #
/// let original = config! {};
/// let copy = config! {
///     invert_y: false,
///     ..original
/// };
///
/// assert_eq!(original.invert_y, true);
/// assert_eq!(copy.invert_y, false);
/// ```
#[macro_export]
macro_rules! config {
    {$($k:ident: $v:expr),+,..$from:expr$(,)?} => {{
        let mut config: $crate::Config = ::std::clone::Clone::clone(&$from);
        $(config.$k = $v;
        )*config
    }};
    {$($k:ident: $v:expr),+$(,)?} => {{
        let mut config: $crate::Config = ::std::default::Default::default();
        $(config.$k = $v;
        )*config
    }};
    {} => { <$crate::Config as ::std::default::Default>::default() }
}