added basic keyframe UI
parent
e6595cd7fd
commit
75fc4d6bf0
@ -1,61 +1,42 @@
|
|||||||
use glium::glutin::dpi::LogicalSize;
|
use glium::glutin::dpi::LogicalSize;
|
||||||
use imgui::*;
|
//use glium::glutin::window::Window as GlutinWindow;
|
||||||
use imgui::color::ImColor32;
|
//use imgui::Ui;
|
||||||
|
|
||||||
mod app;
|
mod app;
|
||||||
mod drag_area;
|
use app::System;
|
||||||
|
|
||||||
|
mod storage;
|
||||||
|
use storage::Storage;
|
||||||
|
|
||||||
|
mod main_window;
|
||||||
|
use main_window::MainWindow;
|
||||||
|
|
||||||
|
mod ui_generic;
|
||||||
|
|
||||||
const INIT_SIZE: (f64, f64) = (640.0, 480.0);
|
const INIT_SIZE: (f64, f64) = (640.0, 480.0);
|
||||||
const MIN_SIZE: (f64, f64) = (300.0, 250.0);
|
const MIN_SIZE: (f64, f64) = (300.0, 250.0);
|
||||||
|
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let main_app = app::init("muh window", INIT_SIZE);
|
let main_app = System::init("muh window", INIT_SIZE);
|
||||||
|
|
||||||
main_app.display
|
main_app.display
|
||||||
.gl_window()
|
.gl_window()
|
||||||
.window()
|
.window()
|
||||||
.set_min_inner_size(Some(LogicalSize::new(MIN_SIZE.0, MIN_SIZE.1)));
|
.set_min_inner_size(Some(LogicalSize::new(MIN_SIZE.0, MIN_SIZE.1)));
|
||||||
|
|
||||||
let mut main_drag_area = drag_area::init(
|
let mut main_window = MainWindow::init();
|
||||||
[30.0, 30.0],
|
|
||||||
[200.0, 200.0]
|
|
||||||
);
|
|
||||||
|
|
||||||
main_app.main_loop(move |_, ui, display| {
|
let win_size = main_app.display.gl_window().window().inner_size();
|
||||||
Window::new("Node Editor")
|
main_window.size = [
|
||||||
.size([300.0, 110.0], Condition::FirstUseEver)
|
win_size.width as f32,
|
||||||
.build(&ui, || {
|
300.0
|
||||||
ui.text("Basic Drag Area");
|
];
|
||||||
ui.separator();
|
|
||||||
|
let mut main_storage = Storage::init();
|
||||||
main_drag_area.build(
|
|
||||||
&ui,
|
|
||||||
&display.gl_window().window(),
|
|
||||||
move |draw_list, area_min, area_max, offset| {
|
|
||||||
let text_pos: [f32; 2] = [
|
|
||||||
area_min[0] + offset[0],
|
|
||||||
area_min[1] + offset[1]
|
|
||||||
];
|
|
||||||
|
|
||||||
draw_list.add_text(
|
|
||||||
text_pos,
|
|
||||||
ImColor32::WHITE,
|
|
||||||
"Basic Drag Area Contents"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
draw_mouse_pos_text(ui);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
fn draw_mouse_pos_text(ui: &Ui) {
|
main_storage.add_bpm_change(0, 120);
|
||||||
let mouse_pos = ui.io().mouse_pos;
|
|
||||||
|
|
||||||
ui.text(format!(
|
main_app.main_loop(move |_, ui, display| {
|
||||||
"mouse pos: ({:.1}, {:.1})",
|
main_window.build(ui, display, &mut main_storage);
|
||||||
mouse_pos[0], mouse_pos[1]
|
});
|
||||||
));
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,112 @@
|
|||||||
|
//use glium::glutin::window::Window as GlutinWindow;
|
||||||
|
use glium::Display;
|
||||||
|
use imgui::{Window, Ui, Condition};
|
||||||
|
|
||||||
|
use crate::ui_generic::drag_area::DragArea;
|
||||||
|
use crate::ui_generic::keyframe_editor::KeyframeEditor;
|
||||||
|
use crate::storage::Storage;
|
||||||
|
|
||||||
|
const DRAG_AREA_MIN_SIZE: [f32; 2] = [300.0, 300.0];
|
||||||
|
|
||||||
|
const WIN_SIZE: [f32; 2] = [
|
||||||
|
DRAG_AREA_MIN_SIZE[0] + 15.0,
|
||||||
|
DRAG_AREA_MIN_SIZE[1] + 100.0
|
||||||
|
];
|
||||||
|
|
||||||
|
const WIN_MIN_SIZE: [f32; 2] = WIN_SIZE;
|
||||||
|
|
||||||
|
pub struct MainWindow {
|
||||||
|
pub size: [f32; 2],
|
||||||
|
default_size: [f32; 2],
|
||||||
|
min_size: [f32; 2],
|
||||||
|
editor: KeyframeEditor,
|
||||||
|
string_buf: String
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MainWindow {
|
||||||
|
pub fn init() -> MainWindow {
|
||||||
|
MainWindow {
|
||||||
|
size: WIN_SIZE,
|
||||||
|
default_size: WIN_SIZE,
|
||||||
|
min_size: WIN_MIN_SIZE,
|
||||||
|
editor: KeyframeEditor::new(DRAG_AREA_MIN_SIZE),
|
||||||
|
string_buf: String::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn build(
|
||||||
|
&mut self,
|
||||||
|
ui: &Ui,
|
||||||
|
display: &Display,
|
||||||
|
storage: &mut Storage
|
||||||
|
) {
|
||||||
|
Window::new("Main Window")
|
||||||
|
.size(self.size, Condition::FirstUseEver)
|
||||||
|
.size_constraints(self.min_size, [1000.0, 1000.0])
|
||||||
|
.build(&ui, || {
|
||||||
|
MainWindow::build_window_contents(
|
||||||
|
self,
|
||||||
|
ui,
|
||||||
|
display,
|
||||||
|
storage
|
||||||
|
)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn build_window_contents(
|
||||||
|
self: &mut MainWindow,
|
||||||
|
ui: &Ui,
|
||||||
|
display: &Display,
|
||||||
|
storage: &mut Storage
|
||||||
|
) {
|
||||||
|
let win_size = ui.window_size();
|
||||||
|
|
||||||
|
self.editor.drag_area.size = [
|
||||||
|
win_size[0] - 20.0,
|
||||||
|
win_size[1] - 100.0
|
||||||
|
];
|
||||||
|
|
||||||
|
ui.same_line();
|
||||||
|
let dec_bpm = ui.button("-");
|
||||||
|
|
||||||
|
ui.same_line();
|
||||||
|
ui.text(format!("{} bpm", storage.initial_bpm()));
|
||||||
|
|
||||||
|
ui.same_line();
|
||||||
|
let inc_bpm = ui.button("+");
|
||||||
|
|
||||||
|
if dec_bpm { storage.bpm_changes[0].bpm -= 1; }
|
||||||
|
if inc_bpm { storage.bpm_changes[0].bpm += 1; }
|
||||||
|
|
||||||
|
ui.same_line();
|
||||||
|
let kf_name_input = ui
|
||||||
|
.input_text("", &mut self.string_buf)
|
||||||
|
.hint(format!(
|
||||||
|
"name new keyframe channel · {} in current project",
|
||||||
|
storage.keyframe_channels.len()
|
||||||
|
))
|
||||||
|
.enter_returns_true(true)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
ui.same_line();
|
||||||
|
let add_kf_chan = ui.button("add keyframe channel");
|
||||||
|
|
||||||
|
if (kf_name_input || add_kf_chan) && !self.string_buf.is_empty() {
|
||||||
|
storage.add_keyframe_channel(String::from(&self.string_buf));
|
||||||
|
self.string_buf.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
ui.separator();
|
||||||
|
|
||||||
|
self.editor.build(ui, display, storage);
|
||||||
|
draw_mouse_pos_text(ui);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn draw_mouse_pos_text(ui: &Ui) {
|
||||||
|
let mouse_pos = ui.io().mouse_pos;
|
||||||
|
ui.text(format!(
|
||||||
|
"mouse pos: ({:.1}, {:.1})",
|
||||||
|
mouse_pos[0], mouse_pos[1]
|
||||||
|
));
|
||||||
|
}
|
@ -0,0 +1,133 @@
|
|||||||
|
#[allow(dead_code)]
|
||||||
|
pub struct Storage {
|
||||||
|
// TODO: add custom timestamp class which can be queried by
|
||||||
|
// beats/measures/etc.
|
||||||
|
pub timestamp: u64,
|
||||||
|
pub playback_mode: PlaybackMode,
|
||||||
|
pub bpm_changes: Vec<BpmChange>,
|
||||||
|
pub time_changes: Vec<TimeChange>,
|
||||||
|
pub keyframe_channels: Vec<KeyframeChannel>
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum PlaybackMode {
|
||||||
|
Playing,
|
||||||
|
Stopped,
|
||||||
|
Paused
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct BpmChange {
|
||||||
|
pub timestamp: u64,
|
||||||
|
pub bpm: u32
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct TimeChange {
|
||||||
|
pub timestamp: u64,
|
||||||
|
pub time: (u8, u8)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct KeyframeChannel {
|
||||||
|
pub name: String,
|
||||||
|
pub enabled: bool,
|
||||||
|
pub keyframes: Vec<Keyframe>
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum KeyframeType {
|
||||||
|
Tick
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Keyframe {
|
||||||
|
pub kf_type: KeyframeType,
|
||||||
|
pub timestamp: u64
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Storage {
|
||||||
|
pub fn init() -> Storage {
|
||||||
|
Storage {
|
||||||
|
timestamp: 0,
|
||||||
|
playback_mode: PlaybackMode::Stopped,
|
||||||
|
bpm_changes: Vec::<BpmChange>::new(),
|
||||||
|
time_changes: Vec::<TimeChange>::new(),
|
||||||
|
keyframe_channels: Vec::<KeyframeChannel>::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_bpm_change(&mut self, timestamp: u64, bpm: u32) {
|
||||||
|
self.bpm_changes.push(BpmChange::new(timestamp, bpm));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn delete_last_bpm_change(&mut self) {
|
||||||
|
self.bpm_changes.pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_bpm_change_index(&self, timestamp: u64) -> Option<usize> {
|
||||||
|
for (idx, bc) in self.bpm_changes.iter().enumerate() {
|
||||||
|
if (bc.timestamp) == timestamp {
|
||||||
|
return Some(idx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn initial_bpm(&self) -> u32 {
|
||||||
|
self.bpm_changes[0].bpm
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_time_change(&mut self, timestamp: u64, time: (u8, u8)) {
|
||||||
|
self.time_changes.push(TimeChange::new(timestamp, time));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_keyframe_channel(&mut self, name: String) {
|
||||||
|
self.keyframe_channels.push(KeyframeChannel::new(name));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn keyframe_channel_by_name(
|
||||||
|
&self,
|
||||||
|
name: String
|
||||||
|
) -> Option<&KeyframeChannel> {
|
||||||
|
for chan in self.keyframe_channels.iter() {
|
||||||
|
if chan.name == name {
|
||||||
|
return Some(chan);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BpmChange {
|
||||||
|
pub fn new(timestamp: u64, bpm: u32) -> BpmChange {
|
||||||
|
BpmChange { timestamp, bpm }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TimeChange {
|
||||||
|
pub fn new(timestamp: u64, time: (u8, u8)) -> TimeChange {
|
||||||
|
TimeChange { timestamp, time }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl KeyframeChannel {
|
||||||
|
pub fn new(name: String) -> KeyframeChannel {
|
||||||
|
KeyframeChannel {
|
||||||
|
name,
|
||||||
|
enabled: true,
|
||||||
|
keyframes: Vec::<Keyframe>::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_keyframe(&mut self, timestamp: u64) {
|
||||||
|
self.keyframes.push(Keyframe {
|
||||||
|
kf_type: KeyframeType::Tick,
|
||||||
|
timestamp
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: improve efficiency of this method
|
||||||
|
pub fn keyframe_at_timestamp(&self, timestamp: u64) -> Option<&Keyframe> {
|
||||||
|
for keyframe in self.keyframes.iter() {
|
||||||
|
if keyframe.timestamp == timestamp {
|
||||||
|
return Some(keyframe);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,2 @@
|
|||||||
|
pub mod drag_area;
|
||||||
|
pub mod keyframe_editor;
|
@ -0,0 +1,98 @@
|
|||||||
|
// Node editor UI element
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
|
||||||
|
use glium::glutin::window::Window as GlutinWindow;
|
||||||
|
use glium::glutin::dpi::LogicalPosition;
|
||||||
|
use imgui::{Ui, MouseButton};
|
||||||
|
use imgui::color::ImColor32;
|
||||||
|
use imgui::draw_list::DrawListMut;
|
||||||
|
|
||||||
|
pub struct DragArea {
|
||||||
|
pub offset: [f32; 2],
|
||||||
|
pub size: [f32; 2],
|
||||||
|
pub border_color: ImColor32,
|
||||||
|
pub default_offset: [f32; 2],
|
||||||
|
default_size: [f32; 2],
|
||||||
|
active: bool
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DragArea {
|
||||||
|
pub fn new(size: [f32; 2], offset: [f32; 2]) -> DragArea {
|
||||||
|
DragArea {
|
||||||
|
offset,
|
||||||
|
size,
|
||||||
|
border_color: ImColor32::from_rgb(15u8, 20u8, 25u8),
|
||||||
|
default_offset: offset,
|
||||||
|
default_size: size,
|
||||||
|
active: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn build
|
||||||
|
<F: FnMut(&DrawListMut, &[f32; 2], &[f32; 2], &[f32; 2])>
|
||||||
|
(&mut self, ui: &Ui, window: &GlutinWindow, mut build_children: F)
|
||||||
|
{
|
||||||
|
let mouse_delta = ui.io().mouse_delta;
|
||||||
|
let mouse_pos = ui.io().mouse_pos;
|
||||||
|
|
||||||
|
if ui.button("reset view") {
|
||||||
|
self.offset = self.default_offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
ui.invisible_button("##empty", self.size);
|
||||||
|
|
||||||
|
let rect_min = ui.item_rect_min();
|
||||||
|
let rect_max = ui.item_rect_max();
|
||||||
|
|
||||||
|
let lmb_down = ui.is_mouse_dragging(MouseButton::Left);
|
||||||
|
let mmb_down = ui.is_mouse_dragging(MouseButton::Middle);
|
||||||
|
let hovered = ui.is_item_hovered();
|
||||||
|
|
||||||
|
if !self.active && ((lmb_down || mmb_down) && hovered) {
|
||||||
|
self.active = true;
|
||||||
|
} else if self.active && !(lmb_down || mmb_down) {
|
||||||
|
self.active = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.active {
|
||||||
|
if rect_min[0] > mouse_pos[0] {
|
||||||
|
window.set_cursor_position(
|
||||||
|
LogicalPosition { x: rect_max[0], y: mouse_pos[1] }
|
||||||
|
).unwrap();
|
||||||
|
self.offset[0] -= self.size[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
if rect_max[0] < mouse_pos[0] {
|
||||||
|
window.set_cursor_position(
|
||||||
|
LogicalPosition { x: rect_min[0], y: mouse_pos[1] }
|
||||||
|
).unwrap();
|
||||||
|
self.offset[0] += self.size[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
if rect_min[1] > mouse_pos[1] {
|
||||||
|
window.set_cursor_position(
|
||||||
|
LogicalPosition { x: mouse_pos[0], y: rect_max[1] }
|
||||||
|
).unwrap();
|
||||||
|
self.offset[1] -= self.size[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
if rect_max[1] < mouse_pos[1] {
|
||||||
|
window.set_cursor_position(
|
||||||
|
LogicalPosition { x: mouse_pos[0], y: rect_min[1] }
|
||||||
|
).unwrap();
|
||||||
|
self.offset[1] += self.size[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
self.offset[0] += mouse_delta[0];
|
||||||
|
self.offset[1] += mouse_delta[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
let draw_list = ui.get_window_draw_list();
|
||||||
|
|
||||||
|
draw_list.with_clip_rect(rect_min, rect_max, || {
|
||||||
|
build_children(&draw_list, &rect_min, &rect_max, &self.offset);
|
||||||
|
draw_list.add_rect(rect_min, rect_max, self.border_color).build();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,153 @@
|
|||||||
|
use glium::Display;
|
||||||
|
use imgui::Ui;
|
||||||
|
use imgui::color::ImColor32;
|
||||||
|
|
||||||
|
use crate::ui_generic::drag_area::DragArea;
|
||||||
|
use crate::storage::Storage;
|
||||||
|
|
||||||
|
const KF_CHAN_HEIGHT: f32 = 20.0;
|
||||||
|
const KF_CHAN_TITLE_DEFAULT_COLOR: ImColor32 =
|
||||||
|
ImColor32::from_rgb(38, 38, 38);
|
||||||
|
const KF_CHAN_TITLE_HOVER_COLOR: ImColor32 =
|
||||||
|
ImColor32::from_rgb(79, 79, 79);
|
||||||
|
const KF_CHAN_CONTENT_DEFAULT_COLOR: ImColor32 =
|
||||||
|
ImColor32::from_rgb(16, 16, 16);
|
||||||
|
const KF_CHAN_CONTENT_HOVER_COLOR: ImColor32 =
|
||||||
|
ImColor32::from_rgb(54, 54, 54);
|
||||||
|
|
||||||
|
pub struct KeyframeEditor {
|
||||||
|
pub drag_area: DragArea
|
||||||
|
}
|
||||||
|
|
||||||
|
impl KeyframeEditor {
|
||||||
|
pub fn new(size: [f32; 2]) -> KeyframeEditor {
|
||||||
|
KeyframeEditor {
|
||||||
|
drag_area: DragArea::new(size, [0.0, 0.0])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn build(
|
||||||
|
&mut self,
|
||||||
|
ui: &Ui,
|
||||||
|
display: &Display,
|
||||||
|
storage: &mut Storage
|
||||||
|
) {
|
||||||
|
self.drag_area.build(
|
||||||
|
&ui,
|
||||||
|
&display.gl_window().window(),
|
||||||
|
|draw_list, area_min, area_max, offset| {
|
||||||
|
let mouse_y = ui.io().mouse_pos[1];
|
||||||
|
|
||||||
|
let origin: [f32; 2];
|
||||||
|
|
||||||
|
if storage.keyframe_channels.len() == 0 {
|
||||||
|
let text_pos: [f32; 2] = [
|
||||||
|
area_min[0] + (area_max[0] - area_min[0]) / 2.0 - 50.0,
|
||||||
|
area_min[1] + (area_max[1] - area_min[1]) / 2.0 - 2.5
|
||||||
|
];
|
||||||
|
|
||||||
|
draw_list.add_text(
|
||||||
|
text_pos,
|
||||||
|
KF_CHAN_TITLE_HOVER_COLOR,
|
||||||
|
"No keyframes to show."
|
||||||
|
);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let origin = [
|
||||||
|
area_min[0] + offset[0],
|
||||||
|
area_min[1] + offset[1]
|
||||||
|
];
|
||||||
|
|
||||||
|
let mut chan_y: f32 = 1.0;
|
||||||
|
let mut next_chan_y: f32 = chan_y + KF_CHAN_HEIGHT;
|
||||||
|
|
||||||
|
for (idx, chan) in storage.keyframe_channels.iter().enumerate() {
|
||||||
|
let title_bg: ImColor32;
|
||||||
|
let content_bg: ImColor32;
|
||||||
|
|
||||||
|
if (
|
||||||
|
(mouse_y < origin[1] + next_chan_y) &&
|
||||||
|
(mouse_y > origin[1] + chan_y)
|
||||||
|
) {
|
||||||
|
content_bg = KF_CHAN_CONTENT_HOVER_COLOR;
|
||||||
|
title_bg = KF_CHAN_TITLE_HOVER_COLOR;
|
||||||
|
} else {
|
||||||
|
content_bg = KF_CHAN_CONTENT_DEFAULT_COLOR;
|
||||||
|
title_bg = KF_CHAN_TITLE_DEFAULT_COLOR;
|
||||||
|
}
|
||||||
|
|
||||||
|
let chan_title_start: [f32; 2] = [
|
||||||
|
origin[0] + 1.0,
|
||||||
|
origin[1] + chan_y + 1.0
|
||||||
|
];
|
||||||
|
|
||||||
|
let chan_title_end: [f32; 2] = [
|
||||||
|
origin[0] + 100.0,
|
||||||
|
origin[1] + next_chan_y + 1.0
|
||||||
|
];
|
||||||
|
|
||||||
|
draw_list
|
||||||
|
.add_rect(
|
||||||
|
chan_title_start,
|
||||||
|
chan_title_end,
|
||||||
|
title_bg
|
||||||
|
)
|
||||||
|
.filled(true)
|
||||||
|
.thickness(0.0)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
let title_pos: [f32; 2] = [
|
||||||
|
origin[0] + 1.0 + 3.0,
|
||||||
|
origin[1] + chan_y + 2.0
|
||||||
|
];
|
||||||
|
|
||||||
|
draw_list.add_text(
|
||||||
|
title_pos,
|
||||||
|
ImColor32::WHITE,
|
||||||
|
String::from(&chan.name)
|
||||||
|
);
|
||||||
|
|
||||||
|
let chan_content_end: [f32; 2] = [
|
||||||
|
area_max[0],
|
||||||
|
chan_title_start[1]
|
||||||
|
];
|
||||||
|
|
||||||
|
draw_list
|
||||||
|
.add_rect(
|
||||||
|
chan_title_end,
|
||||||
|
chan_content_end,
|
||||||
|
content_bg
|
||||||
|
)
|
||||||
|
.filled(true)
|
||||||
|
.thickness(0.0)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
if true {
|
||||||
|
let border_start = [
|
||||||
|
origin[0],
|
||||||
|
origin[1] + next_chan_y
|
||||||
|
];
|
||||||
|
|
||||||
|
let border_end = [
|
||||||
|
area_max[0],
|
||||||
|
origin[1] + next_chan_y
|
||||||
|
];
|
||||||
|
|
||||||
|
draw_list
|
||||||
|
.add_line(
|
||||||
|
border_start,
|
||||||
|
border_end,
|
||||||
|
title_bg
|
||||||
|
)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
chan_y += KF_CHAN_HEIGHT;
|
||||||
|
next_chan_y += KF_CHAN_HEIGHT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue