Skip to content

Commit

Permalink
Add cursor animations
Browse files Browse the repository at this point in the history
Looks pretty nice. No fancy effects like Neovide but the cursor's
movement is animated. Frametimes / framerate is controllable with
:NvuiCursorFrametime {ms}, duration controllable with
:NvuiCursorAnimationDuration {seconds}, and the scaler is controllable
with :NvuiCursorScaler {scaler}. Also added the documentation for these
commands.
This has also alerted me to deficiencies in the move and scroll
animations, particularly the scroll animation, that is caused by the
timer reset.
  • Loading branch information
rohit-px2 committed Aug 28, 2021
1 parent 87c39ea commit f2ed699
Show file tree
Hide file tree
Showing 8 changed files with 175 additions and 5 deletions.
85 changes: 81 additions & 4 deletions src/cursor.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
#include "cursor.hpp"
#include "editor.hpp"
#include "grid.hpp"
#include <fmt/core.h>
#include <fmt/format.h>

using scalers::time_scaler;
time_scaler Cursor::animation_scaler = scalers::oneminusexpo2negative10;

Cursor::Cursor()
: blinkwait_timer(nullptr),
blinkon_timer(nullptr),
Expand All @@ -24,6 +30,35 @@ Cursor::Cursor()
});
}

Cursor::Cursor(EditorArea* ea)
: Cursor()
{
assert(editor_area);
editor_area = ea;
cursor_animation_timer.callOnTimeout([this] {
auto interval = cursor_animation_timer.interval();
cursor_animation_time -= static_cast<float>(interval) / 1000.f;
if (cursor_animation_time <= 0.f)
{
cursor_animation_timer.stop();
cur_x = destination_x;
cur_y = destination_y;
}
else
{
auto x_diff = destination_x - old_x;
auto y_diff = destination_y - old_y;
auto duration = editor_area->cursor_animation_duration();
auto animation_left = cursor_animation_time / duration;
float animation_finished = 1.0f - animation_left;
float scaled = animation_scaler(animation_finished);
cur_x = old_x + (x_diff * scaled);
cur_y = old_y + (y_diff * scaled);
}
editor_area->update();
});
}

