Includes some misc cleanup of haptic double-triggering (or non-triggering), since those cases all end up being TTS event double-reporting, which to me crosses the threshold from "annoying" to "usability issue"custom
parent
ef812a53e5
commit
2ff8eac022
@ -0,0 +1,97 @@ |
||||
/*
|
||||
* Copyright 2024 jacqueline <me@jacqueline.id.au> |
||||
* |
||||
* SPDX-License-Identifier: GPL-3.0-only |
||||
*/ |
||||
|
||||
#include "input/feedback_tts.hpp" |
||||
|
||||
#include <cstdint> |
||||
#include <variant> |
||||
|
||||
#include "lvgl/lvgl.h" |
||||
|
||||
#include "core/lv_event.h" |
||||
#include "core/lv_group.h" |
||||
#include "core/lv_obj.h" |
||||
#include "core/lv_obj_class.h" |
||||
#include "core/lv_obj_tree.h" |
||||
#include "extra/widgets/list/lv_list.h" |
||||
#include "tts/events.hpp" |
||||
#include "widgets/lv_label.h" |
||||
|
||||
#include "tts/events.hpp" |
||||
#include "tts/provider.hpp" |
||||
|
||||
namespace input { |
||||
|
||||
TextToSpeech::TextToSpeech(tts::Provider& tts) |
||||
: tts_(tts), last_obj_(nullptr) {} |
||||
|
||||
auto TextToSpeech::feedback(lv_group_t* group, uint8_t event_type) -> void { |
||||
if (group != last_group_) { |
||||
last_group_ = group; |
||||
last_obj_ = nullptr; |
||||
if (group) { |
||||
tts_.feed(tts::SimpleEvent::kContextChanged); |
||||
} |
||||
} |
||||
|
||||
if (group) { |
||||
lv_obj_t* focused = lv_group_get_focused(group); |
||||
if (focused == last_obj_) { |
||||
return; |
||||
} |
||||
|
||||
last_obj_ = focused; |
||||
if (focused != nullptr) { |
||||
describe(*focused); |
||||
} |
||||
} |
||||
} |
||||
|
||||
auto TextToSpeech::describe(lv_obj_t& obj) -> void { |
||||
if (lv_obj_check_type(&obj, &lv_btn_class) || |
||||
lv_obj_check_type(&obj, &lv_list_btn_class)) { |
||||
auto desc = findDescription(obj); |
||||
tts_.feed(tts::SelectionChanged{ |
||||
.new_selection = |
||||
tts::SelectionChanged::Selection{ |
||||
.description = desc, |
||||
.is_interactive = true, |
||||
}, |
||||
}); |
||||
} else { |
||||
auto desc = findDescription(obj); |
||||
tts_.feed(tts::SelectionChanged{ |
||||
.new_selection = |
||||
tts::SelectionChanged::Selection{ |
||||
.description = desc, |
||||
.is_interactive = false, |
||||
}, |
||||
}); |
||||
} |
||||
} |
||||
|
||||
auto TextToSpeech::findDescription(lv_obj_t& obj) |
||||
-> std::optional<std::string> { |
||||
if (lv_obj_get_child_cnt(&obj) > 0) { |
||||
for (size_t i = 0; i < lv_obj_get_child_cnt(&obj); i++) { |
||||
auto res = findDescription(*lv_obj_get_child(&obj, i)); |
||||
if (res) { |
||||
return res; |
||||
} |
||||
} |
||||
} |
||||
|
||||
if (lv_obj_check_type(&obj, &lv_label_class)) { |
||||
std::string text = lv_label_get_text(&obj); |
||||
if (!text.empty()) { |
||||
return text; |
||||
} |
||||
} |
||||
|
||||
return {}; |
||||
} |
||||
|
||||
} // namespace input
|
@ -0,0 +1,36 @@ |
||||
/*
|
||||
* Copyright 2024 jacqueline <me@jacqueline.id.au> |
||||
* |
||||
* SPDX-License-Identifier: GPL-3.0-only |
||||
*/ |
||||
|
||||
#pragma once |
||||
|
||||
#include <cstdint> |
||||
|
||||
#include "core/lv_obj.h" |
||||
#include "drivers/haptics.hpp" |
||||
|
||||
#include "input/feedback_device.hpp" |
||||
#include "tts/events.hpp" |
||||
#include "tts/provider.hpp" |
||||
|
||||
namespace input { |
||||
|
||||
class TextToSpeech : public IFeedbackDevice { |
||||
public: |
||||
TextToSpeech(tts::Provider&); |
||||
|
||||
auto feedback(lv_group_t*, uint8_t event_type) -> void override; |
||||
|
||||
private: |
||||
tts::Provider& tts_; |
||||
|
||||
auto describe(lv_obj_t&) -> void; |
||||
auto findDescription(lv_obj_t&) -> std::optional<std::string>; |
||||
|
||||
lv_group_t* last_group_; |
||||
lv_obj_t* last_obj_; |
||||
}; |
||||
|
||||
} // namespace input
|
@ -0,0 +1,41 @@ |
||||
/*
|
||||
* Copyright 2024 jacqueline <me@jacqueline.id.au> |
||||
* |
||||
* SPDX-License-Identifier: GPL-3.0-only |
||||
*/ |
||||
|
||||
#pragma once |
||||
|
||||
#include <optional> |
||||
#include <string> |
||||
#include <variant> |
||||
|
||||
namespace tts { |
||||
|
||||
/*
|
||||
* 'Simple' TTS events are events that do not have any extra event-specific |
||||
* details. |
||||
*/ |
||||
enum class SimpleEvent { |
||||
/*
|
||||
* Indicates that the screen's content has substantially changed. e.g. a new |
||||
* screen has been pushed. |
||||
*/ |
||||
kContextChanged, |
||||
}; |
||||
|
||||
/*
|
||||
* Event indicating that the currently selected LVGL object has changed. |
||||
*/ |
||||
struct SelectionChanged { |
||||
struct Selection { |
||||
std::optional<std::string> description; |
||||
bool is_interactive; |
||||
}; |
||||
|
||||
std::optional<Selection> new_selection; |
||||
}; |
||||
|
||||
using Event = std::variant<SimpleEvent, SelectionChanged>; |
||||
|
||||
} // namespace tts
|
@ -0,0 +1,38 @@ |
||||
/*
|
||||
* Copyright 2024 jacqueline <me@jacqueline.id.au> |
||||
* |
||||
* SPDX-License-Identifier: GPL-3.0-only |
||||
*/ |
||||
|
||||
#include "tts/provider.hpp" |
||||
|
||||
#include <optional> |
||||
#include <string> |
||||
#include <variant> |
||||
|
||||
#include "esp_log.h" |
||||
|
||||
#include "tts/events.hpp" |
||||
|
||||
namespace tts { |
||||
|
||||
[[maybe_unused]] static constexpr char kTag[] = "tts"; |
||||
|
||||
Provider::Provider() {} |
||||
|
||||
auto Provider::feed(const Event& e) -> void { |
||||
if (std::holds_alternative<SimpleEvent>(e)) { |
||||
// ESP_LOGI(kTag, "context changed");
|
||||
} else if (std::holds_alternative<SelectionChanged>(e)) { |
||||
auto ev = std::get<SelectionChanged>(e); |
||||
if (!ev.new_selection) { |
||||
// ESP_LOGI(kTag, "no selection");
|
||||
} else { |
||||
// ESP_LOGI(kTag, "new selection: '%s', interactive? %i",
|
||||
// ev.new_selection->description.value_or("").c_str(),
|
||||
// ev.new_selection->is_interactive);
|
||||
} |
||||
} |
||||
} |
||||
|
||||
} // namespace tts
|
@ -0,0 +1,23 @@ |
||||
/*
|
||||
* Copyright 2024 jacqueline <me@jacqueline.id.au> |
||||
* |
||||
* SPDX-License-Identifier: GPL-3.0-only |
||||
*/ |
||||
|
||||
#pragma once |
||||
|
||||
#include <optional> |
||||
#include <string> |
||||
#include <variant> |
||||
|
||||
#include "tts/events.hpp" |
||||
|
||||
namespace tts { |
||||
|
||||
class Provider { |
||||
public: |
||||
Provider(); |
||||
auto feed(const Event&) -> void; |
||||
}; |
||||
|
||||
} // namespace tts
|
Loading…
Reference in new issue