Vendor helgoboss/helgobox (ReaLearn) as basis for custom UI fork
Stripped upstream git history; starting point for replacing the native SWELL/Win32 mapping UI with something more suited to bulk editing. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,14 @@
|
||||
[package]
|
||||
name = "helgobox-dialogs"
|
||||
version = "0.1.0"
|
||||
authors = ["Benjamin Klum <benjamin.klum@helgoboss.org>"]
|
||||
edition = "2021"
|
||||
publish = false
|
||||
|
||||
[dependencies]
|
||||
derive_more.workspace = true
|
||||
indexmap.workspace = true
|
||||
|
||||
[lints.clippy]
|
||||
# Enum glob use is not really dangerous in this module. Non-critical.
|
||||
enum_glob_use = "allow"
|
||||
@@ -0,0 +1,759 @@
|
||||
#![allow(non_camel_case_types, clippy::upper_case_acronyms)]
|
||||
|
||||
use indexmap::IndexMap;
|
||||
use std::collections::HashSet;
|
||||
use std::fmt;
|
||||
use std::fmt::Display;
|
||||
use std::fmt::Formatter;
|
||||
use std::ops::Add;
|
||||
|
||||
pub type Caption = &'static str;
|
||||
|
||||
pub struct ResourceInfo {
|
||||
global_scope: Scope,
|
||||
scopes: IndexMap<String, Scope>,
|
||||
optional_dialog_ids: HashSet<Id>,
|
||||
conditional_control_ids: HashSet<Id>,
|
||||
named_ids: Vec<Id>,
|
||||
}
|
||||
|
||||
/// Formats the info as C header file.
|
||||
///
|
||||
/// Useful if you want to preview the dialogs in Visual Studio.
|
||||
pub struct ResourceInfoAsCHeaderCode<'a>(pub &'a ResourceInfo);
|
||||
|
||||
impl Display for ResourceInfoAsCHeaderCode<'_> {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
for id in &self.0.named_ids {
|
||||
writeln!(f, "#define {} {}", id.name, id.value)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Formats the header as Rust code.
|
||||
///
|
||||
/// Uses a similar format like bindgen because previously, bindgen was used to translate
|
||||
/// the C header file to Rust.
|
||||
pub struct ResourceInfoAsRustCode<'a>(pub &'a ResourceInfo);
|
||||
|
||||
impl Display for ResourceInfoAsRustCode<'_> {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
// Write module opener
|
||||
f.write_str("pub mod root {\n")?;
|
||||
// Write scaling information
|
||||
ScopeAsRustCode::new("GLOBAL", &self.0.global_scope).fmt(f)?;
|
||||
for (key, scope) in self.0.scopes.iter() {
|
||||
ScopeAsRustCode::new(key, scope).fmt(f)?;
|
||||
}
|
||||
// Write resource IDs
|
||||
for id in &self.0.named_ids {
|
||||
if self.0.optional_dialog_ids.contains(id)
|
||||
|| self.0.conditional_control_ids.contains(id)
|
||||
{
|
||||
f.write_str(" #[allow(dead_code)]\n")?;
|
||||
}
|
||||
writeln!(f, " pub const {}: u32 = {};", id.name, id.value)?;
|
||||
}
|
||||
// Write module closer
|
||||
f.write_str("}\n")?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Resource {
|
||||
pub dialogs: Vec<Dialog>,
|
||||
}
|
||||
|
||||
impl Resource {
|
||||
pub fn generate_info(&self, context: &Context) -> ResourceInfo {
|
||||
ResourceInfo {
|
||||
global_scope: context.global_scope,
|
||||
scopes: context.scopes.clone(),
|
||||
optional_dialog_ids: self.optional_dialog_ids().collect(),
|
||||
conditional_control_ids: self.conditional_control_ids().collect(),
|
||||
named_ids: self.named_ids().collect(),
|
||||
}
|
||||
}
|
||||
|
||||
fn named_ids(&self) -> impl Iterator<Item = Id> + '_ {
|
||||
self.dialogs.iter().flat_map(|dialog| {
|
||||
fn get_if_named(id: Id) -> Option<Id> {
|
||||
if id.is_named() {
|
||||
Some(id)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
let named_dialog_id = get_if_named(dialog.id);
|
||||
let named_control_ids = dialog
|
||||
.controls
|
||||
.iter()
|
||||
.flat_map(|control| get_if_named(control.id));
|
||||
named_dialog_id.into_iter().chain(named_control_ids)
|
||||
})
|
||||
}
|
||||
|
||||
fn optional_dialog_ids(&self) -> impl Iterator<Item = Id> + '_ {
|
||||
self.dialogs.iter().filter(|d| d.optional).map(|d| d.id)
|
||||
}
|
||||
|
||||
fn conditional_control_ids(&self) -> impl Iterator<Item = Id> + '_ {
|
||||
self.dialogs.iter().flat_map(|dialog| {
|
||||
dialog
|
||||
.controls
|
||||
.iter()
|
||||
.filter(|control| !control.conditions.is_empty())
|
||||
.map(|control| control.id)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Resource {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
for (i, dialog) in self.dialogs.iter().enumerate() {
|
||||
dialog.fmt(f)?;
|
||||
if i < self.dialogs.len() - 1 {
|
||||
f.write_str("\n\n")?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
pub struct Dialog {
|
||||
pub id: Id,
|
||||
pub optional: bool,
|
||||
pub rect: Rect,
|
||||
pub kind: DialogKind,
|
||||
pub styles: Styles,
|
||||
pub ex_styles: Styles,
|
||||
pub caption: Caption,
|
||||
pub font: Option<Font>,
|
||||
pub controls: Vec<Control>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
pub struct Styles(pub Vec<Style>);
|
||||
|
||||
impl Display for Styles {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
for (i, style) in self.0.iter().enumerate() {
|
||||
style.fmt(f)?;
|
||||
if i < self.0.len() - 1 {
|
||||
f.write_str(" | ")?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Hash, Default)]
|
||||
pub struct Id {
|
||||
value: u32,
|
||||
name: &'static str,
|
||||
}
|
||||
|
||||
impl Id {
|
||||
fn is_named(&self) -> bool {
|
||||
!self.name.is_empty()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct DialogScaling {
|
||||
pub x_scale: f64,
|
||||
pub y_scale: f64,
|
||||
pub width_scale: f64,
|
||||
pub height_scale: f64,
|
||||
}
|
||||
|
||||
struct DialogScalingAsRustCode<'a> {
|
||||
attr: &'a str,
|
||||
scope: &'a str,
|
||||
scaling: &'a DialogScaling,
|
||||
}
|
||||
|
||||
impl<'a> DialogScalingAsRustCode<'a> {
|
||||
pub fn new(attr: &'a str, scope: &'a str, scaling: &'a DialogScaling) -> Self {
|
||||
Self {
|
||||
attr,
|
||||
scope,
|
||||
scaling,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for DialogScalingAsRustCode<'_> {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
writeln!(
|
||||
f,
|
||||
" {}\n pub const {}_X_SCALE: f64 = {:.4};",
|
||||
self.attr, self.scope, self.scaling.x_scale
|
||||
)?;
|
||||
writeln!(
|
||||
f,
|
||||
" {}\n pub const {}_Y_SCALE: f64 = {:.4};",
|
||||
self.attr, self.scope, self.scaling.y_scale
|
||||
)?;
|
||||
writeln!(
|
||||
f,
|
||||
" {}\n pub const {}_WIDTH_SCALE: f64 = {:.4};",
|
||||
self.attr, self.scope, self.scaling.width_scale
|
||||
)?;
|
||||
writeln!(
|
||||
f,
|
||||
" {}\n pub const {}_HEIGHT_SCALE: f64 = {:.4};",
|
||||
self.attr, self.scope, self.scaling.height_scale
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct ScopedContext<'a> {
|
||||
pub(crate) context: &'a Context,
|
||||
scope: Option<Scope>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct Scope {
|
||||
pub linux: OsSpecificSettings,
|
||||
pub windows: OsSpecificSettings,
|
||||
pub macos: OsSpecificSettings,
|
||||
}
|
||||
|
||||
struct ScopeAsRustCode<'a> {
|
||||
scope_name: &'a str,
|
||||
scope: &'a Scope,
|
||||
}
|
||||
|
||||
impl<'a> ScopeAsRustCode<'a> {
|
||||
pub fn new(scope_name: &'a str, scope: &'a Scope) -> Self {
|
||||
Self { scope_name, scope }
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for ScopeAsRustCode<'_> {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
let mut write_os = |os: &str, scaling: &DialogScaling| -> fmt::Result {
|
||||
let attr = format!("#[cfg(target_os = {})]", Quoted(os));
|
||||
DialogScalingAsRustCode::new(&attr, self.scope_name, scaling).fmt(f)?;
|
||||
Ok(())
|
||||
};
|
||||
write_os("linux", &self.scope.linux.scaling)?;
|
||||
write_os("windows", &self.scope.windows.scaling)?;
|
||||
write_os("macos", &self.scope.macos.scaling)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Scope {
|
||||
pub const fn settings_for_this_os(&self) -> &OsSpecificSettings {
|
||||
#[cfg(target_os = "linux")]
|
||||
{
|
||||
&self.linux
|
||||
}
|
||||
#[cfg(target_os = "windows")]
|
||||
{
|
||||
&self.windows
|
||||
}
|
||||
#[cfg(target_os = "macos")]
|
||||
{
|
||||
&self.macos
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct OsSpecificSettings {
|
||||
pub scaling: DialogScaling,
|
||||
}
|
||||
|
||||
pub fn rect(x: u32, y: u32, width: u32, height: u32) -> Rect {
|
||||
Rect::new(x, y, width, height)
|
||||
}
|
||||
|
||||
impl ScopedContext<'_> {
|
||||
pub fn default_dialog(&self) -> Dialog {
|
||||
self.context.default_dialog()
|
||||
}
|
||||
|
||||
pub fn scale_width(&self, width: u32) -> u32 {
|
||||
scale(self.scaling().width_scale, width)
|
||||
}
|
||||
|
||||
pub fn scale_height(&self, height: u32) -> u32 {
|
||||
scale(self.scaling().height_scale, height)
|
||||
}
|
||||
|
||||
pub fn rect(&self, x: u32, y: u32, width: u32, height: u32) -> Rect {
|
||||
self.rect_flexible(Rect::new(x, y, width, height))
|
||||
}
|
||||
|
||||
pub fn rect_flexible(&self, rect: Rect) -> Rect {
|
||||
let scaling = self.scaling();
|
||||
Rect {
|
||||
x: scale(scaling.x_scale, rect.x),
|
||||
y: scale(scaling.y_scale, rect.y),
|
||||
width: scale(scaling.width_scale, rect.width),
|
||||
height: scale(scaling.height_scale, rect.height),
|
||||
}
|
||||
}
|
||||
|
||||
fn scaling(&self) -> DialogScaling {
|
||||
self.scope
|
||||
.as_ref()
|
||||
.map(|s| s.settings_for_this_os().scaling)
|
||||
.unwrap_or(self.context.global_scope.settings_for_this_os().scaling)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct IdGenerator {
|
||||
next_id_value: u32,
|
||||
}
|
||||
impl IdGenerator {
|
||||
pub fn new(initial_id_value: u32) -> Self {
|
||||
Self {
|
||||
next_id_value: initial_id_value,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn id(&mut self) -> Id {
|
||||
Id {
|
||||
value: self.next_id_value(),
|
||||
name: "",
|
||||
}
|
||||
}
|
||||
|
||||
pub fn named_id(&mut self, name: &'static str) -> Id {
|
||||
Id {
|
||||
value: self.next_id_value(),
|
||||
name,
|
||||
}
|
||||
}
|
||||
|
||||
fn next_id_value(&mut self) -> u32 {
|
||||
let v = self.next_id_value;
|
||||
self.next_id_value += 1;
|
||||
v
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Context {
|
||||
pub default_dialog: Dialog,
|
||||
pub global_scope: Scope,
|
||||
// IndexMap instead of HashMap because we don't want the order to be always the same when
|
||||
// writing the bindings file.
|
||||
pub scopes: IndexMap<String, Scope>,
|
||||
}
|
||||
|
||||
impl Context {
|
||||
pub fn global(&self) -> ScopedContext {
|
||||
ScopedContext {
|
||||
context: self,
|
||||
scope: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn scoped<'a>(&'a self, scope: &'a str) -> ScopedContext<'a> {
|
||||
let scope = *self.scopes.get(scope).expect("scope not found");
|
||||
ScopedContext {
|
||||
context: self,
|
||||
scope: Some(scope),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn default_dialog(&self) -> Dialog {
|
||||
self.default_dialog.clone()
|
||||
}
|
||||
}
|
||||
|
||||
fn scale(scale: f64, value: u32) -> u32 {
|
||||
(scale * value as f64).round() as _
|
||||
}
|
||||
|
||||
impl Display for Id {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
if self.name.is_empty() {
|
||||
self.value.fmt(f)
|
||||
} else {
|
||||
self.name.fmt(f)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, derive_more::Display)]
|
||||
pub enum DialogKind {
|
||||
DIALOG,
|
||||
DIALOGEX,
|
||||
}
|
||||
|
||||
impl Default for DialogKind {
|
||||
fn default() -> Self {
|
||||
Self::DIALOG
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
pub struct Control {
|
||||
pub id: Id,
|
||||
/// Unlike in dialog, it's important to distinguish between Some and None because some
|
||||
/// controls need an empty string.
|
||||
pub caption: Option<Caption>,
|
||||
pub kind: ControlKind,
|
||||
pub sub_kind: Option<SubControlKind>,
|
||||
pub rect: Rect,
|
||||
pub styles: Styles,
|
||||
pub conditions: HashSet<Condition>,
|
||||
}
|
||||
|
||||
impl Add<Style> for Control {
|
||||
type Output = Control;
|
||||
|
||||
fn add(mut self, rhs: Style) -> Self::Output {
|
||||
self.styles.0.push(rhs);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl Add<Condition> for Control {
|
||||
type Output = Control;
|
||||
|
||||
fn add(mut self, rhs: Condition) -> Self::Output {
|
||||
self.conditions.insert(rhs);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
struct Quoted<D>(D);
|
||||
|
||||
impl<D: Display> Display for Quoted<D> {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
write!(f, "\"{}\"", self.0)
|
||||
}
|
||||
}
|
||||
|
||||
struct LineBreaksEscaped<D>(D);
|
||||
|
||||
impl<D: Display> Display for LineBreaksEscaped<D> {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
self.0
|
||||
.to_string()
|
||||
.replace("\r\n", "\\r\\n")
|
||||
.replace('\n', "\\r\\n")
|
||||
.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
fn opt<T: Display>(v: &Option<T>) -> Option<String> {
|
||||
let v = v.as_ref()?;
|
||||
Some(v.to_string())
|
||||
}
|
||||
|
||||
fn req<T: Display>(v: T) -> Option<String> {
|
||||
Some(v.to_string())
|
||||
}
|
||||
|
||||
impl Display for Dialog {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
writeln!(f, "{} {} {}", self.id, self.kind, self.rect)?;
|
||||
if !self.styles.0.is_empty() {
|
||||
writeln!(f, "STYLE {}", self.styles)?;
|
||||
}
|
||||
if !self.ex_styles.0.is_empty() {
|
||||
writeln!(f, "EXSTYLE {}", self.ex_styles)?;
|
||||
}
|
||||
if !self.caption.is_empty() {
|
||||
writeln!(f, "CAPTION {}", Quoted(self.caption))?;
|
||||
}
|
||||
if let Some(font) = self.font.as_ref() {
|
||||
writeln!(f, "FONT {font}")?;
|
||||
}
|
||||
f.write_str("BEGIN\n")?;
|
||||
if !self.controls.is_empty() {
|
||||
for control in &self.controls {
|
||||
#[cfg(target_os = "macos")]
|
||||
if control.conditions.contains(&Condition::SkipOnMacOs) {
|
||||
continue;
|
||||
}
|
||||
writeln!(f, " {control}")?;
|
||||
}
|
||||
}
|
||||
f.write_str("END")?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Control {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
let caption = opt(&self.caption.map(LineBreaksEscaped).map(Quoted));
|
||||
let id = req(self.id);
|
||||
let rect = req(self.rect);
|
||||
let styles = if self.styles.0.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(self.styles.to_string())
|
||||
};
|
||||
let args = if self.kind == ControlKind::CONTROL {
|
||||
vec![
|
||||
caption,
|
||||
id,
|
||||
req(Quoted(self.sub_kind.unwrap())),
|
||||
styles,
|
||||
rect,
|
||||
]
|
||||
} else {
|
||||
vec![caption, id, rect, styles]
|
||||
};
|
||||
let args: Vec<_> = args.into_iter().flatten().collect();
|
||||
write!(f, "{} {}", self.kind, args.join(","))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Eq, PartialEq, derive_more::Display)]
|
||||
pub enum ControlKind {
|
||||
LTEXT,
|
||||
RTEXT,
|
||||
COMBOBOX,
|
||||
PUSHBUTTON,
|
||||
CONTROL,
|
||||
EDITTEXT,
|
||||
GROUPBOX,
|
||||
DEFPUSHBUTTON,
|
||||
CTEXT,
|
||||
}
|
||||
|
||||
impl Default for ControlKind {
|
||||
fn default() -> Self {
|
||||
Self::CTEXT
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, derive_more::Display)]
|
||||
pub enum SubControlKind {
|
||||
Button,
|
||||
Static,
|
||||
msctls_trackbar32,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct Font {
|
||||
pub name: &'static str,
|
||||
pub size: u32,
|
||||
}
|
||||
|
||||
impl Display for Font {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{}, {}", self.size, Quoted(self.name))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Default)]
|
||||
pub struct Rect {
|
||||
pub x: u32,
|
||||
pub y: u32,
|
||||
pub width: u32,
|
||||
pub height: u32,
|
||||
}
|
||||
|
||||
impl Display for Rect {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
write!(f, "{}, {}, {}, {}", self.x, self.y, self.width, self.height)
|
||||
}
|
||||
}
|
||||
|
||||
impl Rect {
|
||||
pub fn new(x: u32, y: u32, width: u32, height: u32) -> Self {
|
||||
Self {
|
||||
x,
|
||||
y,
|
||||
width,
|
||||
height,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn pushbutton(caption: Caption, id: Id, rect: Rect) -> Control {
|
||||
Control {
|
||||
id,
|
||||
caption: Some(caption),
|
||||
kind: ControlKind::PUSHBUTTON,
|
||||
rect,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn groupbox(caption: Caption, id: Id, rect: Rect) -> Control {
|
||||
Control {
|
||||
id,
|
||||
caption: Some(caption),
|
||||
kind: ControlKind::GROUPBOX,
|
||||
rect,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn defpushbutton(caption: Caption, id: Id, rect: Rect) -> Control {
|
||||
Control {
|
||||
id,
|
||||
caption: Some(caption),
|
||||
kind: ControlKind::DEFPUSHBUTTON,
|
||||
rect,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ltext(caption: Caption, id: Id, rect: Rect) -> Control {
|
||||
Control {
|
||||
id,
|
||||
caption: Some(caption),
|
||||
kind: ControlKind::LTEXT,
|
||||
rect: fix_text_rect(rect),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn rtext(caption: Caption, id: Id, rect: Rect) -> Control {
|
||||
Control {
|
||||
id,
|
||||
caption: Some(caption),
|
||||
kind: ControlKind::RTEXT,
|
||||
rect: fix_text_rect(rect),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ctext(caption: Caption, id: Id, rect: Rect) -> Control {
|
||||
Control {
|
||||
id,
|
||||
caption: Some(caption),
|
||||
kind: ControlKind::CTEXT,
|
||||
rect: fix_text_rect(rect),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn combobox(id: Id, rect: Rect) -> Control {
|
||||
Control {
|
||||
id,
|
||||
kind: ControlKind::COMBOBOX,
|
||||
rect,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn edittext(id: Id, rect: Rect) -> Control {
|
||||
Control {
|
||||
id,
|
||||
kind: ControlKind::EDITTEXT,
|
||||
rect,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn control(caption: Caption, id: Id, sub_kind: SubControlKind, rect: Rect) -> Control {
|
||||
Control {
|
||||
id,
|
||||
caption: Some(caption),
|
||||
kind: ControlKind::CONTROL,
|
||||
sub_kind: Some(sub_kind),
|
||||
rect,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Hash)]
|
||||
pub enum Condition {
|
||||
/// Doesn't output the control in the RC file generated on macOS.
|
||||
///
|
||||
/// Still assigns an ID because it's better to keep the bindings file the same on every
|
||||
/// platform.
|
||||
SkipOnMacOs,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, derive_more::Display)]
|
||||
pub enum Style {
|
||||
DS_SETFONT,
|
||||
DS_MODALFRAME,
|
||||
DS_3DLOOK,
|
||||
DS_FIXEDSYS,
|
||||
DS_CENTER,
|
||||
WS_POPUP,
|
||||
WS_VISIBLE,
|
||||
WS_CAPTION,
|
||||
WS_SYSMENU,
|
||||
WS_THICKFRAME,
|
||||
WS_MAXIMIZEBOX,
|
||||
DS_CONTROL,
|
||||
WS_CHILD,
|
||||
CBS_DROPDOWNLIST,
|
||||
CBS_HASSTRINGS,
|
||||
ES_MULTILINE,
|
||||
ES_READONLY,
|
||||
ES_WANTRETURN,
|
||||
WS_VSCROLL,
|
||||
WS_TABSTOP,
|
||||
WS_GROUP,
|
||||
WS_DISABLED,
|
||||
BS_AUTOCHECKBOX,
|
||||
BS_AUTORADIOBUTTON,
|
||||
TBS_BOTH,
|
||||
TBS_NOTICKS,
|
||||
SS_ETCHEDHORZ,
|
||||
SS_LEFTNOWORDWRAP,
|
||||
ES_AUTOHSCROLL,
|
||||
SS_CENTERIMAGE,
|
||||
SS_WORDELLIPSIS,
|
||||
// With negation
|
||||
#[display(fmt = "NOT WS_TABSTOP")]
|
||||
NOT_WS_TABSTOP,
|
||||
#[display(fmt = "NOT WS_GROUP")]
|
||||
NOT_WS_GROUP,
|
||||
// Ex styles
|
||||
WS_EX_TOPMOST,
|
||||
WS_EX_WINDOWEDGE,
|
||||
}
|
||||
|
||||
/// Makes sure the effective (already scaled) height of a text is not too low.
|
||||
pub fn fix_text_rect(rect: Rect) -> Rect {
|
||||
Rect {
|
||||
height: rect.height.max(MIN_EFFECTIVE_TEXT_HEIGHT),
|
||||
..rect
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Adder(pub u32);
|
||||
|
||||
impl Adder {
|
||||
pub fn space(&mut self, units: u32) -> u32 {
|
||||
self.0 += units;
|
||||
self.0
|
||||
}
|
||||
|
||||
pub fn span(&mut self, units: u32) -> u32 {
|
||||
self.0 += units;
|
||||
units
|
||||
}
|
||||
|
||||
pub fn get(&self) -> u32 {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Adder> for u32 {
|
||||
fn from(v: Adder) -> Self {
|
||||
v.0
|
||||
}
|
||||
}
|
||||
|
||||
// If lower than this, text will be cut off, especially the part below the baseline.
|
||||
#[cfg(target_os = "windows")]
|
||||
const MIN_EFFECTIVE_TEXT_HEIGHT: u32 = 8;
|
||||
// If lower than this, radio buttons will be cut off.
|
||||
#[cfg(target_os = "macos")]
|
||||
const MIN_EFFECTIVE_TEXT_HEIGHT: u32 = 10;
|
||||
#[cfg(target_os = "linux")]
|
||||
const MIN_EFFECTIVE_TEXT_HEIGHT: u32 = 13;
|
||||
@@ -0,0 +1,11 @@
|
||||
use crate::base::*;
|
||||
|
||||
pub fn create(context: ScopedContext, ids: &mut IdGenerator) -> Dialog {
|
||||
use Style::*;
|
||||
Dialog {
|
||||
id: ids.named_id("ID_COLOR_PANEL"),
|
||||
rect: context.rect(0, 0, 250, 250),
|
||||
styles: Styles(vec![DS_SETFONT, DS_CONTROL, WS_CHILD, WS_VISIBLE]),
|
||||
..context.default_dialog()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
// Attention: We can't calculate a constant main panel height at this point because different
|
||||
// scaling factors will be applied to the header panel, depending on the operating system.
|
||||
pub const MAIN_PANEL_WIDTH: u32 = 470;
|
||||
pub const HEADER_PANEL_HEIGHT: u32 = 124;
|
||||
pub const HEADER_PANEL_WIDTH: u32 = MAIN_PANEL_WIDTH;
|
||||
// Need to leave some space for the scrollbar.
|
||||
pub const MAPPING_ROW_PANEL_WIDTH: u32 = MAIN_PANEL_WIDTH - 10;
|
||||
pub const MAPPING_ROW_PANEL_HEIGHT: u32 = 48;
|
||||
pub const FOOTER_PANEL_HEIGHT: u32 = 43;
|
||||
pub const MAPPING_ROW_COUNT: u32 = 5;
|
||||
pub const MAPPING_ROWS_PANEL_WIDTH: u32 = MAIN_PANEL_WIDTH;
|
||||
pub const MAPPING_ROWS_PANEL_HEIGHT: u32 = MAPPING_ROW_PANEL_HEIGHT * MAPPING_ROW_COUNT;
|
||||
@@ -0,0 +1,22 @@
|
||||
use crate::base::*;
|
||||
|
||||
pub fn create(context: ScopedContext, ids: &mut IdGenerator) -> Dialog {
|
||||
use Style::*;
|
||||
Dialog {
|
||||
id: ids.named_id("ID_EMPTY_PANEL"),
|
||||
optional: true,
|
||||
caption: "Editor",
|
||||
rect: context.rect(0, 0, 600, 250),
|
||||
styles: Styles(vec![
|
||||
// Places the window into the center by default
|
||||
DS_CENTER,
|
||||
// Displays a close button
|
||||
WS_SYSMENU,
|
||||
// Displays a maximize button
|
||||
WS_MAXIMIZEBOX,
|
||||
// Allows user to change size of window
|
||||
WS_THICKFRAME,
|
||||
]),
|
||||
..context.default_dialog()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
use crate::base::*;
|
||||
|
||||
impl ScopedContext<'_> {
|
||||
pub fn checkbox(&self, caption: Caption, id: Id, rect: Rect) -> Control {
|
||||
use Style::*;
|
||||
// We want to completely ignore the given checkbox height, but we want it to scale.
|
||||
let fixed_rect = self.rect_flexible(Rect { height: 8, ..rect });
|
||||
control(
|
||||
caption,
|
||||
id,
|
||||
SubControlKind::Button,
|
||||
fix_text_rect(fixed_rect),
|
||||
) + BS_AUTOCHECKBOX
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ok_button(id: Id, rect: Rect) -> Control {
|
||||
defpushbutton("OK", id, rect)
|
||||
}
|
||||
|
||||
pub fn dropdown(id: Id, rect: Rect) -> Control {
|
||||
use Style::*;
|
||||
combobox(id, rect) + CBS_DROPDOWNLIST + CBS_HASSTRINGS
|
||||
}
|
||||
|
||||
pub fn slider(id: Id, rect: Rect) -> Control {
|
||||
use Style::*;
|
||||
control("", id, SubControlKind::msctls_trackbar32, rect) + TBS_BOTH + TBS_NOTICKS
|
||||
}
|
||||
|
||||
pub fn radio_button(caption: Caption, id: Id, rect: Rect) -> Control {
|
||||
use Style::*;
|
||||
control(caption, id, SubControlKind::Button, fix_text_rect(rect)) + BS_AUTORADIOBUTTON
|
||||
}
|
||||
|
||||
pub fn divider(id: Id, rect: Rect) -> Control {
|
||||
use Style::*;
|
||||
control("", id, SubControlKind::Static, rect) + SS_ETCHEDHORZ
|
||||
}
|
||||
|
||||
pub fn static_text(caption: Caption, id: Id, rect: Rect) -> Control {
|
||||
control(caption, id, SubControlKind::Static, fix_text_rect(rect))
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
use crate::base::*;
|
||||
use crate::ext::*;
|
||||
|
||||
pub fn create(context: ScopedContext, ids: &mut IdGenerator) -> Dialog {
|
||||
use Style::*;
|
||||
Dialog {
|
||||
id: ids.named_id("ID_GROUP_PANEL"),
|
||||
caption: "Edit group",
|
||||
rect: context.rect(0, 0, 444, 74),
|
||||
styles: Styles(vec![
|
||||
DS_SETFONT,
|
||||
DS_MODALFRAME,
|
||||
DS_3DLOOK,
|
||||
DS_FIXEDSYS,
|
||||
DS_CENTER,
|
||||
WS_POPUP,
|
||||
WS_VISIBLE,
|
||||
WS_CAPTION,
|
||||
WS_SYSMENU,
|
||||
]),
|
||||
controls: vec![ok_button(
|
||||
ids.named_id("ID_GROUP_PANEL_OK"),
|
||||
context.rect(197, 53, 50, 14),
|
||||
)],
|
||||
..context.default_dialog()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,238 @@
|
||||
use crate::base::*;
|
||||
use crate::constants::{HEADER_PANEL_HEIGHT, HEADER_PANEL_WIDTH};
|
||||
use crate::ext::*;
|
||||
|
||||
pub fn create(context: ScopedContext, ids: &mut IdGenerator) -> Dialog {
|
||||
use Style::*;
|
||||
let text_height = 9;
|
||||
let left_label_x = 7;
|
||||
let io_label_width = 24;
|
||||
let space = 3;
|
||||
let (io_combo_box_width, io_combo_box_height) = (190, 14);
|
||||
let mut row1 = Adder(left_label_x);
|
||||
let mut row2 = Adder(left_label_x);
|
||||
let upper_part_controls = [
|
||||
// Input/output
|
||||
ltext(
|
||||
"Input",
|
||||
ids.id(),
|
||||
context.rect(row1.get(), 5, row1.span(io_label_width), text_height),
|
||||
),
|
||||
pushbutton(
|
||||
"MIDI: <FX input>",
|
||||
ids.named_id("ID_CONTROL_INPUT_BUTTON"),
|
||||
context.rect(
|
||||
row1.space(space),
|
||||
3,
|
||||
row1.span(io_combo_box_width),
|
||||
io_combo_box_height,
|
||||
),
|
||||
) + NOT_WS_TABSTOP,
|
||||
ltext(
|
||||
"Output",
|
||||
ids.id(),
|
||||
context.rect(row2.get(), 25, row2.span(io_label_width), text_height),
|
||||
),
|
||||
pushbutton(
|
||||
"<None>",
|
||||
ids.named_id("ID_FEEDBACK_OUTPUT_BUTTON"),
|
||||
context.rect(
|
||||
row2.space(space),
|
||||
23,
|
||||
row2.span(io_combo_box_width),
|
||||
io_combo_box_height,
|
||||
),
|
||||
) + NOT_WS_TABSTOP,
|
||||
// Quick actions
|
||||
pushbutton(
|
||||
"Menu",
|
||||
ids.named_id("ID_MENU_BUTTON"),
|
||||
context.rect(row1.space(space), 3, row1.span(27), 14),
|
||||
) + WS_GROUP,
|
||||
pushbutton(
|
||||
"Import from clipboard",
|
||||
ids.named_id("ID_IMPORT_BUTTON"),
|
||||
context.rect(row1.space(space), 3, row1.span(73), 14),
|
||||
) + WS_GROUP,
|
||||
pushbutton(
|
||||
"Export to clipboard",
|
||||
ids.named_id("ID_EXPORT_BUTTON"),
|
||||
context.rect(row1.space(space), 3, row1.span(67), 14),
|
||||
) + NOT_WS_TABSTOP,
|
||||
pushbutton(
|
||||
"Projection",
|
||||
ids.named_id("ID_PROJECTION_BUTTON"),
|
||||
context.rect(row1.space(space), 3, row1.span(42), 14),
|
||||
) + NOT_WS_TABSTOP,
|
||||
pushbutton(
|
||||
"?",
|
||||
ids.named_id("ID_MAIN_HELP_BUTTON"),
|
||||
context.rect(row1.space(space), 3, row1.span(14), 14),
|
||||
) + NOT_WS_TABSTOP,
|
||||
// Event filter
|
||||
ltext(
|
||||
"Let MIDI through:",
|
||||
ids.named_id("ID_LET_THROUGH_LABEL_TEXT"),
|
||||
context.rect(257, 25, 55, 9),
|
||||
),
|
||||
context.checkbox(
|
||||
"Matched events",
|
||||
ids.named_id("ID_LET_MATCHED_EVENTS_THROUGH_CHECK_BOX"),
|
||||
rect(319, 25, 67, 8),
|
||||
) + WS_TABSTOP,
|
||||
context.checkbox(
|
||||
"Unmatched events",
|
||||
ids.named_id("ID_LET_UNMATCHED_EVENTS_THROUGH_CHECK_BOX"),
|
||||
rect(392, 25, 76, 8),
|
||||
) + WS_TABSTOP,
|
||||
];
|
||||
let show_controls = [
|
||||
ltext(
|
||||
"Show",
|
||||
ids.named_id("ID_HEADER_PANEL_SHOW_LABEL_TEXT"),
|
||||
context.rect(7, 47, 24, 9),
|
||||
),
|
||||
radio_button(
|
||||
"Controller compartment (for describing your device, optional)",
|
||||
ids.named_id("ID_CONTROLLER_COMPARTMENT_RADIO_BUTTON"),
|
||||
context.rect(50, 47, 200, 8),
|
||||
) + WS_TABSTOP,
|
||||
radio_button(
|
||||
"Main compartment (for defining what it should do)",
|
||||
ids.named_id("ID_MAIN_COMPARTMENT_RADIO_BUTTON"),
|
||||
context.rect(269, 47, 200, 8),
|
||||
) + WS_TABSTOP,
|
||||
];
|
||||
let lower_part_controls = [
|
||||
// Preset
|
||||
ltext(
|
||||
"Controller preset",
|
||||
ids.named_id("ID_PRESET_LABEL_TEXT"),
|
||||
context.rect(7, 69, 57, 9),
|
||||
),
|
||||
pushbutton(
|
||||
"<None>",
|
||||
ids.named_id("ID_PRESET_BROWSE_BUTTON"),
|
||||
context.rect(68, 66, 135, 14),
|
||||
) + WS_GROUP,
|
||||
// Preset actions
|
||||
pushbutton(
|
||||
"Save",
|
||||
ids.named_id("ID_PRESET_SAVE_BUTTON"),
|
||||
context.rect(207, 66, 26, 14),
|
||||
) + NOT_WS_TABSTOP,
|
||||
pushbutton(
|
||||
"Save as...",
|
||||
ids.named_id("ID_PRESET_SAVE_AS_BUTTON"),
|
||||
context.rect(235, 66, 42, 14),
|
||||
) + WS_GROUP,
|
||||
pushbutton(
|
||||
"Delete",
|
||||
ids.named_id("ID_PRESET_DELETE_BUTTON"),
|
||||
context.rect(279, 66, 28, 14),
|
||||
) + NOT_WS_TABSTOP,
|
||||
// Auto-load
|
||||
ltext(
|
||||
"Auto-load",
|
||||
ids.named_id("ID_AUTO_LOAD_LABEL_TEXT"),
|
||||
context.rect(319, 69, 33, 9),
|
||||
) + NOT_WS_GROUP,
|
||||
dropdown(
|
||||
ids.named_id("ID_AUTO_LOAD_COMBO_BOX"),
|
||||
context.rect(356, 67, 107, 16),
|
||||
) + WS_VSCROLL
|
||||
+ WS_GROUP
|
||||
+ WS_TABSTOP,
|
||||
// Mapping group
|
||||
ltext("Mapping group", ids.id(), context.rect(7, 89, 55, 9)) + NOT_WS_GROUP,
|
||||
dropdown(
|
||||
ids.named_id("ID_GROUP_COMBO_BOX"),
|
||||
context.rect(68, 87, 135, 16),
|
||||
) + WS_VSCROLL
|
||||
+ WS_TABSTOP,
|
||||
// Mapping group actions
|
||||
pushbutton(
|
||||
"Add",
|
||||
ids.named_id("ID_GROUP_ADD_BUTTON"),
|
||||
context.rect(207, 86, 26, 14),
|
||||
) + WS_GROUP,
|
||||
pushbutton(
|
||||
"Remove",
|
||||
ids.named_id("ID_GROUP_DELETE_BUTTON"),
|
||||
context.rect(235, 86, 42, 14),
|
||||
) + NOT_WS_TABSTOP,
|
||||
pushbutton(
|
||||
"Edit",
|
||||
ids.named_id("ID_GROUP_EDIT_BUTTON"),
|
||||
context.rect(279, 86, 28, 14),
|
||||
) + NOT_WS_TABSTOP,
|
||||
pushbutton(
|
||||
"Notes",
|
||||
ids.named_id("ID_NOTES_BUTTON"),
|
||||
context.rect(426, 86, 35, 14),
|
||||
) + NOT_WS_TABSTOP,
|
||||
// Mapping list actions
|
||||
ltext("Mappings", ids.id(), context.rect(7, 109, 33, 9)) + NOT_WS_GROUP,
|
||||
pushbutton(
|
||||
"Add one",
|
||||
ids.named_id("ID_ADD_MAPPING_BUTTON"),
|
||||
context.rect(42, 106, 41, 14),
|
||||
) + WS_GROUP,
|
||||
pushbutton(
|
||||
"Learn many",
|
||||
ids.named_id("ID_LEARN_MANY_MAPPINGS_BUTTON"),
|
||||
context.rect(86, 106, 47, 14),
|
||||
) + NOT_WS_TABSTOP,
|
||||
// Search
|
||||
ltext("Search", ids.id(), context.rect(139, 109, 25, 9)) + NOT_WS_GROUP,
|
||||
edittext(
|
||||
ids.named_id("ID_HEADER_SEARCH_EDIT_CONTROL"),
|
||||
context.rect(165, 106, 157, 14),
|
||||
) + ES_AUTOHSCROLL,
|
||||
pushbutton(
|
||||
"X",
|
||||
ids.named_id("ID_CLEAR_SEARCH_BUTTON"),
|
||||
context.rect(323, 106, 11, 14),
|
||||
) + NOT_WS_TABSTOP,
|
||||
// Source filter
|
||||
pushbutton(
|
||||
"Filter source",
|
||||
ids.named_id("ID_FILTER_BY_SOURCE_BUTTON"),
|
||||
context.rect(340, 106, 48, 14),
|
||||
) + WS_GROUP,
|
||||
pushbutton(
|
||||
"X",
|
||||
ids.named_id("ID_CLEAR_SOURCE_FILTER_BUTTON"),
|
||||
context.rect(389, 106, 11, 14),
|
||||
) + NOT_WS_TABSTOP,
|
||||
// Target filter
|
||||
pushbutton(
|
||||
"Filter target",
|
||||
ids.named_id("ID_FILTER_BY_TARGET_BUTTON"),
|
||||
context.rect(404, 106, 45, 14),
|
||||
) + WS_GROUP,
|
||||
pushbutton(
|
||||
"X",
|
||||
ids.named_id("ID_CLEAR_TARGET_FILTER_BUTTON"),
|
||||
context.rect(450, 106, 11, 14),
|
||||
) + NOT_WS_TABSTOP,
|
||||
];
|
||||
let divider_controls = [
|
||||
divider(ids.id(), context.rect(0, 41, HEADER_PANEL_WIDTH, 1)),
|
||||
divider(ids.id(), context.rect(0, 62, HEADER_PANEL_WIDTH, 1)),
|
||||
// divider(ids.id(), context.rect(0, 123, HEADER_PANEL_WIDTH, 1)),
|
||||
];
|
||||
Dialog {
|
||||
id: ids.named_id("ID_HEADER_PANEL"),
|
||||
kind: DialogKind::DIALOGEX,
|
||||
rect: context.rect(0, 0, HEADER_PANEL_WIDTH, HEADER_PANEL_HEIGHT),
|
||||
styles: Styles(vec![DS_SETFONT, DS_CONTROL, WS_CHILD, WS_VISIBLE]),
|
||||
controls: upper_part_controls
|
||||
.into_iter()
|
||||
.chain(show_controls)
|
||||
.chain(lower_part_controls)
|
||||
.chain(divider_controls)
|
||||
.collect(),
|
||||
..context.default_dialog()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
use crate::base::*;
|
||||
|
||||
pub fn create(context: ScopedContext, ids: &mut IdGenerator) -> Dialog {
|
||||
Dialog {
|
||||
id: ids.named_id("ID_HIDDEN_PANEL"),
|
||||
caption: "Hidden panel",
|
||||
..context.default_dialog()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
use crate::base::*;
|
||||
use crate::constants::{FOOTER_PANEL_HEIGHT, MAIN_PANEL_WIDTH};
|
||||
|
||||
pub fn create(
|
||||
context: ScopedContext,
|
||||
ids: &mut IdGenerator,
|
||||
effective_header_panel_height: u32,
|
||||
effective_rows_panel_height: u32,
|
||||
) -> Dialog {
|
||||
use Style::*;
|
||||
let controls = vec![];
|
||||
Dialog {
|
||||
id: ids.named_id("ID_INSTANCE_PANEL"),
|
||||
kind: DialogKind::DIALOGEX,
|
||||
rect: Rect::new(
|
||||
0,
|
||||
0,
|
||||
context.scale_width(MAIN_PANEL_WIDTH),
|
||||
effective_header_panel_height
|
||||
+ effective_rows_panel_height
|
||||
+ context.scale_height(FOOTER_PANEL_HEIGHT),
|
||||
),
|
||||
styles: Styles(vec![DS_SETFONT, DS_CONTROL, WS_CHILD, WS_VISIBLE]),
|
||||
controls,
|
||||
..context.default_dialog()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,198 @@
|
||||
use crate::base::{
|
||||
Context, Dialog, DialogScaling, Font, IdGenerator, OsSpecificSettings, Resource,
|
||||
ResourceInfoAsCHeaderCode, ResourceInfoAsRustCode, Scope,
|
||||
};
|
||||
use std::io::Write;
|
||||
use std::path::Path;
|
||||
|
||||
mod base;
|
||||
mod color_panel;
|
||||
pub mod constants;
|
||||
mod empty_panel;
|
||||
mod ext;
|
||||
mod group_panel;
|
||||
mod header_panel;
|
||||
mod hidden_panel;
|
||||
mod instance_panel;
|
||||
mod mapping_panel;
|
||||
mod mapping_row_panel;
|
||||
mod mapping_rows_panel;
|
||||
mod message_panel;
|
||||
mod shared_group_mapping_panel;
|
||||
mod simple_editor_panel;
|
||||
mod unit_panel;
|
||||
mod welcome_panel;
|
||||
|
||||
pub fn generate_dialog_files(rc_dir: impl AsRef<Path>, bindings_file: impl AsRef<Path>) {
|
||||
let default_font = Font {
|
||||
name: "Ms Shell Dlg",
|
||||
size: 8,
|
||||
};
|
||||
let default_dialog = Dialog {
|
||||
font: Some(default_font),
|
||||
..Default::default()
|
||||
};
|
||||
let default_scaling = {
|
||||
let horizontal_scale = 1.0;
|
||||
let vertical_scale = 1.0;
|
||||
DialogScaling {
|
||||
x_scale: horizontal_scale,
|
||||
y_scale: vertical_scale,
|
||||
width_scale: horizontal_scale,
|
||||
height_scale: vertical_scale,
|
||||
}
|
||||
};
|
||||
let global_scope = {
|
||||
Scope {
|
||||
linux: {
|
||||
let horizontal_scale = 1.0;
|
||||
let vertical_scale = 1.0;
|
||||
OsSpecificSettings {
|
||||
scaling: DialogScaling {
|
||||
x_scale: horizontal_scale,
|
||||
y_scale: vertical_scale,
|
||||
width_scale: horizontal_scale,
|
||||
height_scale: vertical_scale,
|
||||
},
|
||||
}
|
||||
},
|
||||
windows: OsSpecificSettings {
|
||||
scaling: default_scaling,
|
||||
},
|
||||
macos: {
|
||||
let horizontal_scale = 1.0;
|
||||
let vertical_scale = 1.0;
|
||||
OsSpecificSettings {
|
||||
scaling: DialogScaling {
|
||||
x_scale: horizontal_scale,
|
||||
y_scale: vertical_scale,
|
||||
width_scale: horizontal_scale,
|
||||
height_scale: vertical_scale,
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
};
|
||||
let header_panel_scope = {
|
||||
let horizontal_scale = 1.0;
|
||||
let vertical_scale = 0.8;
|
||||
Scope {
|
||||
windows: OsSpecificSettings {
|
||||
scaling: DialogScaling {
|
||||
x_scale: horizontal_scale,
|
||||
y_scale: vertical_scale,
|
||||
width_scale: horizontal_scale,
|
||||
height_scale: vertical_scale,
|
||||
},
|
||||
},
|
||||
..global_scope
|
||||
}
|
||||
};
|
||||
let mapping_panel_scope = {
|
||||
Scope {
|
||||
windows: {
|
||||
let horizontal_scale = 1.0;
|
||||
let vertical_scale = 0.8;
|
||||
OsSpecificSettings {
|
||||
scaling: DialogScaling {
|
||||
x_scale: horizontal_scale,
|
||||
y_scale: vertical_scale,
|
||||
width_scale: horizontal_scale,
|
||||
height_scale: vertical_scale,
|
||||
},
|
||||
}
|
||||
},
|
||||
macos: {
|
||||
let horizontal_scale = 1.0;
|
||||
let vertical_scale = 0.92;
|
||||
OsSpecificSettings {
|
||||
scaling: DialogScaling {
|
||||
x_scale: horizontal_scale,
|
||||
y_scale: vertical_scale,
|
||||
width_scale: horizontal_scale,
|
||||
height_scale: vertical_scale,
|
||||
},
|
||||
}
|
||||
},
|
||||
..global_scope
|
||||
}
|
||||
};
|
||||
let mut ids = IdGenerator::new(30_000);
|
||||
let context = Context {
|
||||
default_dialog,
|
||||
scopes: [
|
||||
("MAPPING_PANEL", mapping_panel_scope),
|
||||
("HEADER_PANEL", header_panel_scope),
|
||||
]
|
||||
.into_iter()
|
||||
.map(|(key, value)| (key.to_string(), value))
|
||||
.collect(),
|
||||
global_scope,
|
||||
};
|
||||
let group_panel_dialog = group_panel::create(context.scoped("MAPPING_PANEL"), &mut ids);
|
||||
let header_panel_dialog = header_panel::create(context.scoped("HEADER_PANEL"), &mut ids);
|
||||
let mapping_panel_dialog = mapping_panel::create(context.scoped("MAPPING_PANEL"), &mut ids);
|
||||
let mapping_row_panel_dialog = mapping_row_panel::create(context.global(), &mut ids);
|
||||
let mapping_rows_panel_dialog = mapping_rows_panel::create(context.global(), &mut ids);
|
||||
let message_panel_dialog = message_panel::create(context.global(), &mut ids);
|
||||
let shared_group_mapping_panel_dialog =
|
||||
shared_group_mapping_panel::create(context.scoped("MAPPING_PANEL"), &mut ids);
|
||||
let unit_panel_dialog = {
|
||||
instance_panel::create(
|
||||
context.global(),
|
||||
&mut ids,
|
||||
header_panel_dialog.rect.height,
|
||||
mapping_rows_panel_dialog.rect.height,
|
||||
)
|
||||
};
|
||||
let main_panel_dialog = {
|
||||
unit_panel::create(
|
||||
context.global(),
|
||||
&mut ids,
|
||||
header_panel_dialog.rect.height,
|
||||
mapping_rows_panel_dialog.rect.height,
|
||||
)
|
||||
};
|
||||
let simple_editor_panel_dialog = simple_editor_panel::create(context.global(), &mut ids);
|
||||
let empty_panel_dialog = empty_panel::create(context.global(), &mut ids);
|
||||
let setup_panel_dialog = welcome_panel::create(context.global(), &mut ids);
|
||||
let color_panel_dialog = color_panel::create(context.global(), &mut ids);
|
||||
let resource = Resource {
|
||||
dialogs: vec![
|
||||
group_panel_dialog,
|
||||
header_panel_dialog,
|
||||
mapping_panel_dialog,
|
||||
mapping_row_panel_dialog,
|
||||
mapping_rows_panel_dialog,
|
||||
message_panel_dialog,
|
||||
shared_group_mapping_panel_dialog,
|
||||
unit_panel_dialog,
|
||||
main_panel_dialog,
|
||||
simple_editor_panel_dialog,
|
||||
empty_panel_dialog,
|
||||
setup_panel_dialog,
|
||||
color_panel_dialog,
|
||||
hidden_panel::create(context.global(), &mut ids),
|
||||
],
|
||||
};
|
||||
let header_info = resource.generate_info(&context);
|
||||
// Write C header file (in case we want to use a resource editor to preview the dialogs)
|
||||
let c_header_code = ResourceInfoAsCHeaderCode(&header_info).to_string();
|
||||
std::fs::write(rc_dir.as_ref().join("resource.h"), c_header_code)
|
||||
.expect("couldn't write C header file");
|
||||
// Write Rust file (so we don't have to do it via bindgen, which is slow)
|
||||
let rust_code = ResourceInfoAsRustCode(&header_info).to_string();
|
||||
std::fs::write(bindings_file, rust_code).expect("couldn't write Rust bindings file");
|
||||
// Write rc file
|
||||
let rc_file_header = include_str!("rc_file_header.txt");
|
||||
let rc_file_footer = include_str!("rc_file_footer.txt");
|
||||
let rc_file_content = format!("{rc_file_header}\n\n{resource}\n\n{rc_file_footer}");
|
||||
let mut output = Vec::new();
|
||||
// Write UTF_16LE BOM
|
||||
output.write_all(&[0xFF, 0xFE]).unwrap();
|
||||
// Write UTF_16LE contents
|
||||
for utf16 in rc_file_content.encode_utf16() {
|
||||
output.write_all(&utf16.to_le_bytes()).unwrap();
|
||||
}
|
||||
std::fs::write(rc_dir.as_ref().join("msvc.rc"), output).expect("couldn't write rc file");
|
||||
}
|
||||
@@ -0,0 +1,772 @@
|
||||
use crate::base::*;
|
||||
use crate::ext::*;
|
||||
|
||||
pub fn create(context: ScopedContext, ids: &mut IdGenerator) -> Dialog {
|
||||
use Condition::*;
|
||||
use Style::*;
|
||||
let mapping_controls = [
|
||||
// groupbox(
|
||||
// "Mapping",
|
||||
// ids.named_id("ID_MAPPING_PANEL_LABEL"),
|
||||
// context.rect(7, 1, 435, 67),
|
||||
// ),
|
||||
ltext(
|
||||
"Mapping",
|
||||
ids.named_id("ID_MAPPING_PANEL_MAPPING_LABEL"),
|
||||
context.rect(7, 1, 435, 9),
|
||||
),
|
||||
ltext(
|
||||
"Feedback",
|
||||
ids.named_id("ID_MAPPING_PANEL_FEEDBACK_LABEL"),
|
||||
context.rect(11, 53, 34, 9),
|
||||
) + NOT_WS_GROUP,
|
||||
combobox(
|
||||
ids.named_id("ID_MAPPING_FEEDBACK_SEND_BEHAVIOR_COMBO_BOX"),
|
||||
context.rect(48, 51, 120, 15),
|
||||
) + CBS_DROPDOWNLIST
|
||||
+ CBS_HASSTRINGS
|
||||
+ WS_TABSTOP,
|
||||
context.checkbox(
|
||||
"Show in projection",
|
||||
ids.named_id("ID_MAPPING_SHOW_IN_PROJECTION_CHECK_BOX"),
|
||||
rect(180, 53, 74, 8),
|
||||
) + WS_GROUP
|
||||
+ WS_TABSTOP,
|
||||
pushbutton(
|
||||
"Advanced settings",
|
||||
ids.named_id("ID_MAPPING_ADVANCED_BUTTON"),
|
||||
context.rect(259, 50, 87, 14),
|
||||
) + NOT_WS_TABSTOP,
|
||||
pushbutton(
|
||||
"Find in mapping list",
|
||||
ids.named_id("ID_MAPPING_FIND_IN_LIST_BUTTON"),
|
||||
context.rect(352, 50, 87, 14),
|
||||
) + NOT_WS_TABSTOP,
|
||||
];
|
||||
let source_controls = [
|
||||
// groupbox(
|
||||
// "Source",
|
||||
// ids.named_id("ID_SOURCE_PANEL_LABEL"),
|
||||
// context.rect(7, 67, 165, 165),
|
||||
// ) + WS_GROUP,
|
||||
ltext(
|
||||
"Source",
|
||||
ids.named_id("ID_MAPPING_PANEL_SOURCE_LABEL"),
|
||||
context.rect(7, 67, 165, 9),
|
||||
) + WS_GROUP,
|
||||
pushbutton(
|
||||
"Learn",
|
||||
ids.named_id("ID_SOURCE_LEARN_BUTTON"),
|
||||
context.rect(11, 77, 157, 14),
|
||||
),
|
||||
ltext(
|
||||
"Category",
|
||||
ids.named_id("ID_MAPPING_PANEL_SOURCE_CATEGORY_LABEL"),
|
||||
context.rect(11, 98, 31, 9),
|
||||
) + NOT_WS_GROUP,
|
||||
dropdown(
|
||||
ids.named_id("ID_SOURCE_CATEGORY_COMBO_BOX"),
|
||||
context.rect(48, 96, 120, 15),
|
||||
) + WS_TABSTOP,
|
||||
ltext(
|
||||
"Type",
|
||||
ids.named_id("ID_SOURCE_TYPE_LABEL_TEXT"),
|
||||
context.rect(11, 118, 32, 9),
|
||||
) + NOT_WS_GROUP,
|
||||
dropdown(
|
||||
ids.named_id("ID_SOURCE_TYPE_COMBO_BOX"),
|
||||
context.rect(48, 116, 120, 15),
|
||||
) + WS_VSCROLL
|
||||
+ WS_TABSTOP,
|
||||
ltext(
|
||||
"Message",
|
||||
ids.named_id("ID_SOURCE_MIDI_MESSAGE_TYPE_LABEL_TEXT"),
|
||||
context.rect(11, 138, 30, 9),
|
||||
) + NOT_WS_GROUP,
|
||||
ltext(
|
||||
"Channel",
|
||||
ids.named_id("ID_SOURCE_CHANNEL_LABEL"),
|
||||
context.rect(11, 138, 32, 9),
|
||||
) + NOT_WS_GROUP,
|
||||
dropdown(
|
||||
ids.named_id("ID_SOURCE_CHANNEL_COMBO_BOX"),
|
||||
context.rect(48, 136, 120, 15),
|
||||
) + WS_VSCROLL
|
||||
+ WS_TABSTOP,
|
||||
edittext(
|
||||
ids.named_id("ID_SOURCE_LINE_3_EDIT_CONTROL"),
|
||||
context.rect(48, 135, 120, 14),
|
||||
) + ES_AUTOHSCROLL,
|
||||
ltext(
|
||||
"Note/CC number",
|
||||
ids.named_id("ID_SOURCE_NOTE_OR_CC_NUMBER_LABEL_TEXT"),
|
||||
context.rect(11, 158, 34, 9),
|
||||
) + NOT_WS_GROUP,
|
||||
context.checkbox(
|
||||
"RPN",
|
||||
ids.named_id("ID_SOURCE_RPN_CHECK_BOX"),
|
||||
rect(48, 158, 30, 8),
|
||||
) + WS_TABSTOP,
|
||||
dropdown(
|
||||
ids.named_id("ID_SOURCE_LINE_4_COMBO_BOX_1"),
|
||||
context.rect(47, 156, 26, 15),
|
||||
) + WS_TABSTOP,
|
||||
edittext(
|
||||
ids.named_id("ID_SOURCE_NUMBER_EDIT_CONTROL"),
|
||||
context.rect(87, 155, 80, 14),
|
||||
) + ES_AUTOHSCROLL,
|
||||
dropdown(
|
||||
ids.named_id("ID_SOURCE_NUMBER_COMBO_BOX"),
|
||||
context.rect(84, 156, 84, 15),
|
||||
) + WS_VSCROLL
|
||||
+ WS_TABSTOP,
|
||||
pushbutton(
|
||||
"Pick",
|
||||
ids.named_id("ID_SOURCE_LINE_4_BUTTON"),
|
||||
context.rect(47, 155, 26, 14),
|
||||
),
|
||||
ltext(
|
||||
"Character",
|
||||
ids.named_id("ID_SOURCE_CHARACTER_LABEL_TEXT"),
|
||||
context.rect(11, 178, 32, 9),
|
||||
) + NOT_WS_GROUP,
|
||||
dropdown(
|
||||
ids.named_id("ID_SOURCE_CHARACTER_COMBO_BOX"),
|
||||
context.rect(48, 176, 120, 15),
|
||||
) + WS_TABSTOP,
|
||||
pushbutton(
|
||||
"Pick",
|
||||
ids.named_id("ID_SOURCE_LINE_5_BUTTON"),
|
||||
context.rect(47, 176, 26, 14),
|
||||
),
|
||||
edittext(
|
||||
ids.named_id("ID_SOURCE_LINE_5_EDIT_CONTROL"),
|
||||
context.rect(87, 176, 80, 14),
|
||||
) + ES_AUTOHSCROLL,
|
||||
context.checkbox(
|
||||
"14-bit values",
|
||||
ids.named_id("ID_SOURCE_14_BIT_CHECK_BOX"),
|
||||
rect(47, 192, 56, 8),
|
||||
) + WS_TABSTOP,
|
||||
ltext(
|
||||
"Address",
|
||||
ids.named_id("ID_SOURCE_OSC_ADDRESS_LABEL_TEXT"),
|
||||
context.rect(11, 202, 139, 9),
|
||||
) + NOT_WS_GROUP,
|
||||
edittext(
|
||||
ids.named_id("ID_SOURCE_OSC_ADDRESS_PATTERN_EDIT_CONTROL"),
|
||||
context.rect(11, 213, 140, 14),
|
||||
) + ES_AUTOHSCROLL,
|
||||
pushbutton(
|
||||
"...",
|
||||
ids.named_id("ID_SOURCE_SCRIPT_DETAIL_BUTTON"),
|
||||
context.rect(155, 213, 13, 14),
|
||||
),
|
||||
];
|
||||
let target_controls = [
|
||||
// groupbox(
|
||||
// "Target",
|
||||
// ids.named_id("ID_TARGET_PANEL_LABEL"),
|
||||
// context.rect(177, 67, 265, 165),
|
||||
// ),
|
||||
ltext(
|
||||
"Target",
|
||||
ids.named_id("ID_MAPPING_PANEL_TARGET_LABEL"),
|
||||
context.rect(177, 67, 265, 9),
|
||||
),
|
||||
pushbutton(
|
||||
"Learn",
|
||||
ids.named_id("ID_TARGET_LEARN_BUTTON"),
|
||||
context.rect(181, 77, 50, 14),
|
||||
) + WS_GROUP,
|
||||
pushbutton(
|
||||
"Menu",
|
||||
ids.named_id("ID_TARGET_MENU_BUTTON"),
|
||||
context.rect(236, 77, 42, 14),
|
||||
) + NOT_WS_TABSTOP,
|
||||
ltext(
|
||||
"Hint",
|
||||
ids.named_id("ID_TARGET_HINT"),
|
||||
context.rect(285, 80, 155, 9),
|
||||
) + WS_TABSTOP,
|
||||
ltext(
|
||||
"Type",
|
||||
ids.named_id("ID_MAPPING_PANEL_TARGET_TYPE_LABEL"),
|
||||
context.rect(181, 98, 35, 9),
|
||||
) + NOT_WS_GROUP,
|
||||
dropdown(
|
||||
ids.named_id("ID_TARGET_CATEGORY_COMBO_BOX"),
|
||||
context.rect(220, 96, 58, 15),
|
||||
) + WS_TABSTOP,
|
||||
pushbutton(
|
||||
"Target type",
|
||||
ids.named_id("ID_TARGET_TYPE_BUTTON"),
|
||||
context.rect(283, 96, 155, 15),
|
||||
),
|
||||
ltext(
|
||||
"Action name",
|
||||
ids.named_id("ID_TARGET_LINE_2_LABEL_2"),
|
||||
context.rect(220, 118, 189, 9),
|
||||
) + NOT_WS_GROUP,
|
||||
ltext(
|
||||
"Hint",
|
||||
ids.named_id("ID_TARGET_LINE_2_LABEL_3"),
|
||||
context.rect(412, 118, 26, 9),
|
||||
) + NOT_WS_GROUP,
|
||||
ltext(
|
||||
"Line 2",
|
||||
ids.named_id("ID_TARGET_LINE_2_LABEL_1"),
|
||||
context.rect(181, 118, 35, 9),
|
||||
) + NOT_WS_GROUP,
|
||||
dropdown(
|
||||
ids.named_id("ID_TARGET_LINE_2_COMBO_BOX_1"),
|
||||
context.rect(220, 116, 58, 30),
|
||||
) + WS_VSCROLL
|
||||
+ WS_TABSTOP,
|
||||
edittext(
|
||||
ids.named_id("ID_TARGET_LINE_2_EDIT_CONTROL"),
|
||||
context.rect(282, 115, 127, 14),
|
||||
) + ES_AUTOHSCROLL,
|
||||
dropdown(
|
||||
ids.named_id("ID_TARGET_LINE_2_COMBO_BOX_2"),
|
||||
context.rect(283, 116, 127, 30),
|
||||
) + WS_VSCROLL
|
||||
+ WS_TABSTOP,
|
||||
pushbutton(
|
||||
"Pick",
|
||||
ids.named_id("ID_TARGET_LINE_2_BUTTON"),
|
||||
context.rect(412, 114, 26, 14),
|
||||
),
|
||||
ltext(
|
||||
"Line 3",
|
||||
ids.named_id("ID_TARGET_LINE_3_LABEL_1"),
|
||||
context.rect(181, 138, 35, 9),
|
||||
) + NOT_WS_GROUP,
|
||||
dropdown(
|
||||
ids.named_id("ID_TARGET_LINE_3_COMBO_BOX_1"),
|
||||
context.rect(220, 136, 58, 30),
|
||||
) + WS_VSCROLL
|
||||
+ WS_TABSTOP,
|
||||
edittext(
|
||||
ids.named_id("ID_TARGET_LINE_3_EDIT_CONTROL"),
|
||||
context.rect(282, 135, 127, 14),
|
||||
) + ES_AUTOHSCROLL,
|
||||
dropdown(
|
||||
ids.named_id("ID_TARGET_LINE_3_COMBO_BOX_2"),
|
||||
context.rect(283, 136, 155, 30),
|
||||
) + WS_VSCROLL
|
||||
+ WS_TABSTOP,
|
||||
ltext(
|
||||
"Parameter",
|
||||
ids.named_id("ID_TARGET_LINE_3_LABEL_2"),
|
||||
context.rect(220, 138, 189, 9),
|
||||
) + NOT_WS_GROUP,
|
||||
ltext(
|
||||
"Hint",
|
||||
ids.named_id("ID_TARGET_LINE_3_LABEL_3"),
|
||||
context.rect(412, 138, 26, 9),
|
||||
) + NOT_WS_GROUP,
|
||||
pushbutton(
|
||||
"Pick",
|
||||
ids.named_id("ID_TARGET_LINE_3_BUTTON"),
|
||||
context.rect(412, 134, 26, 14),
|
||||
),
|
||||
ltext(
|
||||
"Line 4",
|
||||
ids.named_id("ID_TARGET_LINE_4_LABEL_1"),
|
||||
context.rect(181, 158, 35, 9),
|
||||
) + NOT_WS_GROUP,
|
||||
dropdown(
|
||||
ids.named_id("ID_TARGET_LINE_4_COMBO_BOX_1"),
|
||||
context.rect(220, 156, 58, 30),
|
||||
) + WS_VSCROLL
|
||||
+ WS_TABSTOP,
|
||||
edittext(
|
||||
ids.named_id("ID_TARGET_LINE_4_EDIT_CONTROL"),
|
||||
context.rect(282, 155, 127, 14),
|
||||
) + ES_AUTOHSCROLL,
|
||||
dropdown(
|
||||
ids.named_id("ID_TARGET_LINE_4_COMBO_BOX_2"),
|
||||
context.rect(283, 156, 155, 15),
|
||||
) + WS_VSCROLL
|
||||
+ WS_TABSTOP,
|
||||
ltext(
|
||||
"Parameter",
|
||||
ids.named_id("ID_TARGET_LINE_4_LABEL_2"),
|
||||
context.rect(220, 158, 189, 9),
|
||||
) + NOT_WS_GROUP,
|
||||
pushbutton(
|
||||
"Take!",
|
||||
ids.named_id("ID_TARGET_LINE_4_BUTTON"),
|
||||
context.rect(412, 154, 26, 14),
|
||||
),
|
||||
ltext(
|
||||
"Hint",
|
||||
ids.named_id("ID_TARGET_LINE_4_LABEL_3"),
|
||||
context.rect(412, 158, 26, 9),
|
||||
) + NOT_WS_GROUP,
|
||||
ltext(
|
||||
"Line 5",
|
||||
ids.named_id("ID_TARGET_LINE_5_LABEL_1"),
|
||||
context.rect(181, 178, 35, 9),
|
||||
) + NOT_WS_GROUP,
|
||||
edittext(
|
||||
ids.named_id("ID_TARGET_LINE_5_EDIT_CONTROL"),
|
||||
context.rect(282, 175, 127, 14),
|
||||
) + ES_AUTOHSCROLL,
|
||||
context.checkbox(
|
||||
"Monitoring FX",
|
||||
ids.named_id("ID_TARGET_CHECK_BOX_1"),
|
||||
rect(181, 175, 68, 8),
|
||||
) + WS_TABSTOP,
|
||||
context.checkbox(
|
||||
"Track must be selected",
|
||||
ids.named_id("ID_TARGET_CHECK_BOX_2"),
|
||||
rect(255, 175, 101, 8),
|
||||
) + WS_TABSTOP,
|
||||
context.checkbox(
|
||||
"FX must have focus",
|
||||
ids.named_id("ID_TARGET_CHECK_BOX_3"),
|
||||
rect(363, 175, 76, 8),
|
||||
) + WS_TABSTOP,
|
||||
context.checkbox(
|
||||
"Monitoring FX",
|
||||
ids.named_id("ID_TARGET_CHECK_BOX_4"),
|
||||
rect(181, 195, 69, 8),
|
||||
) + WS_TABSTOP,
|
||||
context.checkbox(
|
||||
"Track must be selected",
|
||||
ids.named_id("ID_TARGET_CHECK_BOX_5"),
|
||||
rect(255, 195, 101, 8),
|
||||
) + WS_TABSTOP,
|
||||
context.checkbox(
|
||||
"FX must have focus",
|
||||
ids.named_id("ID_TARGET_CHECK_BOX_6"),
|
||||
rect(363, 195, 76, 8),
|
||||
) + WS_TABSTOP,
|
||||
ltext(
|
||||
"Value",
|
||||
ids.named_id("ID_TARGET_VALUE_LABEL_TEXT"),
|
||||
context.rect(182, 216, 20, 9),
|
||||
) + NOT_WS_GROUP,
|
||||
pushbutton(
|
||||
"Off",
|
||||
ids.named_id("ID_TARGET_VALUE_OFF_BUTTON"),
|
||||
context.rect(210, 213, 32, 14),
|
||||
),
|
||||
pushbutton(
|
||||
"On",
|
||||
ids.named_id("ID_TARGET_VALUE_ON_BUTTON"),
|
||||
context.rect(250, 213, 32, 14),
|
||||
),
|
||||
slider(
|
||||
ids.named_id("ID_TARGET_VALUE_SLIDER_CONTROL"),
|
||||
context.rect(215, 213, 74, 15),
|
||||
) + WS_TABSTOP,
|
||||
edittext(
|
||||
ids.named_id("ID_TARGET_VALUE_EDIT_CONTROL"),
|
||||
context.rect(289, 213, 30, 14),
|
||||
) + ES_AUTOHSCROLL,
|
||||
ltext(
|
||||
"% 1 ms",
|
||||
ids.named_id("ID_TARGET_VALUE_TEXT"),
|
||||
context.rect(321, 216, 71, 9),
|
||||
) + SS_WORDELLIPSIS
|
||||
+ NOT_WS_GROUP,
|
||||
pushbutton(
|
||||
"bpm (bpm)",
|
||||
ids.named_id("ID_TARGET_UNIT_BUTTON"),
|
||||
context.rect(393, 213, 40, 14),
|
||||
),
|
||||
];
|
||||
let glue_controls = [
|
||||
// groupbox(
|
||||
// "Glue",
|
||||
// ids.named_id("ID_GLUE_PANEL_LABEL"),
|
||||
// context.rect(7, 232, 435, 239),
|
||||
// ),
|
||||
ltext(
|
||||
"Glue",
|
||||
ids.named_id("ID_MAPPING_PANEL_GLUE_LABEL"),
|
||||
context.rect(7, 232, 435, 9),
|
||||
),
|
||||
pushbutton(
|
||||
"Reset to defaults",
|
||||
ids.named_id("ID_SETTINGS_RESET_BUTTON"),
|
||||
context.rect(11, 243, 211, 14),
|
||||
),
|
||||
ltext(
|
||||
"Source",
|
||||
ids.named_id("ID_SETTINGS_SOURCE_LABEL"),
|
||||
context.rect(15, 281, 24, 9),
|
||||
) + NOT_WS_GROUP,
|
||||
groupbox(
|
||||
"Source",
|
||||
ids.named_id("ID_SETTINGS_SOURCE_GROUP"),
|
||||
context.rect(55, 270, 74, 15),
|
||||
) + WS_GROUP
|
||||
+ SkipOnMacOs,
|
||||
ltext(
|
||||
"Min",
|
||||
ids.named_id("ID_SETTINGS_SOURCE_MIN_LABEL"),
|
||||
context.rect(41, 273, 15, 9),
|
||||
) + NOT_WS_GROUP,
|
||||
slider(
|
||||
ids.named_id("ID_SETTINGS_MIN_SOURCE_VALUE_SLIDER_CONTROL"),
|
||||
context.rect(55, 270, 74, 15),
|
||||
) + WS_TABSTOP,
|
||||
edittext(
|
||||
ids.named_id("ID_SETTINGS_MIN_SOURCE_VALUE_EDIT_CONTROL"),
|
||||
context.rect(129, 271, 30, 14),
|
||||
) + ES_AUTOHSCROLL,
|
||||
ltext(
|
||||
"Max",
|
||||
ids.named_id("ID_SETTINGS_SOURCE_MAX_LABEL"),
|
||||
context.rect(41, 291, 15, 9),
|
||||
) + NOT_WS_GROUP,
|
||||
slider(
|
||||
ids.named_id("ID_SETTINGS_MAX_SOURCE_VALUE_SLIDER_CONTROL"),
|
||||
context.rect(55, 288, 74, 15),
|
||||
) + WS_TABSTOP,
|
||||
edittext(
|
||||
ids.named_id("ID_SETTINGS_MAX_SOURCE_VALUE_EDIT_CONTROL"),
|
||||
context.rect(129, 288, 30, 14),
|
||||
) + ES_AUTOHSCROLL,
|
||||
ltext(
|
||||
"Out-of-range behavior",
|
||||
ids.named_id("ID_MODE_OUT_OF_RANGE_LABEL_TEXT"),
|
||||
context.rect(15, 308, 70, 9),
|
||||
) + NOT_WS_GROUP,
|
||||
dropdown(
|
||||
ids.named_id("ID_MODE_OUT_OF_RANGE_COMBOX_BOX"),
|
||||
context.rect(92, 306, 125, 15),
|
||||
) + WS_TABSTOP,
|
||||
ltext(
|
||||
"Group interaction",
|
||||
ids.named_id("ID_MODE_GROUP_INTERACTION_LABEL_TEXT"),
|
||||
context.rect(15, 327, 71, 9),
|
||||
) + NOT_WS_GROUP,
|
||||
dropdown(
|
||||
ids.named_id("ID_MODE_GROUP_INTERACTION_COMBO_BOX"),
|
||||
context.rect(92, 325, 125, 15),
|
||||
) + WS_TABSTOP,
|
||||
ltext(
|
||||
"Target",
|
||||
ids.named_id("ID_SETTINGS_TARGET_LABEL_TEXT"),
|
||||
context.rect(231, 281, 22, 9),
|
||||
) + NOT_WS_GROUP,
|
||||
ltext(
|
||||
"Value sequence",
|
||||
ids.named_id("ID_SETTINGS_TARGET_SEQUENCE_LABEL_TEXT"),
|
||||
context.rect(231, 246, 55, 9),
|
||||
) + NOT_WS_GROUP,
|
||||
edittext(
|
||||
ids.named_id("ID_MODE_TARGET_SEQUENCE_EDIT_CONTROL"),
|
||||
context.rect(288, 243, 149, 14),
|
||||
) + ES_AUTOHSCROLL,
|
||||
groupbox(
|
||||
"Target",
|
||||
ids.named_id("ID_SETTINGS_TARGET_GROUP"),
|
||||
context.rect(271, 270, 75, 15),
|
||||
) + SkipOnMacOs,
|
||||
ltext(
|
||||
"Min",
|
||||
ids.named_id("ID_SETTINGS_MIN_TARGET_LABEL_TEXT"),
|
||||
context.rect(257, 273, 15, 9),
|
||||
) + NOT_WS_GROUP,
|
||||
slider(
|
||||
ids.named_id("ID_SETTINGS_MIN_TARGET_VALUE_SLIDER_CONTROL"),
|
||||
context.rect(271, 270, 75, 15),
|
||||
) + WS_TABSTOP,
|
||||
edittext(
|
||||
ids.named_id("ID_SETTINGS_MIN_TARGET_VALUE_EDIT_CONTROL"),
|
||||
context.rect(347, 270, 30, 14),
|
||||
) + ES_AUTOHSCROLL,
|
||||
ltext(
|
||||
"% 1 ms",
|
||||
ids.named_id("ID_SETTINGS_MIN_TARGET_VALUE_TEXT"),
|
||||
context.rect(379, 273, 56, 9),
|
||||
) + SS_WORDELLIPSIS
|
||||
+ NOT_WS_GROUP,
|
||||
ltext(
|
||||
"Max",
|
||||
ids.named_id("ID_SETTINGS_MAX_TARGET_LABEL_TEXT"),
|
||||
context.rect(257, 291, 15, 9),
|
||||
) + NOT_WS_GROUP,
|
||||
slider(
|
||||
ids.named_id("ID_SETTINGS_MAX_TARGET_VALUE_SLIDER_CONTROL"),
|
||||
context.rect(271, 287, 75, 15),
|
||||
) + WS_TABSTOP,
|
||||
edittext(
|
||||
ids.named_id("ID_SETTINGS_MAX_TARGET_VALUE_EDIT_CONTROL"),
|
||||
context.rect(347, 288, 30, 14),
|
||||
) + ES_AUTOHSCROLL,
|
||||
ltext(
|
||||
"% 127 ms",
|
||||
ids.named_id("ID_SETTINGS_MAX_TARGET_VALUE_TEXT"),
|
||||
context.rect(379, 291, 56, 9),
|
||||
) + SS_WORDELLIPSIS
|
||||
+ NOT_WS_GROUP,
|
||||
context.checkbox(
|
||||
"Reverse",
|
||||
ids.named_id("ID_SETTINGS_REVERSE_CHECK_BOX"),
|
||||
rect(400, 307, 39, 8),
|
||||
) + WS_TABSTOP,
|
||||
dropdown(
|
||||
ids.named_id("IDC_MODE_FEEDBACK_TYPE_COMBO_BOX"),
|
||||
context.rect(231, 306, 163, 30),
|
||||
) + WS_TABSTOP,
|
||||
edittext(
|
||||
ids.named_id("ID_MODE_EEL_FEEDBACK_TRANSFORMATION_EDIT_CONTROL"),
|
||||
context.rect(231, 323, 179, 14),
|
||||
) + ES_AUTOHSCROLL,
|
||||
pushbutton(
|
||||
"...",
|
||||
ids.named_id("IDC_MODE_FEEDBACK_TYPE_BUTTON"),
|
||||
context.rect(413, 323, 25, 14),
|
||||
),
|
||||
groupbox(
|
||||
"For knobs/faders and buttons (control only)",
|
||||
ids.named_id("ID_MODE_KNOB_FADER_GROUP_BOX"),
|
||||
context.rect(11, 344, 211, 123),
|
||||
),
|
||||
ltext(
|
||||
"Mode",
|
||||
ids.named_id("ID_SETTINGS_MODE_LABEL"),
|
||||
context.rect(15, 357, 20, 9),
|
||||
) + NOT_WS_GROUP,
|
||||
dropdown(
|
||||
ids.named_id("ID_SETTINGS_MODE_COMBO_BOX"),
|
||||
context.rect(50, 355, 168, 15),
|
||||
) + WS_TABSTOP,
|
||||
ltext(
|
||||
"Takeover",
|
||||
ids.named_id("ID_MODE_TAKEOVER_LABEL"),
|
||||
context.rect(15, 409, 35, 9),
|
||||
) + NOT_WS_GROUP,
|
||||
dropdown(
|
||||
ids.named_id("ID_MODE_TAKEOVER_MODE"),
|
||||
context.rect(53, 407, 86, 15),
|
||||
) + WS_TABSTOP,
|
||||
context.checkbox(
|
||||
"Round target value",
|
||||
ids.named_id("ID_SETTINGS_ROUND_TARGET_VALUE_CHECK_BOX"),
|
||||
rect(146, 409, 73, 8),
|
||||
) + WS_TABSTOP,
|
||||
ltext(
|
||||
"Control transformation (EEL)",
|
||||
ids.named_id("ID_MODE_EEL_CONTROL_TRANSFORMATION_LABEL"),
|
||||
context.rect(15, 423, 95, 9),
|
||||
) + NOT_WS_GROUP,
|
||||
edittext(
|
||||
ids.named_id("ID_MODE_EEL_CONTROL_TRANSFORMATION_EDIT_CONTROL"),
|
||||
context.rect(15, 435, 184, 14),
|
||||
) + ES_AUTOHSCROLL,
|
||||
pushbutton(
|
||||
"...",
|
||||
ids.named_id("ID_MODE_EEL_CONTROL_TRANSFORMATION_DETAIL_BUTTON"),
|
||||
context.rect(201, 435, 13, 14),
|
||||
),
|
||||
groupbox(
|
||||
"For encoders and incremental buttons (control only)",
|
||||
ids.named_id("ID_MODE_RELATIVE_GROUP_BOX"),
|
||||
context.rect(227, 344, 211, 61),
|
||||
),
|
||||
ltext(
|
||||
"Step size",
|
||||
ids.named_id("ID_SETTINGS_STEP_SIZE_LABEL_TEXT"),
|
||||
context.rect(231, 366, 30, 9),
|
||||
) + NOT_WS_GROUP,
|
||||
groupbox(
|
||||
"Step size",
|
||||
ids.named_id("ID_SETTINGS_STEP_SIZE_GROUP"),
|
||||
context.rect(279, 355, 74, 15),
|
||||
) + SkipOnMacOs,
|
||||
ltext(
|
||||
"Min",
|
||||
ids.named_id("ID_SETTINGS_MIN_STEP_SIZE_LABEL_TEXT"),
|
||||
context.rect(266, 358, 15, 9),
|
||||
) + NOT_WS_GROUP,
|
||||
slider(
|
||||
ids.named_id("ID_SETTINGS_MIN_STEP_SIZE_SLIDER_CONTROL"),
|
||||
context.rect(279, 355, 74, 15),
|
||||
) + WS_TABSTOP,
|
||||
edittext(
|
||||
ids.named_id("ID_SETTINGS_MIN_STEP_SIZE_EDIT_CONTROL"),
|
||||
context.rect(353, 355, 30, 14),
|
||||
) + ES_AUTOHSCROLL,
|
||||
ltext(
|
||||
"% 1 ms",
|
||||
ids.named_id("ID_SETTINGS_MIN_STEP_SIZE_VALUE_TEXT"),
|
||||
context.rect(385, 358, 51, 9),
|
||||
) + SS_WORDELLIPSIS
|
||||
+ NOT_WS_GROUP,
|
||||
ltext(
|
||||
"Max",
|
||||
ids.named_id("ID_SETTINGS_MAX_STEP_SIZE_LABEL_TEXT"),
|
||||
context.rect(266, 376, 15, 9),
|
||||
) + NOT_WS_GROUP,
|
||||
slider(
|
||||
ids.named_id("ID_SETTINGS_MAX_STEP_SIZE_SLIDER_CONTROL"),
|
||||
context.rect(279, 372, 74, 15),
|
||||
) + WS_TABSTOP,
|
||||
edittext(
|
||||
ids.named_id("ID_SETTINGS_MAX_STEP_SIZE_EDIT_CONTROL"),
|
||||
context.rect(353, 372, 30, 14),
|
||||
) + ES_AUTOHSCROLL,
|
||||
ltext(
|
||||
"% 127 ms",
|
||||
ids.named_id("ID_SETTINGS_MAX_STEP_SIZE_VALUE_TEXT"),
|
||||
context.rect(385, 375, 51, 9),
|
||||
) + SS_WORDELLIPSIS
|
||||
+ NOT_WS_GROUP,
|
||||
dropdown(
|
||||
ids.named_id("ID_MODE_RELATIVE_FILTER_COMBO_BOX"),
|
||||
context.rect(231, 388, 104, 15),
|
||||
) + WS_TABSTOP,
|
||||
context.checkbox(
|
||||
"Wrap",
|
||||
ids.named_id("ID_SETTINGS_ROTATE_CHECK_BOX"),
|
||||
rect(342, 391, 30, 8),
|
||||
) + WS_TABSTOP,
|
||||
context.checkbox(
|
||||
"Make absolute",
|
||||
ids.named_id("ID_SETTINGS_MAKE_ABSOLUTE_CHECK_BOX"),
|
||||
rect(375, 391, 60, 8),
|
||||
) + WS_TABSTOP,
|
||||
groupbox(
|
||||
"For buttons (control only)",
|
||||
ids.named_id("ID_MODE_BUTTON_GROUP_BOX"),
|
||||
context.rect(227, 406, 211, 61),
|
||||
),
|
||||
dropdown(
|
||||
ids.named_id("ID_MODE_FIRE_COMBO_BOX"),
|
||||
context.rect(231, 416, 131, 15),
|
||||
) + WS_TABSTOP,
|
||||
dropdown(
|
||||
ids.named_id("ID_MODE_BUTTON_FILTER_COMBO_BOX"),
|
||||
context.rect(367, 416, 68, 15),
|
||||
) + WS_TABSTOP,
|
||||
ltext(
|
||||
"Min",
|
||||
ids.named_id("ID_MODE_FIRE_LINE_2_LABEL_1"),
|
||||
context.rect(231, 436, 30, 9),
|
||||
) + NOT_WS_GROUP,
|
||||
slider(
|
||||
ids.named_id("ID_MODE_FIRE_LINE_2_SLIDER_CONTROL"),
|
||||
context.rect(265, 432, 87, 15),
|
||||
) + WS_TABSTOP,
|
||||
edittext(
|
||||
ids.named_id("ID_MODE_FIRE_LINE_2_EDIT_CONTROL"),
|
||||
context.rect(353, 432, 30, 14),
|
||||
) + ES_AUTOHSCROLL,
|
||||
ltext(
|
||||
"% 1 ms",
|
||||
ids.named_id("ID_MODE_FIRE_LINE_2_LABEL_2"),
|
||||
context.rect(385, 435, 50, 9),
|
||||
) + SS_WORDELLIPSIS
|
||||
+ NOT_WS_GROUP,
|
||||
ltext(
|
||||
"Max",
|
||||
ids.named_id("ID_MODE_FIRE_LINE_3_LABEL_1"),
|
||||
context.rect(231, 454, 31, 9),
|
||||
) + NOT_WS_GROUP,
|
||||
slider(
|
||||
ids.named_id("ID_MODE_FIRE_LINE_3_SLIDER_CONTROL"),
|
||||
context.rect(265, 449, 87, 15),
|
||||
) + WS_TABSTOP,
|
||||
edittext(
|
||||
ids.named_id("ID_MODE_FIRE_LINE_3_EDIT_CONTROL"),
|
||||
context.rect(353, 449, 30, 14),
|
||||
) + ES_AUTOHSCROLL,
|
||||
ltext(
|
||||
"% 127 ms",
|
||||
ids.named_id("ID_MODE_FIRE_LINE_3_LABEL_2"),
|
||||
context.rect(385, 452, 50, 9),
|
||||
) + SS_WORDELLIPSIS
|
||||
+ NOT_WS_GROUP,
|
||||
];
|
||||
let footer_controls = [
|
||||
ltext(
|
||||
"Help left",
|
||||
ids.named_id("ID_MAPPING_HELP_LEFT_SUBJECT_LABEL"),
|
||||
context.rect(7, 475, 183, 9),
|
||||
) + NOT_WS_GROUP,
|
||||
edittext(
|
||||
ids.named_id("ID_MAPPING_HELP_LEFT_CONTENT_LABEL"),
|
||||
context.rect(7, 488, 210, 22),
|
||||
) + ES_MULTILINE
|
||||
+ ES_READONLY
|
||||
+ WS_VSCROLL,
|
||||
static_text(
|
||||
"•",
|
||||
ids.named_id("IDC_MAPPING_MATCHED_INDICATOR_TEXT"),
|
||||
context.rect(220, 495, 8, 8),
|
||||
) + SS_LEFTNOWORDWRAP
|
||||
+ WS_DISABLED
|
||||
+ WS_GROUP
|
||||
+ WS_TABSTOP,
|
||||
ltext(
|
||||
"Help right",
|
||||
ids.named_id("ID_MAPPING_HELP_RIGHT_SUBJECT_LABEL"),
|
||||
context.rect(7 + 225, 475, 183, 9),
|
||||
) + NOT_WS_GROUP,
|
||||
edittext(
|
||||
ids.named_id("ID_MAPPING_HELP_RIGHT_CONTENT_LABEL"),
|
||||
context.rect(7 + 225, 488, 210, 22),
|
||||
) + ES_MULTILINE
|
||||
+ ES_READONLY
|
||||
+ WS_VSCROLL,
|
||||
context.checkbox(
|
||||
"Beep on success",
|
||||
ids.named_id("IDC_BEEP_ON_SUCCESS_CHECK_BOX"),
|
||||
rect(7, 516, 70, 10),
|
||||
) + WS_TABSTOP,
|
||||
pushbutton(
|
||||
"<=",
|
||||
ids.named_id("ID_MAPPING_PANEL_PREVIOUS_BUTTON"),
|
||||
context.rect(160, 514, 30, 14),
|
||||
),
|
||||
// We make this a normal push button instead of an OK button so that
|
||||
// pressing enter in a text field doesn't automatically trigger it.
|
||||
pushbutton(
|
||||
"OK",
|
||||
ids.named_id("ID_MAPPING_PANEL_OK"),
|
||||
context.rect(200, 514, 50, 14),
|
||||
),
|
||||
pushbutton(
|
||||
"=>",
|
||||
ids.named_id("ID_MAPPING_PANEL_NEXT_BUTTON"),
|
||||
context.rect(260, 514, 30, 14),
|
||||
),
|
||||
context.checkbox(
|
||||
"Enabled",
|
||||
ids.named_id("IDC_MAPPING_ENABLED_CHECK_BOX"),
|
||||
rect(405, 516, 39, 10),
|
||||
) + WS_TABSTOP,
|
||||
];
|
||||
Dialog {
|
||||
id: ids.named_id("ID_MAPPING_PANEL"),
|
||||
caption: "Edit mapping",
|
||||
kind: DialogKind::DIALOGEX,
|
||||
rect: context.rect(0, 0, 451, 532),
|
||||
styles: Styles(vec![
|
||||
DS_SETFONT,
|
||||
DS_MODALFRAME,
|
||||
DS_3DLOOK,
|
||||
DS_CENTER,
|
||||
WS_POPUP,
|
||||
WS_VISIBLE,
|
||||
WS_CAPTION,
|
||||
WS_SYSMENU,
|
||||
]),
|
||||
controls: mapping_controls
|
||||
.into_iter()
|
||||
.chain(source_controls)
|
||||
.chain(target_controls)
|
||||
.chain(glue_controls)
|
||||
.chain(footer_controls)
|
||||
.collect(),
|
||||
..context.default_dialog()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
use crate::base::Condition::SkipOnMacOs;
|
||||
use crate::base::*;
|
||||
use crate::constants::{MAPPING_ROW_PANEL_HEIGHT, MAPPING_ROW_PANEL_WIDTH};
|
||||
|
||||
pub fn create(context: ScopedContext, ids: &mut IdGenerator) -> Dialog {
|
||||
use Style::*;
|
||||
let controls = [
|
||||
// Label and on/off checkbox
|
||||
ltext(
|
||||
"Mapping 1",
|
||||
ids.named_id("ID_MAPPING_ROW_MAPPING_LABEL"),
|
||||
context.rect(14, 0, 225, 9),
|
||||
) + NOT_WS_GROUP,
|
||||
context.checkbox(
|
||||
"",
|
||||
ids.named_id("IDC_MAPPING_ROW_ENABLED_CHECK_BOX"),
|
||||
rect(2, 0, 10, 10),
|
||||
) + WS_GROUP,
|
||||
// Mapping actions
|
||||
pushbutton(
|
||||
"Edit",
|
||||
ids.named_id("ID_MAPPING_ROW_EDIT_BUTTON"),
|
||||
context.rect(347, 13, 31, 14),
|
||||
) + NOT_WS_TABSTOP,
|
||||
pushbutton(
|
||||
"Duplicate",
|
||||
ids.named_id("ID_MAPPING_ROW_DUPLICATE_BUTTON"),
|
||||
context.rect(379, 13, 37, 14),
|
||||
) + NOT_WS_TABSTOP,
|
||||
pushbutton(
|
||||
"Remove",
|
||||
ids.named_id("ID_MAPPING_ROW_REMOVE_BUTTON"),
|
||||
context.rect(417, 13, 31, 14),
|
||||
) + NOT_WS_TABSTOP,
|
||||
pushbutton(
|
||||
"Learn source",
|
||||
ids.named_id("ID_MAPPING_ROW_LEARN_SOURCE_BUTTON"),
|
||||
context.rect(347, 28, 47, 14),
|
||||
) + WS_GROUP
|
||||
+ NOT_WS_TABSTOP,
|
||||
pushbutton(
|
||||
"Learn target",
|
||||
ids.named_id("ID_MAPPING_ROW_LEARN_TARGET_BUTTON"),
|
||||
context.rect(395, 28, 53, 14),
|
||||
) + NOT_WS_TABSTOP,
|
||||
// Control/feedback checkboxes
|
||||
context.checkbox(
|
||||
"=>",
|
||||
ids.named_id("ID_MAPPING_ROW_CONTROL_CHECK_BOX"),
|
||||
rect(140, 15, 24, 8),
|
||||
),
|
||||
context.checkbox(
|
||||
"<=",
|
||||
ids.named_id("ID_MAPPING_ROW_FEEDBACK_CHECK_BOX"),
|
||||
rect(140, 30, 24, 8),
|
||||
),
|
||||
// Source and target labels
|
||||
ctext(
|
||||
"MIDI CC Value (ch1, cc5)\r\nbla\r\nbla",
|
||||
ids.named_id("ID_MAPPING_ROW_SOURCE_LABEL_TEXT"),
|
||||
context.rect(43, 12, 94, 34),
|
||||
) + NOT_WS_GROUP,
|
||||
ctext(
|
||||
"FX Param Target\r\nbla\r\nbla\r\nmoin",
|
||||
ids.named_id("ID_MAPPING_ROW_TARGET_LABEL_TEXT"),
|
||||
context.rect(164, 12, 179, 34),
|
||||
) + NOT_WS_GROUP,
|
||||
// Group label
|
||||
rtext(
|
||||
"Group 1",
|
||||
ids.named_id("ID_MAPPING_ROW_GROUP_LABEL"),
|
||||
context.rect(239, 0, 208, 9),
|
||||
) + NOT_WS_GROUP,
|
||||
// Match indicator
|
||||
ltext(
|
||||
"•",
|
||||
ids.named_id("IDC_MAPPING_ROW_MATCHED_INDICATOR_TEXT"),
|
||||
context.rect(3, 23, 8, 8),
|
||||
) + WS_DISABLED,
|
||||
// Up/down buttons
|
||||
groupbox("Up", ids.id(), context.rect(13, 13, 26, 14)) + WS_GROUP + SkipOnMacOs,
|
||||
pushbutton(
|
||||
"Up",
|
||||
ids.named_id("ID_UP_BUTTON"),
|
||||
context.rect(13, 13, 26, 14),
|
||||
),
|
||||
pushbutton(
|
||||
"Down",
|
||||
ids.named_id("ID_DOWN_BUTTON"),
|
||||
context.rect(13, 28, 26, 14),
|
||||
),
|
||||
];
|
||||
Dialog {
|
||||
id: ids.named_id("ID_MAPPING_ROW_PANEL"),
|
||||
kind: DialogKind::DIALOGEX,
|
||||
rect: context.rect(0, 0, MAPPING_ROW_PANEL_WIDTH, MAPPING_ROW_PANEL_HEIGHT),
|
||||
styles: Styles(vec![DS_SETFONT, DS_CONTROL, WS_CHILD]),
|
||||
controls: controls.into_iter().collect(),
|
||||
..context.default_dialog()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
use crate::base::*;
|
||||
use crate::constants::{MAPPING_ROWS_PANEL_HEIGHT, MAPPING_ROWS_PANEL_WIDTH};
|
||||
|
||||
pub fn create(context: ScopedContext, ids: &mut IdGenerator) -> Dialog {
|
||||
use Style::*;
|
||||
let controls = [
|
||||
pushbutton(
|
||||
"Display mappings in all groups",
|
||||
ids.named_id("ID_DISPLAY_ALL_GROUPS_BUTTON"),
|
||||
context.rect(157, 137, 156, 14),
|
||||
),
|
||||
ctext(
|
||||
"There are no mappings in this compartment.",
|
||||
ids.named_id("ID_GROUP_IS_EMPTY_TEXT"),
|
||||
context.rect(149, 121, 173, 9),
|
||||
) + NOT_WS_GROUP,
|
||||
];
|
||||
Dialog {
|
||||
id: ids.named_id("ID_MAPPING_ROWS_PANEL"),
|
||||
kind: DialogKind::DIALOGEX,
|
||||
rect: context.rect(0, 0, MAPPING_ROWS_PANEL_WIDTH, MAPPING_ROWS_PANEL_HEIGHT),
|
||||
styles: Styles(vec![DS_SETFONT, DS_CONTROL, WS_CHILD, WS_VISIBLE]),
|
||||
controls: controls.into_iter().collect(),
|
||||
..context.default_dialog()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
use crate::base::*;
|
||||
|
||||
pub fn create(context: ScopedContext, ids: &mut IdGenerator) -> Dialog {
|
||||
use Style::*;
|
||||
let controls = [ctext(
|
||||
"Some message",
|
||||
ids.named_id("ID_MESSAGE_TEXT"),
|
||||
context.rect(0, 0, 300, 40),
|
||||
) + SS_CENTERIMAGE
|
||||
+ SS_WORDELLIPSIS
|
||||
+ NOT_WS_GROUP];
|
||||
Dialog {
|
||||
id: ids.named_id("ID_MESSAGE_PANEL"),
|
||||
caption: "Helgobox",
|
||||
rect: context.rect(0, 0, 300, 40),
|
||||
styles: Styles(vec![
|
||||
DS_SETFONT,
|
||||
DS_MODALFRAME,
|
||||
DS_3DLOOK,
|
||||
DS_FIXEDSYS,
|
||||
DS_CENTER,
|
||||
WS_POPUP,
|
||||
WS_VISIBLE,
|
||||
WS_CAPTION,
|
||||
WS_SYSMENU,
|
||||
]),
|
||||
ex_styles: Styles(vec![WS_EX_TOPMOST, WS_EX_WINDOWEDGE]),
|
||||
controls: controls.into_iter().collect(),
|
||||
..context.default_dialog()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
#endif // German (Germany) resources
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
@@ -0,0 +1,7 @@
|
||||
// Resource script generated by helgobox-dialogs.
|
||||
//
|
||||
#include "resource.h"
|
||||
#include "windows.h"
|
||||
|
||||
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_DEU)
|
||||
LANGUAGE LANG_GERMAN, SUBLANG_GERMAN
|
||||
@@ -0,0 +1,98 @@
|
||||
use crate::base::*;
|
||||
use crate::ext::*;
|
||||
|
||||
pub fn create(context: ScopedContext, ids: &mut IdGenerator) -> Dialog {
|
||||
use Style::*;
|
||||
let col_1_x = 0;
|
||||
let line_1_y = 0;
|
||||
let line_2_y = line_1_y + 20;
|
||||
let controls = vec![
|
||||
// Name
|
||||
ltext(
|
||||
"Name",
|
||||
ids.named_id("ID_MAPPING_NAME_LABEL"),
|
||||
context.rect(col_1_x, line_1_y + 3, 20, 9),
|
||||
) + NOT_WS_GROUP,
|
||||
edittext(
|
||||
ids.named_id("ID_MAPPING_NAME_EDIT_CONTROL"),
|
||||
context.rect(col_1_x + 28, line_1_y, 131, 14),
|
||||
) + ES_AUTOHSCROLL,
|
||||
// Tags
|
||||
ltext(
|
||||
"Tags",
|
||||
ids.named_id("ID_MAPPING_TAGS_LABEL"),
|
||||
context.rect(col_1_x + 167, line_1_y + 3, 18, 9),
|
||||
) + NOT_WS_GROUP,
|
||||
edittext(
|
||||
ids.named_id("ID_MAPPING_TAGS_EDIT_CONTROL"),
|
||||
context.rect(col_1_x + 189, line_1_y, 131, 14),
|
||||
) + ES_AUTOHSCROLL,
|
||||
// Control/feedback checkboxes
|
||||
context.checkbox(
|
||||
"=> Control",
|
||||
ids.named_id("ID_MAPPING_CONTROL_ENABLED_CHECK_BOX"),
|
||||
rect(col_1_x + 325, line_1_y + 3, 50, 8),
|
||||
) + WS_TABSTOP,
|
||||
context.checkbox(
|
||||
"<= Feedback",
|
||||
ids.named_id("ID_MAPPING_FEEDBACK_ENABLED_CHECK_BOX"),
|
||||
rect(col_1_x + 376, line_1_y + 3, 56, 8),
|
||||
) + WS_TABSTOP,
|
||||
// Conditional activation
|
||||
ltext(
|
||||
"Active",
|
||||
ids.named_id("ID_MAPPING_ACTIVATION_TYPE_LABEL"),
|
||||
context.rect(col_1_x, line_2_y + 2, 21, 9),
|
||||
) + NOT_WS_GROUP,
|
||||
dropdown(
|
||||
ids.named_id("ID_MAPPING_ACTIVATION_TYPE_COMBO_BOX"),
|
||||
context.rect(col_1_x + 28, line_2_y, 102, 15),
|
||||
) + WS_TABSTOP,
|
||||
// Conditional activation criteria 1
|
||||
ltext(
|
||||
"Modifier 1",
|
||||
ids.named_id("ID_MAPPING_ACTIVATION_SETTING_1_LABEL_TEXT"),
|
||||
context.rect(col_1_x + 138, line_2_y + 2, 34, 9),
|
||||
) + NOT_WS_GROUP,
|
||||
pushbutton(
|
||||
"Pick 1",
|
||||
ids.named_id("ID_MAPPING_ACTIVATION_SETTING_1_BUTTON"),
|
||||
context.rect(col_1_x + 177, line_2_y, 90, 15),
|
||||
) + WS_TABSTOP,
|
||||
context.checkbox(
|
||||
"",
|
||||
ids.named_id("ID_MAPPING_ACTIVATION_SETTING_1_CHECK_BOX"),
|
||||
rect(col_1_x + 269, line_2_y + 2, 11, 8),
|
||||
) + WS_TABSTOP,
|
||||
// Conditional activation criteria 2
|
||||
ltext(
|
||||
"Modifier 2",
|
||||
ids.named_id("ID_MAPPING_ACTIVATION_SETTING_2_LABEL_TEXT"),
|
||||
context.rect(col_1_x + 287, line_2_y + 2, 34, 9),
|
||||
) + NOT_WS_GROUP,
|
||||
pushbutton(
|
||||
"Pick 1",
|
||||
ids.named_id("ID_MAPPING_ACTIVATION_SETTING_2_BUTTON"),
|
||||
context.rect(col_1_x + 325, line_2_y, 90, 15),
|
||||
) + WS_TABSTOP,
|
||||
context.checkbox(
|
||||
"",
|
||||
ids.named_id("ID_MAPPING_ACTIVATION_SETTING_2_CHECK_BOX"),
|
||||
rect(col_1_x + 417, line_2_y + 2, 11, 8),
|
||||
) + WS_TABSTOP,
|
||||
edittext(
|
||||
ids.named_id("ID_MAPPING_ACTIVATION_EDIT_CONTROL"),
|
||||
context.rect(col_1_x + 325, line_2_y, 90, 14),
|
||||
) + ES_AUTOHSCROLL,
|
||||
];
|
||||
Dialog {
|
||||
id: ids.named_id("ID_SHARED_GROUP_MAPPING_PANEL"),
|
||||
kind: DialogKind::DIALOGEX,
|
||||
rect: context.rect(0, 0, 440, 37),
|
||||
styles: Styles(vec![
|
||||
DS_SETFONT, DS_CONTROL, DS_CENTER, WS_CHILD, WS_VISIBLE, WS_SYSMENU,
|
||||
]),
|
||||
controls,
|
||||
..context.default_dialog()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
use crate::base::Condition::SkipOnMacOs;
|
||||
use crate::base::*;
|
||||
|
||||
pub fn create(context: ScopedContext, ids: &mut IdGenerator) -> Dialog {
|
||||
use Style::*;
|
||||
let controls = vec![
|
||||
pushbutton(
|
||||
"Open in text editor",
|
||||
ids.named_id("ID_YAML_TEXT_EDITOR_BUTTON"),
|
||||
context.rect(371, 291, 68, 14),
|
||||
) + SkipOnMacOs,
|
||||
edittext(
|
||||
ids.named_id("ID_YAML_EDIT_CONTROL"),
|
||||
context.rect(0, 0, 490, 284),
|
||||
) + ES_MULTILINE
|
||||
+ ES_WANTRETURN
|
||||
+ WS_VSCROLL,
|
||||
pushbutton(
|
||||
"Help",
|
||||
ids.named_id("ID_YAML_HELP_BUTTON"),
|
||||
context.rect(445, 291, 40, 14),
|
||||
),
|
||||
ltext(
|
||||
"",
|
||||
ids.named_id("ID_YAML_EDIT_INFO_TEXT"),
|
||||
context.rect(5, 294, 355, 9),
|
||||
) + NOT_WS_GROUP,
|
||||
];
|
||||
Dialog {
|
||||
id: ids.named_id("ID_YAML_EDITOR_PANEL"),
|
||||
caption: "Editor",
|
||||
rect: context.rect(0, 0, 490, 310),
|
||||
styles: Styles(vec![
|
||||
DS_SETFONT,
|
||||
DS_MODALFRAME,
|
||||
DS_3DLOOK,
|
||||
DS_FIXEDSYS,
|
||||
DS_CENTER,
|
||||
WS_POPUP,
|
||||
WS_VISIBLE,
|
||||
WS_CAPTION,
|
||||
WS_SYSMENU,
|
||||
]),
|
||||
controls,
|
||||
..context.default_dialog()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
use crate::base::*;
|
||||
use crate::constants::{FOOTER_PANEL_HEIGHT, MAIN_PANEL_WIDTH};
|
||||
use crate::ext::divider;
|
||||
|
||||
pub fn create(
|
||||
context: ScopedContext,
|
||||
ids: &mut IdGenerator,
|
||||
effective_header_panel_height: u32,
|
||||
effective_rows_panel_height: u32,
|
||||
) -> Dialog {
|
||||
use Style::*;
|
||||
let footer_y_offset = effective_header_panel_height + effective_rows_panel_height;
|
||||
let create_rect = |x, y, width, height| {
|
||||
let local_rect = context.rect(x, y, width, height);
|
||||
Rect {
|
||||
y: footer_y_offset + local_rect.y,
|
||||
..local_rect
|
||||
}
|
||||
};
|
||||
let line_spacing = 12;
|
||||
let text_line_left = 84;
|
||||
let text_line_right = MAIN_PANEL_WIDTH - text_line_left;
|
||||
let text_line_width = text_line_right - text_line_left;
|
||||
let controls = vec![
|
||||
divider(ids.id(), create_rect(0, 0, MAIN_PANEL_WIDTH, 1)),
|
||||
ctext(
|
||||
"Status 1",
|
||||
ids.named_id("ID_MAIN_PANEL_STATUS_1_TEXT"),
|
||||
create_rect(text_line_left, 5, text_line_width, 9),
|
||||
) + NOT_WS_GROUP,
|
||||
ctext(
|
||||
"Status 2",
|
||||
ids.named_id("ID_MAIN_PANEL_STATUS_2_TEXT"),
|
||||
create_rect(text_line_left, 5 + line_spacing, text_line_width, 9),
|
||||
) + NOT_WS_GROUP,
|
||||
context.checkbox(
|
||||
"",
|
||||
ids.named_id("IDC_UNIT_ENABLED_CHECK_BOX"),
|
||||
create_rect(2, 7 + line_spacing, 10, 14),
|
||||
) + WS_TABSTOP,
|
||||
pushbutton(
|
||||
"Unit",
|
||||
ids.named_id("IDC_UNIT_BUTTON"),
|
||||
create_rect(17, 5 + line_spacing, 65, 14),
|
||||
),
|
||||
pushbutton(
|
||||
"Unit data...",
|
||||
ids.named_id("IDC_EDIT_TAGS_BUTTON"),
|
||||
create_rect(386, 5 + line_spacing, 76, 14),
|
||||
),
|
||||
ctext(
|
||||
"Helgobox",
|
||||
ids.named_id("ID_MAIN_PANEL_VERSION_TEXT"),
|
||||
create_rect(text_line_left, 5 + line_spacing * 2, text_line_width, 9),
|
||||
),
|
||||
];
|
||||
Dialog {
|
||||
id: ids.named_id("ID_MAIN_PANEL"),
|
||||
kind: DialogKind::DIALOGEX,
|
||||
rect: Rect::new(
|
||||
0,
|
||||
0,
|
||||
context.scale_width(MAIN_PANEL_WIDTH),
|
||||
effective_header_panel_height
|
||||
+ effective_rows_panel_height
|
||||
+ context.scale_height(FOOTER_PANEL_HEIGHT),
|
||||
),
|
||||
styles: Styles(vec![DS_SETFONT, DS_CONTROL, WS_CHILD, WS_VISIBLE]),
|
||||
controls,
|
||||
..context.default_dialog()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
use crate::base::*;
|
||||
use crate::ext::ok_button;
|
||||
|
||||
pub fn create(context: ScopedContext, ids: &mut IdGenerator) -> Dialog {
|
||||
use Style::*;
|
||||
Dialog {
|
||||
id: ids.named_id("ID_SETUP_PANEL"),
|
||||
optional: true,
|
||||
caption: "Welcome to Helgobox!",
|
||||
rect: context.rect(0, 0, 250, 280),
|
||||
styles: Styles(vec![
|
||||
// Places the window into the center by default
|
||||
DS_CENTER, // Displays a close button
|
||||
WS_SYSMENU,
|
||||
]),
|
||||
controls: vec![
|
||||
ctext(
|
||||
"Intro text 1",
|
||||
ids.named_id("ID_SETUP_INTRO_TEXT_1"),
|
||||
context.rect(25, 25, 200, 50),
|
||||
),
|
||||
ctext(
|
||||
"Intro text 2",
|
||||
ids.named_id("ID_SETUP_INTRO_TEXT_2"),
|
||||
context.rect(25, 80, 200, 30),
|
||||
),
|
||||
context.checkbox(
|
||||
"Add Playtime button to main toolbar",
|
||||
ids.named_id("ID_SETUP_ADD_PLAYTIME_TOOLBAR_BUTTON"),
|
||||
context.rect(60, 120, 150, 8),
|
||||
),
|
||||
context.checkbox(
|
||||
"Send errors to developer automatically",
|
||||
ids.named_id("ID_SETUP_SEND_ERRORS_TO_DEV"),
|
||||
context.rect(60, 135, 150, 8),
|
||||
),
|
||||
context.checkbox(
|
||||
"Show errors in console",
|
||||
ids.named_id("ID_SETUP_SHOW_ERRORS_IN_CONSOLE"),
|
||||
context.rect(60, 150, 150, 8),
|
||||
),
|
||||
context.checkbox(
|
||||
"Notify about updates",
|
||||
ids.named_id("ID_SETUP_NOTIFY_ABOUT_UPDATES"),
|
||||
context.rect(60, 165, 150, 8),
|
||||
),
|
||||
ctext(
|
||||
"Comment",
|
||||
ids.named_id("ID_SETUP_COMMENT"),
|
||||
context.rect(25, 185, 200, 25),
|
||||
),
|
||||
ctext(
|
||||
"Tip",
|
||||
ids.named_id("ID_SETUP_TIP_TEXT"),
|
||||
context.rect(25, 210, 200, 25),
|
||||
),
|
||||
ok_button(
|
||||
ids.named_id("ID_SETUP_PANEL_OK"),
|
||||
context.rect(75, 240, 100, 14),
|
||||
),
|
||||
],
|
||||
..context.default_dialog()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user