Add scroll arrows to the karaoke syllable bar when the contents are too wide to fit. Closes #1516.

This commit is contained in:
Thomas Goyne 2012-10-01 10:25:48 -07:00
parent b020322a3a
commit 55bdbf8d48
2 changed files with 105 additions and 14 deletions

View File

@ -52,6 +52,7 @@
#include "libresrc/libresrc.h" #include "libresrc/libresrc.h"
#include "main.h" #include "main.h"
#include "selection_controller.h" #include "selection_controller.h"
#include "utils.h"
template<class Container, class Value> template<class Container, class Value>
static inline size_t last_lt_or_eq(Container const& c, Value const& v) { static inline size_t last_lt_or_eq(Container const& c, Value const& v) {
@ -70,11 +71,11 @@ AudioKaraoke::AudioKaraoke(wxWindow *parent, agi::Context *c)
, audio_closed(c->audioController->AddAudioCloseListener(&AudioKaraoke::OnAudioClosed, this)) , audio_closed(c->audioController->AddAudioCloseListener(&AudioKaraoke::OnAudioClosed, this))
, active_line(0) , active_line(0)
, kara(new AssKaraoke) , kara(new AssKaraoke)
, scroll_x(0)
, enabled(false) , enabled(false)
{ {
using std::tr1::bind; using std::tr1::bind;
cancel_button = new wxBitmapButton(this, -1, GETIMAGE(kara_split_cancel_16)); cancel_button = new wxBitmapButton(this, -1, GETIMAGE(kara_split_cancel_16));
cancel_button->SetToolTip(_("Discard all uncommitted splits")); cancel_button->SetToolTip(_("Discard all uncommitted splits"));
cancel_button->Bind(wxEVT_COMMAND_BUTTON_CLICKED, bind(&AudioKaraoke::CancelSplit, this)); cancel_button->Bind(wxEVT_COMMAND_BUTTON_CLICKED, bind(&AudioKaraoke::CancelSplit, this));
@ -102,6 +103,7 @@ AudioKaraoke::AudioKaraoke(wxWindow *parent, agi::Context *c)
split_area->Bind(wxEVT_MOTION, &AudioKaraoke::OnMouse, this); split_area->Bind(wxEVT_MOTION, &AudioKaraoke::OnMouse, this);
split_area->Bind(wxEVT_LEAVE_WINDOW, &AudioKaraoke::OnMouse, this); split_area->Bind(wxEVT_LEAVE_WINDOW, &AudioKaraoke::OnMouse, this);
split_area->Bind(wxEVT_CONTEXT_MENU, &AudioKaraoke::OnContextMenu, this); split_area->Bind(wxEVT_CONTEXT_MENU, &AudioKaraoke::OnContextMenu, this);
scroll_timer.Bind(wxEVT_TIMER, &AudioKaraoke::OnScrollTimer, this);
c->selectionController->AddSelectionListener(this); c->selectionController->AddSelectionListener(this);
@ -141,8 +143,6 @@ void AudioKaraoke::SetEnabled(bool en) {
enabled = en; enabled = en;
c->audioBox->ShowKaraokeBar(enabled); c->audioBox->ShowKaraokeBar(enabled);
split_area->SetSize(GetSize().GetWidth(), -1);
if (enabled) { if (enabled) {
LoadFromLine(); LoadFromLine();
c->audioController->SetTimingController(CreateKaraokeTimingController(c, kara.get(), file_changed)); c->audioController->SetTimingController(CreateKaraokeTimingController(c, kara.get(), file_changed));
@ -166,19 +166,54 @@ void AudioKaraoke::OnPaint(wxPaintEvent &) {
wxMemoryDC bmp_dc(rendered_line); wxMemoryDC bmp_dc(rendered_line);
// Draw the text and split lines // Draw the text and split lines
dc.Blit(0, 0, w, h, &bmp_dc, 0, 0); dc.Blit(-scroll_x, 0, rendered_line.GetWidth(), h, &bmp_dc, 0, 0);
// Draw the split line under the mouse // Draw the split line under the mouse
dc.SetPen(*wxRED); dc.SetPen(*wxRED);
dc.DrawLine(mouse_pos, 0, mouse_pos, h); dc.DrawLine(mouse_pos, 0, mouse_pos, h);
dc.SetPen(*wxTRANSPARENT_PEN);
int width_past_bmp = w + scroll_x - rendered_line.GetWidth();
dc.SetBrush(*wxWHITE_BRUSH);
if (width_past_bmp > 0)
dc.DrawRectangle(w - width_past_bmp, 0, width_past_bmp, h);
// Draw scroll arrows if needed
if (scroll_x > 0) {
dc.DrawRectangle(0, 0, 20, h);
wxPoint triangle[] = {
wxPoint(10, h / 2 - 6),
wxPoint(4, h / 2),
wxPoint(10, h / 2 + 6)
};
dc.SetBrush(*wxBLACK_BRUSH);
dc.DrawPolygon(3, triangle);
}
if (rendered_line.GetWidth() - scroll_x > w) {
dc.SetBrush(*wxWHITE_BRUSH);
dc.DrawRectangle(w - 20, 0, 20, h);
wxPoint triangle[] = {
wxPoint(w - 10, h / 2 - 6),
wxPoint(w - 4, h / 2),
wxPoint(w - 10, h / 2 + 6)
};
dc.SetBrush(*wxBLACK_BRUSH);
dc.DrawPolygon(3, triangle);
}
} }
void AudioKaraoke::RenderText() { void AudioKaraoke::RenderText() {
int w, h; wxSize bmp_size = split_area->GetClientSize();
split_area->GetClientSize(&w, &h); int line_width = spaced_text.size() * char_width + 5;
if (line_width > bmp_size.GetWidth())
bmp_size.SetWidth(line_width);
if (!rendered_line.IsOk() || split_area->GetClientSize() != rendered_line.GetSize()) { if (!rendered_line.IsOk() || bmp_size != rendered_line.GetSize()) {
rendered_line = wxBitmap(w, h); rendered_line = wxBitmap(bmp_size);
} }
wxMemoryDC dc(rendered_line); wxMemoryDC dc(rendered_line);
@ -186,13 +221,13 @@ void AudioKaraoke::RenderText() {
// Draw background // Draw background
dc.SetBrush(wxBrush(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW))); dc.SetBrush(wxBrush(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW)));
dc.SetPen(*wxTRANSPARENT_PEN); dc.SetPen(*wxTRANSPARENT_PEN);
dc.DrawRectangle(0, 0, w, h); dc.DrawRectangle(wxPoint(), bmp_size);
dc.SetFont(split_font); dc.SetFont(split_font);
dc.SetTextForeground(wxColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT))); dc.SetTextForeground(wxColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT)));
// Draw each character in the line // Draw each character in the line
int y = (h - char_height) / 2; int y = (bmp_size.GetHeight() - char_height) / 2;
for (size_t i = 0; i < spaced_text.size(); ++i) { for (size_t i = 0; i < spaced_text.size(); ++i) {
dc.DrawText(spaced_text[i], char_x[i], y); dc.DrawText(spaced_text[i], char_x[i], y);
} }
@ -200,7 +235,7 @@ void AudioKaraoke::RenderText() {
// Draw the lines between each syllable // Draw the lines between each syllable
dc.SetPen(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT)); dc.SetPen(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT));
for (size_t i = 0; i < syl_lines.size(); ++i) { for (size_t i = 0; i < syl_lines.size(); ++i) {
dc.DrawLine(syl_lines[i], 0, syl_lines[i], h); dc.DrawLine(syl_lines[i], 0, syl_lines[i], bmp_size.GetHeight());
} }
} }
@ -228,8 +263,45 @@ void AudioKaraoke::OnMouse(wxMouseEvent &event) {
mouse_pos = event.GetX(); mouse_pos = event.GetX();
if (event.Leaving()) if (event.Leaving()) {
mouse_pos = -1; mouse_pos = -1;
split_area->Refresh(false);
return;
}
if (scroll_timer.IsRunning() || split_area->HasCapture()) {
if (event.LeftUp()) {
scroll_timer.Stop();
split_area->ReleaseMouse();
}
return;
}
// Check if the mouse is over a scroll arrow
int client_width = split_area->GetClientSize().GetWidth();
if (scroll_x > 0 && mouse_pos < 20) {
scroll_dir = -1;
}
else if (scroll_x + client_width < rendered_line.GetWidth() && mouse_pos > client_width - 20) {
scroll_dir = 1;
}
else {
scroll_dir = 0;
}
if (scroll_dir) {
mouse_pos = -1;
if (event.LeftDown()) {
split_area->Refresh(false);
scroll_timer.Start(50);
split_area->CaptureMouse();
wxTimerEvent evt;
OnScrollTimer(evt);
}
return;
}
int shifted_pos = mouse_pos + scroll_x;
if (!event.LeftDown()) { if (!event.LeftDown()) {
// Erase the old line and draw the new one // Erase the old line and draw the new one
@ -238,14 +310,14 @@ void AudioKaraoke::OnMouse(wxMouseEvent &event) {
} }
// Character to insert the new split point before // Character to insert the new split point before
int split_pos = std::min<int>((mouse_pos - char_width / 2) / char_width, spaced_text.size()); int split_pos = std::min<int>((shifted_pos - char_width / 2) / char_width, spaced_text.size());
// Syllable this character is in // Syllable this character is in
int syl = last_lt_or_eq(syl_start_points, split_pos); int syl = last_lt_or_eq(syl_start_points, split_pos);
// If the click is sufficiently close to a line of a syllable split, // If the click is sufficiently close to a line of a syllable split,
// remove that split rather than adding a new one // remove that split rather than adding a new one
if ((syl > 0 && mouse_pos <= syl_lines[syl - 1] + 2) || (syl < (int)syl_lines.size() && mouse_pos >= syl_lines[syl] - 2)) { if ((syl > 0 && shifted_pos <= syl_lines[syl - 1] + 2) || (syl < (int)syl_lines.size() && shifted_pos >= syl_lines[syl] - 2)) {
kara->RemoveSplit(syl); kara->RemoveSplit(syl);
} }
else { else {
@ -258,7 +330,21 @@ void AudioKaraoke::OnMouse(wxMouseEvent &event) {
split_area->Refresh(false); split_area->Refresh(false);
} }
void AudioKaraoke::OnScrollTimer(wxTimerEvent &) {
scroll_x += scroll_dir * char_width * 3;
int max_scroll = rendered_line.GetWidth() + 20 - split_area->GetClientSize().GetWidth();
if (scroll_x < 0 || scroll_x > max_scroll) {
scroll_x = mid(0, scroll_x, max_scroll);
scroll_timer.Stop();
}
split_area->Refresh(false);
}
void AudioKaraoke::LoadFromLine() { void AudioKaraoke::LoadFromLine() {
scroll_x = 0;
scroll_timer.Stop();
kara->SetLine(active_line, true); kara->SetLine(active_line, true);
SetDisplayText(); SetDisplayText();
accept_button->Enable(kara->GetText() != active_line->Text); accept_button->Enable(kara->GetText() != active_line->Text);

View File

@ -97,6 +97,10 @@ class AudioKaraoke : public wxWindow, private SelectionListener<AssDialogue> {
/// Left x coordinate of each character in spaced_text in pixels /// Left x coordinate of each character in spaced_text in pixels
std::vector<int> char_x; std::vector<int> char_x;
int scroll_x;
int scroll_dir;
wxTimer scroll_timer;
int char_height; ///< Maximum character height in pixels int char_height; ///< Maximum character height in pixels
int char_width; ///< Maximum character width in pixels int char_width; ///< Maximum character width in pixels
int mouse_pos; ///< Last x coordinate of the mouse int mouse_pos; ///< Last x coordinate of the mouse
@ -142,6 +146,7 @@ class AudioKaraoke : public wxWindow, private SelectionListener<AssDialogue> {
void OnSelectedSetChanged(Selection const&, Selection const&) { } void OnSelectedSetChanged(Selection const&, Selection const&) { }
void OnAudioOpened(); void OnAudioOpened();
void OnAudioClosed(); void OnAudioClosed();
void OnScrollTimer(wxTimerEvent &event);
public: public:
/// Constructor /// Constructor