mirror of
https://github.com/obsqrbtz/egui_knob.git
synced 2026-04-08 20:19:17 +03:00
added public properties
This commit is contained in:
26
.vscode/launch.json
vendored
Normal file
26
.vscode/launch.json
vendored
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
{
|
||||||
|
// Use IntelliSense to learn about possible attributes.
|
||||||
|
// Hover to view descriptions of existing attributes.
|
||||||
|
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||||
|
"version": "0.2.0",
|
||||||
|
"configurations": [
|
||||||
|
{
|
||||||
|
"type": "lldb",
|
||||||
|
"request": "launch",
|
||||||
|
"name": "Debug executable 'demo'",
|
||||||
|
"cargo": {
|
||||||
|
"args": [
|
||||||
|
"build",
|
||||||
|
"--bin=demo",
|
||||||
|
"--package=egui_knob"
|
||||||
|
],
|
||||||
|
"filter": {
|
||||||
|
"name": "demo",
|
||||||
|
"kind": "bin"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"args": [],
|
||||||
|
"cwd": "${workspaceFolder}"
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -15,15 +15,39 @@ struct KnobExample {
|
|||||||
|
|
||||||
impl Default for KnobExample {
|
impl Default for KnobExample {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self { value: 0.5 }
|
Self { value: 0.0 }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl eframe::App for KnobExample {
|
impl eframe::App for KnobExample {
|
||||||
fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
|
fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
|
||||||
egui::CentralPanel::default().show(ctx, |ui| {
|
egui::CentralPanel::default().show(ctx, |ui| {
|
||||||
ui.add(Knob::new(&mut self.value, 0.0, 1.0));
|
ui.horizontal(|ui| {
|
||||||
ui.label(format!("Value: {:.2}", self.value));
|
ui.add_space(15.0);
|
||||||
|
ui.add(
|
||||||
|
Knob::new(&mut self.value, 0.0, 100.0)
|
||||||
|
.with_label("Gain", egui_knob::LabelPosition::Bottom)
|
||||||
|
.with_size(50.0),
|
||||||
|
);
|
||||||
|
ui.add_space(15.0);
|
||||||
|
ui.add(
|
||||||
|
Knob::new(&mut self.value, 0.0, 100.0)
|
||||||
|
.with_label("Gain", egui_knob::LabelPosition::Bottom)
|
||||||
|
.with_size(50.0),
|
||||||
|
);
|
||||||
|
ui.add_space(15.0);
|
||||||
|
ui.add(
|
||||||
|
Knob::new(&mut self.value, 0.0, 100.0)
|
||||||
|
.with_label("Gain", egui_knob::LabelPosition::Bottom)
|
||||||
|
.with_size(50.0),
|
||||||
|
);
|
||||||
|
ui.add_space(15.0);
|
||||||
|
ui.add(
|
||||||
|
Knob::new(&mut self.value, 0.0, 100.0)
|
||||||
|
.with_label("Gain", egui_knob::LabelPosition::Bottom)
|
||||||
|
.with_size(50.0),
|
||||||
|
);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
113
src/lib.rs
113
src/lib.rs
@@ -1,20 +1,72 @@
|
|||||||
use egui::{Response, Sense, Ui, Vec2, Widget};
|
use egui::{Align2, Color32, Response, Sense, Stroke, Ui, Vec2, Widget};
|
||||||
|
|
||||||
|
pub enum LabelPosition {
|
||||||
|
Top,
|
||||||
|
Bottom,
|
||||||
|
Left,
|
||||||
|
Right,
|
||||||
|
}
|
||||||
|
|
||||||
pub struct Knob<'a> {
|
pub struct Knob<'a> {
|
||||||
value: &'a mut f32,
|
value: &'a mut f32,
|
||||||
min: f32,
|
min: f32,
|
||||||
max: f32,
|
max: f32,
|
||||||
|
size: f32,
|
||||||
|
font_size: f32,
|
||||||
|
stroke_width: f32,
|
||||||
|
knob_color: Color32,
|
||||||
|
line_color: Color32,
|
||||||
|
label: Option<String>,
|
||||||
|
label_position: LabelPosition,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Knob<'a> {
|
impl<'a> Knob<'a> {
|
||||||
pub fn new(value: &'a mut f32, min: f32, max: f32) -> Self {
|
pub fn new(value: &'a mut f32, min: f32, max: f32) -> Self {
|
||||||
Self { value, min, max }
|
Self {
|
||||||
|
value,
|
||||||
|
min,
|
||||||
|
max,
|
||||||
|
size: 40.0,
|
||||||
|
font_size: 12.0,
|
||||||
|
stroke_width: 2.0,
|
||||||
|
knob_color: Color32::GRAY,
|
||||||
|
line_color: Color32::GRAY,
|
||||||
|
label: None,
|
||||||
|
label_position: LabelPosition::Bottom,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_size(mut self, size: f32) -> Self {
|
||||||
|
self.size = size;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_font_size(mut self, size: f32) -> Self {
|
||||||
|
self.font_size = size;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_stroke_width(mut self, width: f32) -> Self {
|
||||||
|
self.stroke_width = width;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_colors(mut self, knob_color: Color32, line_color: Color32) -> Self {
|
||||||
|
self.knob_color = knob_color;
|
||||||
|
self.line_color = line_color;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_label(mut self, label: impl Into<String>, position: LabelPosition) -> Self {
|
||||||
|
self.label = Some(label.into());
|
||||||
|
self.label_position = position;
|
||||||
|
self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Widget for Knob<'_> {
|
impl Widget for Knob<'_> {
|
||||||
fn ui(self, ui: &mut Ui) -> Response {
|
fn ui(self, ui: &mut Ui) -> Response {
|
||||||
let desired_size = Vec2::splat(40.0);
|
let desired_size = Vec2::splat(self.size);
|
||||||
let (rect, response) = ui.allocate_exact_size(desired_size, Sense::drag());
|
let (rect, response) = ui.allocate_exact_size(desired_size, Sense::drag());
|
||||||
|
|
||||||
if response.dragged() {
|
if response.dragged() {
|
||||||
@@ -28,11 +80,60 @@ impl Widget for Knob<'_> {
|
|||||||
let center = rect.center();
|
let center = rect.center();
|
||||||
let radius = rect.width() / 2.0;
|
let radius = rect.width() / 2.0;
|
||||||
let angle = (*self.value - self.min) / (self.max - self.min) * std::f32::consts::PI * 1.5
|
let angle = (*self.value - self.min) / (self.max - self.min) * std::f32::consts::PI * 1.5
|
||||||
- std::f32::consts::PI * 0.75;
|
- std::f32::consts::PI;
|
||||||
|
|
||||||
painter.circle_stroke(center, radius, egui::Stroke::new(2.0, ui.visuals().text_color()));
|
// Draw knob circle
|
||||||
|
painter.circle_stroke(
|
||||||
|
center,
|
||||||
|
radius,
|
||||||
|
Stroke::new(self.stroke_width, self.knob_color),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Draw pointer line
|
||||||
let pointer = center + Vec2::angled(angle) * (radius * 0.7);
|
let pointer = center + Vec2::angled(angle) * (radius * 0.7);
|
||||||
painter.line_segment([center, pointer], egui::Stroke::new(3.0, ui.visuals().text_color()));
|
painter.line_segment(
|
||||||
|
[center, pointer],
|
||||||
|
Stroke::new(self.stroke_width * 1.5, self.line_color),
|
||||||
|
);
|
||||||
|
|
||||||
|
if let Some(label) = self.label {
|
||||||
|
let label_text = format!("{label}: {:.2}", self.value);
|
||||||
|
|
||||||
|
let font_id = egui::FontId::proportional(self.font_size);
|
||||||
|
let text_size = ui
|
||||||
|
.painter()
|
||||||
|
.layout(
|
||||||
|
label_text.clone(),
|
||||||
|
font_id.clone(),
|
||||||
|
Color32::WHITE,
|
||||||
|
f32::INFINITY,
|
||||||
|
)
|
||||||
|
.size();
|
||||||
|
|
||||||
|
let label_offset = 12.0;
|
||||||
|
let label_pos = match self.label_position {
|
||||||
|
LabelPosition::Top => {
|
||||||
|
center + Vec2::new(-text_size.x / 2.0, -radius - label_offset - text_size.y)
|
||||||
|
}
|
||||||
|
LabelPosition::Bottom => {
|
||||||
|
center + Vec2::new(-text_size.x / 2.0, radius + label_offset)
|
||||||
|
}
|
||||||
|
LabelPosition::Left => {
|
||||||
|
center + Vec2::new(-radius - label_offset - text_size.x, -text_size.y / 2.0)
|
||||||
|
}
|
||||||
|
LabelPosition::Right => {
|
||||||
|
center + Vec2::new(radius + label_offset, -text_size.y / 2.0)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
ui.painter().text(
|
||||||
|
label_pos,
|
||||||
|
Align2::LEFT_TOP,
|
||||||
|
label_text,
|
||||||
|
font_id,
|
||||||
|
Color32::WHITE,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
response
|
response
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user