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
use arcdps::imgui::{Id, TableColumnFlags, TableColumnSetup, TableFlags, TableToken, Ui};
use std::{ffi::c_void, mem};
use windows::Win32::Graphics::Direct3D11::ID3D11ShaderResourceView;

/// Renders a table with (optional) icon headers.
pub fn table_with_icons<'ui, N>(
    ui: &Ui<'ui>,
    label: impl AsRef<str>,
    columns: &[TableIconColumn<N>],
    flags: TableFlags,
    show_icons: bool,
) -> Option<TableToken<'ui>>
where
    N: AsRef<str>,
{
    table_with_icons_sizing(ui, label, columns, flags, show_icons, [0.0, 0.0], 0.0)
}

/// Renders a table with (optional) icon headers and sizing parameters.
pub fn table_with_icons_sizing<'ui, N>(
    ui: &Ui<'ui>,
    label: impl AsRef<str>,
    columns: &[TableIconColumn<N>],
    flags: TableFlags,
    show_icons: bool,
    outer_size: [f32; 2],
    inner_size: f32,
) -> Option<TableToken<'ui>>
where
    N: AsRef<str>,
{
    if let Some(token) =
        ui.begin_table_with_sizing(label, columns.len(), flags, outer_size, inner_size)
    {
        ui.table_setup_scroll_freeze(0, 1);
        for column in columns {
            ui.table_setup_column_with(column.as_setup());
        }

        if show_icons {
            for column in columns {
                table_header_icon(ui, column.name.as_ref(), column.icon);
            }
        } else {
            ui.table_headers_row();
        }

        Some(token)
    } else {
        None
    }
}

/// Icon type.
pub type Icon = ID3D11ShaderResourceView;

/// A table column setup with icon.
#[derive(Debug, Clone)]
pub struct TableIconColumn<'i, 'id, Name> {
    pub name: Name,
    pub icon: Option<&'i Icon>,
    pub flags: TableColumnFlags,
    pub init_width_or_weight: f32,
    pub user_id: Id<'id>,
}

impl<'i, 'id, Name> TableIconColumn<'i, 'id, Name> {
    /// Creates a new icon column.
    pub fn new(name: Name, icon: Option<&'i Icon>) -> Self {
        Self::with_flags(name, icon, Default::default())
    }

    /// Creates a new icon column with given flags.
    pub fn with_flags(name: Name, icon: Option<&'i Icon>, flags: TableColumnFlags) -> Self {
        Self::with_id(name, icon, flags, 0.0, Default::default())
    }

    /// Creates a new icon column with given width/weight and id.
    pub fn with_id(
        name: Name,
        icon: Option<&'i Icon>,
        flags: TableColumnFlags,
        init_width_or_weight: f32,
        user_id: Id<'id>,
    ) -> Self {
        Self {
            name,
            icon,
            flags,
            init_width_or_weight,
            user_id,
        }
    }

    /// Generates the equivalent [`TableColumnSetup`].
    pub fn as_setup(&self) -> TableColumnSetup<'id, &str>
    where
        Name: AsRef<str>,
    {
        TableColumnSetup {
            name: self.name.as_ref(),
            flags: self.flags,
            init_width_or_weight: self.init_width_or_weight,
            user_id: self.user_id,
        }
    }
}

impl<N> Default for TableIconColumn<'_, '_, N>
where
    N: Default,
{
    fn default() -> Self {
        Self::new(Default::default(), None)
    }
}

/// Renders a table header with icon.
pub fn table_header_icon(ui: &Ui, label: impl AsRef<str>, icon: Option<&Icon>) {
    let label = label.as_ref();
    ui.table_next_column();
    if let Some(icon) = icon {
        let size = ui.text_line_height_with_spacing();
        let [pos_x, pos_y] = ui.cursor_screen_pos();
        let top = pos_y + (ui.text_line_height() - size) / 2.0;

        ui.set_cursor_screen_pos([pos_x + size, pos_y]);
        ui.table_header(format!("##{label}"));

        // avoid dropping by transmuting the reference
        let ptr = *unsafe { mem::transmute::<_, &*const c_void>(icon) };
        ui.get_window_draw_list()
            .add_image(ptr.into(), [pos_x, top], [pos_x + size, top + size])
            .build();
    } else {
        ui.table_header(label);
    }
}