Example

This example shows the various helper types that provide simple definitions matching the supported port types saw before.

#pragma once
#include <avnd/concepts/audio_port.hpp>
#include <avnd/concepts/parameter.hpp>
#include <avnd/common/for_nth.hpp>
#include <boost/pfr.hpp>
#include <cmath>
#include <halp/audio.hpp>
#include <halp/controls.hpp>
#include <halp/controls_fmt.hpp>
#include <ossia/network/value/format_value.hpp>

#include <halp/meta.hpp>
#include <halp/sample_accurate_controls.hpp>

#if __has_include(<magic_enum.hpp>)
#include <magic_enum.hpp>
#endif
namespace examples
{

struct ControlGallery
{
  halp_meta(name, "Control gallery");
  halp_meta(c_name, "control_gallery");
  halp_meta(category, "Demo");
  halp_meta(author, "<AUTHOR>");
  halp_meta(description, "<DESCRIPTION>");
  halp_meta(uuid, "a9b0e2c6-61e9-45df-a75d-27abf7fb43d7");

  struct
  {
    //! Buttons are level-triggers: true as long as the button is pressed
    halp::accurate<halp::maintained_button<"Press me ! (Button)">> button;

    //! In contrast, impulses are edge-triggers: there is only a value at the moment of the click.
    halp::accurate<halp::impulse_button<"Press me ! (Impulse)">> impulse_button;

    //! Common widgets
    halp::accurate<halp::hslider_f32<"Float slider", halp::range{0., 1., 0.5}>>
        float_slider;
    halp::accurate<halp::knob_f32<"Float knob", halp::range{0., 1., 0.5}>> float_knob;
    //// // FIXME
    //// struct {
    ////   // FIXME meta_control(Control::LogFloatSlider, "Float slider (log)", 0., 1., 0.5);
    ////   ossia::timed_vec<float> values{};
    //// } log_float_slider;
    ////

#if defined(__clang__) || defined(_MSC_VER)
    // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=104720
    halp::accurate<halp::hslider_i32<"Int slider", halp::range{0., 1000., 10.}>>
        int_slider;
    halp::accurate<halp::spinbox_i32<"Int spinbox", halp::range{0, 1000, 10}>>
        int_spinbox;
#endif

    //! Will look like a checkbox
    halp::accurate<halp::toggle<"Toggle", halp::toggle_setup{.init = true}>> toggle;

    //! Same, but allows to choose what is displayed.
    // FIXME halp::accurate<halp::chooser_toggle<"Toggle", {"Falsey", "Truey"}, false>> chooser_toggle;

    //! Allows to edit some text.
    halp::accurate<halp::lineedit<"Line edit", "Henlo">> lineedit;

    //! First member of the pair is the text, second is the value.
    //! Defining comboboxes and enumerations is a tiny bit more complicated
    struct : halp::sample_accurate_values<halp::combo_pair<float>>
    {
      halp_meta(name, "Combo box");
      enum widget
      {
        combobox
      };

      struct range
      {
        halp::combo_pair<float> values[3]{{"Foo", -10.f}, {"Bar", 5.f}, {"Baz", 10.f}};
        int init{1}; // Bar
      };

      float value{};
    } combobox;

    //! Here value will be the string
    struct : halp::sample_accurate_values<std::string_view>
    {
      halp_meta(name, "Enum 2");
      enum widget
      {
        enumeration
      };

      struct range
      {
        std::string_view values[4]{"Roses", "Red", "Violets", "Blue"};
        int init{1}; // Red
      };

      // FIXME: string_view: allow outside bounds
      std::string_view value;
    } enumeration_a;

    //! Here value will be the index of the string... but even better than that
    //! is below:
    struct : halp::sample_accurate_values<int>
    {
      halp_meta(name, "Enum 3");
      enum widget
      {
        enumeration
      };

      struct range
      {
        std::string_view values[4]{"Roses 2", "Red 2", "Violets 2", "Blue 2"};
        int init{1}; // Red
      };

      int value{};
    } enumeration_b;

    /// // FIXME
    /// //! Same as Enum but won't reject strings that are not part of the list.
    /// struct {
    ///   static const constexpr std::array<const char*, 3> choices() {
    ///     return {"Square", "Sine", "Triangle"};
    ///   };
    ///   // FIXME meta_control(Control::UnvalidatedEnum, "Unchecked enum", 1, choices());
    ///   ossia::timed_vec<std::string> values{};
    /// } unvalidated_enumeration;

    //! It's also possible to use this which will define an enum type and
    //! map to it automatically.
    //! e.g. in source one can then do:
    //!
    //!   auto& param = inputs.simpler_enumeration;
    //!   using enum_type = decltype(param)::enum_type;
    //!   switch(param.value) {
    //!      case enum_type::Square:
    //!        ...
    //!   }
    //!
    //! OSC messages can use either the int index or the string.
    struct enum_t
    {
      halp__enum("Simple Enum", Peg, Square, Peg, Round, Hole)
    };
    halp::accurate<enum_t> simpler_enumeration;

    struct combobox_t
    {
      halp__enum_combobox("Color", Blue, Red, Green, Teal, Blue, Black, Orange)
    };
    halp::accurate<combobox_t> simpler_enumeration_in_a_combobox;

    //! Crosshair XY chooser
    halp::accurate<halp::xy_pad_f32<"XY", halp::range{-5.f, 5.f, 0.f}>> position;

    //! Color chooser. Colors are in 8-bit RGBA by default.
    halp::accurate<halp::color_chooser<"Color">> color;

  } inputs;

  void operator()()
  {
    const bool has_impulse = !inputs.impulse_button.values.empty();
    const bool has_button = std::any_of(
        inputs.button.values.begin(), inputs.button.values.end(),
        [](const auto& p) { return p.second == true; });

    if(!has_impulse && !has_button)
      return;

    avnd::for_each_field_ref(inputs, []<typename Control>(const Control& input) {
      {
        auto val = input.values.begin()->second;
        if constexpr(std::is_enum_v<decltype(val)>) {
#if __has_include(<magic_enum.hpp>)
          fmt::print("changed: {} {}", Control::name(), magic_enum::enum_name(val));
#else
          fmt::print("changed: {} {}", Control::name(), static_cast<std::underlying_type_t<decltype(val)>>(val));
#endif
        } else {
          fmt::print("changed: {} {}", Control::name(), val);
        }
      }
    });
  }
};

}