void Cursor::mode_change(const msgpack::object* obj, std::uint32_t size)
{
assert(obj->type == msgpack::type::ARRAY);
Expand Down Expand Up @@ -170,13 +205,41 @@ void Cursor::show() noexcept
void Cursor::go_to(CursorPos pos)
{
prev_pos = cur_pos;
cur_pos = pos;
if (!use_animated_position())
{
cursor_animation_timer.stop();
cur_pos = pos;
}
else
{
cur_pos = pos;
old_x = cur_x;
old_y = cur_y;
destination_x = cur_pos->grid_x + cur_pos->col;
destination_y = cur_pos->grid_y + cur_pos->row;
cursor_animation_time = editor_area->cursor_animation_duration();
auto interval = editor_area->cursor_animation_frametime();
if (cursor_animation_timer.interval() != interval)
{
cursor_animation_timer.setInterval(interval);
}
if (!cursor_animation_timer.isActive()) cursor_animation_timer.start();
}
reset_timers();
}

/// Returns a CursorRect containing the cursor rectangle
/// in pixels, based on the row, column, font width, and font height.
static CursorRect get_rect(const ModeInfo& mode, int row, int col, float font_width, float font_height, float caret_extend_top, float caret_extend_bottom, float scale = 1.0f)
static CursorRect get_rect(
const ModeInfo& mode,
float row,
float col,
float font_width,
float font_height,
float caret_extend_top,
float caret_extend_bottom,
float scale = 1.0f
)
{
// These do nothing for now
bool should_draw_text = mode.cursor_shape == CursorShape::Block;
Expand Down Expand Up @@ -214,10 +277,17 @@ static CursorRect get_rect(const ModeInfo& mode, int row, int col, float font_wi
std::optional<CursorRect> Cursor::rect(float font_width, float font_height, float scale) const noexcept
{
if (!cur_pos.has_value()) return std::nullopt;
float x = cur_pos->grid_x + cur_pos->col;
float y = cur_pos->grid_y + cur_pos->row;
if (use_animated_position())
{
x = cur_x;
y = cur_y;
}
return get_rect(
cur_mode,
cur_pos->grid_y + cur_pos->row,
cur_pos->grid_x + cur_pos->col,
y,
x,
font_width, font_height,
caret_extend_top,
caret_extend_bottom,
Expand Down Expand Up @@ -252,3 +322,10 @@ void Cursor::busy_stop()
status = CursorStatus::Visible;
reset_timers();
}

bool Cursor::use_animated_position() const
{
return editor_area
&& editor_area->animations_enabled()
&& editor_area->cursor_animation_frametime() > 0;
}
15 changes: 15 additions & 0 deletions src/cursor.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include <cstdint>
#include <msgpack.hpp>
#include "hlstate.hpp"
#include "grid.hpp"

enum class CursorShape : std::uint8_t
{
Expand Down Expand Up @@ -56,12 +57,15 @@ struct ModeInfo
// (mouse_shape)
};

class EditorArea;
/// The Cursor class stores the data for the Neovim cursor
class Cursor : public QObject
{
Q_OBJECT
public:
static scalers::time_scaler animation_scaler;
Cursor();
Cursor(EditorArea* editor_area);
/**
* Handles a 'mode_info_set' Neovim redraw event.
*/
Expand Down Expand Up @@ -132,6 +136,7 @@ class Cursor : public QObject
caret_extend_bottom = bottom;
}
private:
EditorArea* editor_area = nullptr;
float caret_extend_top = 0.f;
float caret_extend_bottom = 0.f;
int cell_width;
Expand All @@ -150,6 +155,15 @@ class Cursor : public QObject
std::size_t old_mode_idx;
float old_mode_scale = 1.0f;
std::string cur_mode_name;
float cursor_animation_time;
// These x and y are in terms of text, not pixels
float cur_x = 0.f;
float cur_y = 0.f;
float old_x = 0.f;
float old_y = 0.f;
float destination_x = 0.f;
float destination_y = 0.f;
QTimer cursor_animation_timer {};
signals:
void cursor_visible();
void cursor_hidden();
Expand Down Expand Up @@ -187,6 +201,7 @@ class Cursor : public QObject
{
return status == CursorStatus::Busy;
}
bool use_animated_position() const;
};

#endif // NVUI_CURSOR_HPP
2 changes: 1 addition & 1 deletion src/editor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ EditorArea::EditorArea(QWidget* parent, HLState* hl_state, Nvim* nv)
state(hl_state),
nvim(nv),
pixmap(width(), height()),
neovim_cursor(),
neovim_cursor(this),
popup_menu(hl_state, this),
cmdline(hl_state, &neovim_cursor, this)
{
Expand Down
25 changes: 25 additions & 0 deletions src/editor.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,13 @@ class EditorArea : public QWidget
GridBase::scroll_scaler = scalers::scalers().at(scaler);
}
}
void set_cursor_scaler(std::string scaler)
{
if (scalers::scalers().contains(scaler))
{
Cursor::animation_scaler = scalers::scalers().at(scaler);
}
}
void set_scroll_animation_duration(float dur)
{
if (dur > 0.f) scroll_animation_time = dur;
Expand All @@ -329,6 +336,22 @@ class EditorArea : public QWidget
{
popup_menu.info_display().set_cols(columns);
}
float cursor_animation_duration() const
{
return cursor_animation_time;
}
int cursor_animation_frametime() const
{
return cursor_animation_frametime_ms;
}
void set_cursor_frametime(int ms)
{
cursor_animation_frametime_ms = ms;
}
void set_cursor_animation_duration(float secs)
{
cursor_animation_time = secs;
}
/// For input methods
virtual QVariant inputMethodQuery(Qt::InputMethodQuery) const override;
protected:
Expand Down Expand Up @@ -364,6 +387,8 @@ class EditorArea : public QWidget
int animation_frame_interval_ms = 10;
int scroll_animation_frame_interval = 10;
float scroll_animation_time = 0.3f;
float cursor_animation_time = 0.3f;
int cursor_animation_frametime_ms = 10;
/**
* Sets the current font to new_font.
*/
Expand Down
12 changes: 12 additions & 0 deletions src/window.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -583,6 +583,18 @@ void Window::register_handlers()
if (b) enable_frameless_window();
else disable_frameless_window();
}));
listen_for_notification("NVUI_CURSOR_SCALER",
paramify<std::string>([this](std::string scaler) {
editor_area.set_cursor_scaler(scaler);
}));
listen_for_notification("NVUI_CURSOR_ANIMATION_DURATION",
paramify<float>([this](float s) {
editor_area.set_cursor_animation_duration(s);
}));
listen_for_notification("NVUI_CURSOR_FRAMETIME",
paramify<int>([this](int ms) {
editor_area.set_cursor_frametime(ms);
}));
/// Add request handlers
using arr = msgpack::object_array;
handle_request<std::vector<std::string>, std::string>(
Expand Down
34 changes: 34 additions & 0 deletions vim/doc/nvui.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ Titlebar |nvui-titlebar|
Popup |nvui-popup|
Cmdline |nvui-cmdline|
Multigrid |nvui-multigrid|
Cursor |nvui-cursor|
==============================================================================
COMMANDS *nvui-commands*

Expand Down Expand Up @@ -374,4 +375,37 @@ through commands.
To get the corresponding framerate divide 1000 by {msperframe}.

==============================================================================
CURSOR *nvui-cursor*
The cursor is pretty simple. There is an animation for the cursor when it
moves, and some commands to customize the animation.

:NvuiCursorScaler {scaler} *:NvuiCursorScaler*

Changes the animation scaler to {scaler}.
For a description of how the animation scalers work, look at
|:NvuiMoveScaler| and |:NvuiScrollScaler|.
There is a completion list of the available scaler functions, any other
input will do nothing.
Ex. :NvuiCursorScaler identity
Uses the "identity" scaler (t => t) to process the animation. The cursor
travels at a constant speed.

:NvuiCursorAnimationDuration {seconds} *:NvuiCursorAnimationDuration*

Change the animation duration to {seconds}. This is how long the cursor
takes to move from one point to the other.
Ex. :NvuiCursorAnimationDuration 2.0
The cursor takes 2.0 seconds to move from one point to another.

:NvuiCursorFrametime {ms} *:NvuiCursorFrametime*

{ms} must be an integer. If the value of {ms} is less than or equal to 0,
the animation is disabled.
Changes the frametime of the cursor animation.
To get the framerate, divide 1000 by {ms}.
Ex. :NvuiCursorFrametime 10
Sets the animation to update every 10ms (100fps). Of course this is only if
the frames are able to render in the correct amount of time.
:NvuiCursorFrametime -1 will disable the animations.
==============================================================================
vim:ft=help:textwidth=78:ts=2:noet
4 changes: 4 additions & 0 deletions vim/doc/tags
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@
:NvuiCmdPadding nvui.txt /*:NvuiCmdPadding*
:NvuiCmdTopPos nvui.txt /*:NvuiCmdTopPos*
:NvuiCmdWidth nvui.txt /*:NvuiCmdWidth*
:NvuiCursorAnimationDuration nvui.txt /*:NvuiCursorAnimationDuration*
:NvuiCursorFrametime nvui.txt /*:NvuiCursorFrametime*
:NvuiCursorScaler nvui.txt /*:NvuiCursorScaler*
:NvuiFrameless nvui.txt /*:NvuiFrameless*
:NvuiFullscreen nvui.txt /*:NvuiFullscreen*
:NvuiMoveAnimationDuration nvui.txt /*:NvuiMoveAnimationDuration*
Expand Down Expand Up @@ -46,6 +49,7 @@
:NvuiToggleFullscreen nvui.txt /*:NvuiToggleFullscreen*
nvui-cmdline nvui.txt /*nvui-cmdline*
nvui-commands nvui.txt /*nvui-commands*
nvui-cursor nvui.txt /*nvui-cursor*
nvui-general nvui.txt /*nvui-general*
nvui-multigrid nvui.txt /*nvui-multigrid*
nvui-popup nvui.txt /*nvui-popup*
Expand Down
3 changes: 3 additions & 0 deletions vim/plugin/nvui.vim
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ command! -nargs=1 NvuiSnapshotLimit call rpcnotify(1, 'NVUI_SNAPSHOT_LIMIT', <ar
command! -nargs=1 NvuiScrollFrametime call rpcnotify(1, 'NVUI_SCROLL_FRAMETIME', <args>)
command! -nargs=1 -complete=customlist,s:complete_scaler NvuiScrollScaler call NvuiNotify('NVUI_SCROLL_SCALER', <f-args>)
command! -nargs=1 -complete=customlist,s:complete_scaler NvuiMoveScaler call NvuiNotify('NVUI_MOVE_SCALER', <f-args>)
command! -nargs=1 -complete=customlist,s:complete_scaler NvuiCursorScaler call NvuiNotify('NVUI_CURSOR_SCALER', <f-args>)
command! -nargs=1 NvuiMoveAnimationFrametime call rpcnotify(1, 'NVUI_ANIMATION_FRAMETIME', <args>)
command! -nargs=1 NvuiAnimationsEnabled call rpcnotify(1, 'NVUI_ANIMATIONS_ENABLED', <args>)
command! -nargs=1 NvuiMoveAnimationDuration call rpcnotify(1, 'NVUI_MOVE_ANIMATION_DURATION', <args>)
Expand Down Expand Up @@ -58,5 +59,7 @@ command! -nargs=1 NvuiCharspace call rpcnotify(1, 'NVUI_CHARSPACE', <args>)
command! -nargs=1 NvuiFullscreen call rpcnotify(1, 'NVUI_FULLSCREEN', <args>)
command! NvuiToggleFullscreen call rpcnotify(1, 'NVUI_TOGGLE_FULLSCREEN')
command! -nargs=1 NvuiFrameless call rpcnotify(1, 'NVUI_FRAMELESS', <args>)
command! -nargs=1 NvuiCursorAnimationDuration call rpcnotify(1, 'NVUI_CURSOR_ANIMATION_DURATION', <args>)
command! -nargs=1 NvuiCursorFrametime call rpcnotify(1, 'NVUI_CURSOR_FRAMETIME', <args>)
autocmd BufEnter * call rpcnotify(1, 'NVUI_BUFENTER', expand('%:t'))
autocmd DirChanged * call rpcnotify(1, 'NVUI_DIRCHANGED', fnamemodify(getcwd(), ':t'), getcwd())

0 comments on commit f2ed699

Please sign in to comment.