mirror of
https://github.com/slint-ui/slint.git
synced 2025-10-01 14:21:16 +00:00
Complete the C++ Timer API
With this patch it matches the Rust API, with start(), restart(), running() and a default constructor.
This commit is contained in:
parent
3d64be7fb6
commit
f557a27556
3 changed files with 134 additions and 9 deletions
|
@ -337,24 +337,67 @@ private:
|
||||||
private_api::WindowRc inner;
|
private_api::WindowRc inner;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#if !defined(DOXYGEN)
|
||||||
|
using cbindgen_private::TimerMode;
|
||||||
|
#else
|
||||||
|
/// The TimerMode specifies what should happen after the timer fired.
|
||||||
|
///
|
||||||
|
/// Used by the sixtyfps::Timer::start() function.
|
||||||
|
enum class TimerMode {
|
||||||
|
/// A SingleShot timer is fired only once.
|
||||||
|
SingleShot,
|
||||||
|
/// A Repeated timer is fired repeatedly until it is stopped or dropped.
|
||||||
|
Repeated,
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
/// A Timer that can call a callback at repeated interval
|
/// A Timer that can call a callback at repeated interval
|
||||||
///
|
///
|
||||||
/// Use the static single_shot function to make a single shot timer
|
/// Use the static single_shot function to make a single shot timer
|
||||||
struct Timer
|
struct Timer
|
||||||
{
|
{
|
||||||
|
/// Construct a null timer. Use the start() method to activate the timer with a mode, interval
|
||||||
|
/// and callback.
|
||||||
|
Timer() : id(-1) { }
|
||||||
/// Construct a timer which will repeat the callback every `interval` milliseconds until
|
/// Construct a timer which will repeat the callback every `interval` milliseconds until
|
||||||
/// the destructor of the timer is called.
|
/// the destructor of the timer is called.
|
||||||
|
///
|
||||||
|
/// This is a convenience function and equivalent to calling
|
||||||
|
/// `start(sixtyfps::TimerMode::Repeated, interval, callback);` on a default constructed Timer.
|
||||||
template<typename F>
|
template<typename F>
|
||||||
Timer(std::chrono::milliseconds interval, F callback)
|
Timer(std::chrono::milliseconds interval, F callback)
|
||||||
: id(cbindgen_private::sixtyfps_timer_start(
|
: id(cbindgen_private::sixtyfps_timer_start(
|
||||||
interval.count(), [](void *data) { (*reinterpret_cast<F *>(data))(); },
|
-1, cbindgen_private::TimerMode::Repeated, interval.count(),
|
||||||
new F(std::move(callback)), [](void *data) { delete reinterpret_cast<F *>(data); }))
|
[](void *data) { (*reinterpret_cast<F *>(data))(); }, new F(std::move(callback)),
|
||||||
|
[](void *data) { delete reinterpret_cast<F *>(data); }))
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
Timer(const Timer &) = delete;
|
Timer(const Timer &) = delete;
|
||||||
Timer &operator=(const Timer &) = delete;
|
Timer &operator=(const Timer &) = delete;
|
||||||
~Timer() { cbindgen_private::sixtyfps_timer_stop(id); }
|
~Timer() { cbindgen_private::sixtyfps_timer_stop(id); }
|
||||||
|
|
||||||
|
/// Starts the timer with the given \a mode and \a interval, in order for the \a callback to
|
||||||
|
/// called when the timer fires. If the timer has been started previously and not fired yet,
|
||||||
|
/// then it will be restarted.
|
||||||
|
template<typename F>
|
||||||
|
void start(TimerMode mode, std::chrono::milliseconds interval, F callback)
|
||||||
|
{
|
||||||
|
id = cbindgen_private::sixtyfps_timer_start(
|
||||||
|
id, mode, interval.count(), [](void *data) { (*reinterpret_cast<F *>(data))(); },
|
||||||
|
new F(std::move(callback)), [](void *data) { delete reinterpret_cast<F *>(data); });
|
||||||
|
}
|
||||||
|
/// Stops the previously started timer. Does nothing if the timer has never been started. A
|
||||||
|
/// stopped timer cannot be restarted with restart() -- instead you need to call start().
|
||||||
|
void stop()
|
||||||
|
{
|
||||||
|
cbindgen_private::sixtyfps_timer_stop(id);
|
||||||
|
id = -1;
|
||||||
|
}
|
||||||
|
/// Restarts the timer, if it was previously started.
|
||||||
|
void restart() { cbindgen_private::sixtyfps_timer_restart(id); }
|
||||||
|
/// Returns true if the timer is running; false otherwise.
|
||||||
|
bool running() const { return cbindgen_private::sixtyfps_timer_running(id); }
|
||||||
|
|
||||||
/// Call the callback after the given duration.
|
/// Call the callback after the given duration.
|
||||||
template<typename F>
|
template<typename F>
|
||||||
static void single_shot(std::chrono::milliseconds duration, F callback)
|
static void single_shot(std::chrono::milliseconds duration, F callback)
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
#include <sixtyfps.h>
|
#include <sixtyfps.h>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
|
||||||
TEST_CASE("C++ Timers")
|
TEST_CASE("C++ Singleshot Timers")
|
||||||
{
|
{
|
||||||
using namespace sixtyfps;
|
using namespace sixtyfps;
|
||||||
int called = 0;
|
int called = 0;
|
||||||
|
@ -20,6 +20,62 @@ TEST_CASE("C++ Timers")
|
||||||
REQUIRE(called == 10);
|
REQUIRE(called == 10);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE("C++ Repeated Timer")
|
||||||
|
{
|
||||||
|
int timer_triggered = 0;
|
||||||
|
sixtyfps::Timer timer;
|
||||||
|
|
||||||
|
timer.start(sixtyfps::TimerMode::Repeated, std::chrono::milliseconds(30),
|
||||||
|
[&]() { timer_triggered++; });
|
||||||
|
|
||||||
|
REQUIRE(timer_triggered == 0);
|
||||||
|
|
||||||
|
bool timer_was_running = false;
|
||||||
|
|
||||||
|
sixtyfps::Timer::single_shot(std::chrono::milliseconds(500), [&]() {
|
||||||
|
timer_was_running = timer.running();
|
||||||
|
sixtyfps::quit_event_loop();
|
||||||
|
});
|
||||||
|
|
||||||
|
sixtyfps::run_event_loop();
|
||||||
|
|
||||||
|
REQUIRE(timer_triggered > 1);
|
||||||
|
REQUIRE(timer_was_running);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("C++ Restart Singleshot Timer")
|
||||||
|
{
|
||||||
|
int timer_triggered = 0;
|
||||||
|
sixtyfps::Timer timer;
|
||||||
|
|
||||||
|
timer.start(sixtyfps::TimerMode::SingleShot, std::chrono::milliseconds(30),
|
||||||
|
[&]() { timer_triggered++; });
|
||||||
|
|
||||||
|
REQUIRE(timer_triggered == 0);
|
||||||
|
|
||||||
|
bool timer_was_running = false;
|
||||||
|
|
||||||
|
sixtyfps::Timer::single_shot(std::chrono::milliseconds(500), [&]() {
|
||||||
|
timer_was_running = timer.running();
|
||||||
|
sixtyfps::quit_event_loop();
|
||||||
|
});
|
||||||
|
|
||||||
|
sixtyfps::run_event_loop();
|
||||||
|
|
||||||
|
REQUIRE(timer_triggered == 1);
|
||||||
|
REQUIRE(timer_was_running);
|
||||||
|
timer_triggered = 0;
|
||||||
|
timer.restart();
|
||||||
|
sixtyfps::Timer::single_shot(std::chrono::milliseconds(500), [&]() {
|
||||||
|
timer_was_running = timer.running();
|
||||||
|
sixtyfps::quit_event_loop();
|
||||||
|
});
|
||||||
|
|
||||||
|
sixtyfps::run_event_loop();
|
||||||
|
|
||||||
|
REQUIRE(timer_triggered == 1);
|
||||||
|
REQUIRE(timer_was_running);
|
||||||
|
}
|
||||||
|
|
||||||
TEST_CASE("Quit from event")
|
TEST_CASE("Quit from event")
|
||||||
{
|
{
|
||||||
|
@ -33,7 +89,6 @@ TEST_CASE("Quit from event")
|
||||||
REQUIRE(called == 10);
|
REQUIRE(called == 10);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
TEST_CASE("Event from thread")
|
TEST_CASE("Event from thread")
|
||||||
{
|
{
|
||||||
std::atomic<int> called = 0;
|
std::atomic<int> called = 0;
|
||||||
|
@ -50,15 +105,13 @@ TEST_CASE("Event from thread")
|
||||||
t.join();
|
t.join();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
TEST_CASE("Blocking Event from thread")
|
TEST_CASE("Blocking Event from thread")
|
||||||
{
|
{
|
||||||
std::atomic<int> called = 0;
|
std::atomic<int> called = 0;
|
||||||
auto t = std::thread([&] {
|
auto t = std::thread([&] {
|
||||||
// test returning a, unique_ptr because it is movable-only
|
// test returning a, unique_ptr because it is movable-only
|
||||||
std::unique_ptr foo = sixtyfps::blocking_invoke_from_event_loop([&] {
|
std::unique_ptr foo = sixtyfps::blocking_invoke_from_event_loop(
|
||||||
return std::make_unique<int>(42);
|
[&] { return std::make_unique<int>(42); });
|
||||||
});
|
|
||||||
called = *foo;
|
called = *foo;
|
||||||
int xxx = 123;
|
int xxx = 123;
|
||||||
sixtyfps::blocking_invoke_from_event_loop([&] {
|
sixtyfps::blocking_invoke_from_event_loop([&] {
|
||||||
|
|
|
@ -21,6 +21,7 @@ type SingleShotTimerCallback = Box<dyn FnOnce()>;
|
||||||
///
|
///
|
||||||
/// Used by the [`Timer::start`] function.
|
/// Used by the [`Timer::start`] function.
|
||||||
#[derive(Copy, Clone)]
|
#[derive(Copy, Clone)]
|
||||||
|
#[repr(C)]
|
||||||
pub enum TimerMode {
|
pub enum TimerMode {
|
||||||
/// A SingleShot timer is fired only once.
|
/// A SingleShot timer is fired only once.
|
||||||
SingleShot,
|
SingleShot,
|
||||||
|
@ -360,6 +361,8 @@ pub(crate) mod ffi {
|
||||||
/// The timer MUST be stopped with sixtyfps_timer_stop
|
/// The timer MUST be stopped with sixtyfps_timer_stop
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern "C" fn sixtyfps_timer_start(
|
pub extern "C" fn sixtyfps_timer_start(
|
||||||
|
id: i64,
|
||||||
|
mode: TimerMode,
|
||||||
duration: u64,
|
duration: u64,
|
||||||
callback: extern "C" fn(*mut c_void),
|
callback: extern "C" fn(*mut c_void),
|
||||||
user_data: *mut c_void,
|
user_data: *mut c_void,
|
||||||
|
@ -367,7 +370,10 @@ pub(crate) mod ffi {
|
||||||
) -> i64 {
|
) -> i64 {
|
||||||
let wrap = WrapFn { callback, user_data, drop_user_data };
|
let wrap = WrapFn { callback, user_data, drop_user_data };
|
||||||
let timer = Timer::default();
|
let timer = Timer::default();
|
||||||
timer.start(TimerMode::Repeated, core::time::Duration::from_millis(duration), move || {
|
if id != -1 {
|
||||||
|
timer.id.set(Some(id as _));
|
||||||
|
}
|
||||||
|
timer.start(mode, core::time::Duration::from_millis(duration), move || {
|
||||||
(wrap.callback)(wrap.user_data)
|
(wrap.callback)(wrap.user_data)
|
||||||
});
|
});
|
||||||
timer.id.take().map(|x| x as i64).unwrap_or(-1)
|
timer.id.take().map(|x| x as i64).unwrap_or(-1)
|
||||||
|
@ -396,4 +402,27 @@ pub(crate) mod ffi {
|
||||||
let timer = Timer { id: Cell::new(Some(id as _)) };
|
let timer = Timer { id: Cell::new(Some(id as _)) };
|
||||||
timer.stop()
|
timer.stop()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Restart a repeated timer
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn sixtyfps_timer_restart(id: i64) {
|
||||||
|
if id == -1 {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let timer = Timer { id: Cell::new(Some(id as _)) };
|
||||||
|
timer.restart();
|
||||||
|
timer.id.take(); // Make sure that dropping the Timer doesn't unregister it. C++ will call stop() in the destructor.
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns true if the timer is running; false otherwise.
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn sixtyfps_timer_running(id: i64) -> bool {
|
||||||
|
if id == -1 {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
let timer = Timer { id: Cell::new(Some(id as _)) };
|
||||||
|
let running = timer.running();
|
||||||
|
timer.id.take(); // Make sure that dropping the Timer doesn't unregister it. C++ will call stop() in the destructor.
|
||||||
|
running
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue