Factor out the placeholder text behavior from SubsEditBox and make it work better
Handle switching from placeholder/normal mode when the value is changed externally (such as from the active line changing) in addition to on focus/blur, and improve behavior when the user sets the text to the placeholder text. Originally committed to SVN as r6321.
This commit is contained in:
parent
1ce9b0d31b
commit
4675dbb29d
4 changed files with 124 additions and 49 deletions
|
@ -633,6 +633,10 @@
|
|||
RelativePath="..\..\src\help_button.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\src\placeholder_ctrl.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\src\scintilla_text_ctrl.cpp"
|
||||
>
|
||||
|
|
104
aegisub/src/placeholder_ctrl.h
Normal file
104
aegisub/src/placeholder_ctrl.h
Normal file
|
@ -0,0 +1,104 @@
|
|||
// Copyright (c) 2012, Thomas Goyne <plorkyeran@aegisub.org>
|
||||
//
|
||||
// Permission to use, copy, modify, and distribute this software for any
|
||||
// purpose with or without fee is hereby granted, provided that the above
|
||||
// copyright notice and this permission notice appear in all copies.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
//
|
||||
// Aegisub Project http://www.aegisub.org/
|
||||
//
|
||||
// $Id$
|
||||
|
||||
/// @file placeholder_ctrl.h
|
||||
/// @ingroup custom_control
|
||||
///
|
||||
|
||||
#ifndef AGI_PRE
|
||||
#include <wx/settings.h>
|
||||
#endif
|
||||
|
||||
/// @class Placeholder
|
||||
/// @brief A wrapper around a control to add placeholder text
|
||||
///
|
||||
/// This control wraps a base control to add default greyed-out placeholder
|
||||
/// text describing the control when the value would otherwise be empty, which
|
||||
/// is removed when the control is focused to begin typing in it, and restored
|
||||
/// when the control loses focus and the value is empty
|
||||
template<class BaseCtrl>
|
||||
class Placeholder : public BaseCtrl {
|
||||
wxString placeholder; ///< Placeholder string
|
||||
bool is_placeholder; ///< Should the value be cleared on focus?
|
||||
|
||||
/// Wrapper around Create to make it possible to override it for specific
|
||||
/// base classes
|
||||
inline void Create(wxWindow *parent, wxSize const& size, long style) {
|
||||
BaseCtrl::Create(parent, -1, placeholder, wxDefaultPosition, size, style);
|
||||
}
|
||||
|
||||
/// Focus gained event handler
|
||||
void OnSetFocus(wxFocusEvent& evt) {
|
||||
evt.Skip();
|
||||
|
||||
if (is_placeholder) {
|
||||
BaseCtrl::ChangeValue("");
|
||||
BaseCtrl::SetForegroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT));
|
||||
}
|
||||
}
|
||||
|
||||
/// Focus lost event handler
|
||||
void OnKillFocus(wxFocusEvent& evt) {
|
||||
evt.Skip();
|
||||
ChangeValue(BaseCtrl::GetValue());
|
||||
}
|
||||
|
||||
public:
|
||||
/// Constructor
|
||||
/// @param parent Parent window
|
||||
/// @param placeholder Placeholder string
|
||||
/// @param size Control size
|
||||
/// @param style Style flags to pass to the base control
|
||||
/// @param tooltip Tooltip string
|
||||
Placeholder(wxWindow *parent, wxString const& placeholder, wxSize const& size, long style, wxString const& tooltip)
|
||||
: placeholder(placeholder)
|
||||
, is_placeholder(true)
|
||||
{
|
||||
Create(parent, size, style);
|
||||
BaseCtrl::SetToolTip(tooltip);
|
||||
BaseCtrl::SetForegroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_GRAYTEXT));
|
||||
|
||||
BaseCtrl::Bind(wxEVT_SET_FOCUS, &Placeholder::OnSetFocus, this);
|
||||
BaseCtrl::Bind(wxEVT_KILL_FOCUS, &Placeholder::OnKillFocus, this);
|
||||
}
|
||||
|
||||
/// @brief Change the value of the control without triggering events
|
||||
/// @param new_value New value of the control
|
||||
///
|
||||
/// If new_value is empty, the control will switch to placeholder mode
|
||||
void ChangeValue(wxString new_value) {
|
||||
if (new_value.empty()) {
|
||||
is_placeholder = true;
|
||||
new_value = placeholder;
|
||||
BaseCtrl::SetForegroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_GRAYTEXT));
|
||||
}
|
||||
else {
|
||||
is_placeholder = false;
|
||||
BaseCtrl::SetForegroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT));
|
||||
}
|
||||
|
||||
// This check should be pointless, but wxGTK is awesome and generates
|
||||
// change events in wxComboBox::ChangeValue
|
||||
if (new_value != BaseCtrl::GetValue())
|
||||
BaseCtrl::ChangeValue(new_value);
|
||||
}
|
||||
};
|
||||
|
||||
template<> inline void Placeholder<wxComboBox>::Create(wxWindow *parent, wxSize const& size, long style) {
|
||||
wxComboBox::Create(parent, -1, "", wxDefaultPosition, size, 0, 0, style);
|
||||
}
|
|
@ -63,6 +63,7 @@
|
|||
#include "include/aegisub/hotkey.h"
|
||||
#include "libresrc/libresrc.h"
|
||||
#include "main.h"
|
||||
#include "placeholder_ctrl.h"
|
||||
#include "subs_edit_ctrl.h"
|
||||
#include "subs_grid.h"
|
||||
#include "timeedit_ctrl.h"
|
||||
|
@ -113,34 +114,6 @@ static T get_value(AssDialogue const& line, int blockn, T initial, wxString tag,
|
|||
return initial;
|
||||
}
|
||||
|
||||
template<class Control>
|
||||
struct FocusHandler : std::unary_function<wxFocusEvent &, void> {
|
||||
wxString value;
|
||||
wxString alt;
|
||||
wxColor color;
|
||||
Control *control;
|
||||
void operator()(wxFocusEvent &event) const {
|
||||
event.Skip();
|
||||
|
||||
if (control->GetValue() == alt) {
|
||||
control->Freeze();
|
||||
control->ChangeValue(value);
|
||||
control->SetForegroundColour(color);
|
||||
control->Thaw();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template<class Event, class T>
|
||||
void bind_focus_handler(T *control, Event event, wxString value, wxString alt, wxColor color) {
|
||||
FocusHandler<T> handler;
|
||||
handler.value = value;
|
||||
handler.alt = alt;
|
||||
handler.color = color;
|
||||
handler.control = control;
|
||||
control->Bind(event, handler);
|
||||
}
|
||||
|
||||
/// Get the block index in the text of the position
|
||||
int block_at_pos(wxString const& text, int pos) {
|
||||
int n = 0;
|
||||
|
@ -182,10 +155,14 @@ SubsEditBox::SubsEditBox(wxWindow *parent, agi::Context *context)
|
|||
TopSizer->Add(CommentBox, 0, wxRIGHT | wxALIGN_CENTER, 5);
|
||||
|
||||
StyleBox = MakeComboBox("Default", wxCB_READONLY, &SubsEditBox::OnStyleChange, _("Style for this line."));
|
||||
ActorBox = MakeComboBox("Actor", wxCB_DROPDOWN, &SubsEditBox::OnActorChange, _("Actor name for this speech. This is only for reference, and is mainly useless."));
|
||||
|
||||
Effect = new wxTextCtrl(this,-1,"",wxDefaultPosition,wxSize(80,-1),wxTE_PROCESS_ENTER);
|
||||
Effect->SetToolTip(_("Effect for this line. This can be used to store extra information for karaoke scripts, or for the effects supported by the renderer."));
|
||||
ActorBox = new Placeholder<wxComboBox>(this, "Actor", wxSize(110, -1), wxCB_DROPDOWN | wxTE_PROCESS_ENTER, _("Actor name for this speech. This is only for reference, and is mainly useless."));
|
||||
Bind(wxEVT_COMMAND_TEXT_UPDATED, &SubsEditBox::OnActorChange, this, ActorBox->GetId());
|
||||
Bind(wxEVT_COMMAND_COMBOBOX_SELECTED, &SubsEditBox::OnActorChange, this, ActorBox->GetId());
|
||||
TopSizer->Add(ActorBox, wxSizerFlags(2).Center().Border(wxRIGHT));
|
||||
|
||||
Effect = new Placeholder<wxTextCtrl>(this, "Effect", wxSize(80,-1), wxTE_PROCESS_ENTER, _("Effect for this line. This can be used to store extra information for karaoke scripts, or for the effects supported by the renderer."));
|
||||
Bind(wxEVT_COMMAND_TEXT_UPDATED, &SubsEditBox::OnEffectChange, this, Effect->GetId());
|
||||
TopSizer->Add(Effect, 3, wxALIGN_CENTER, 5);
|
||||
|
||||
// Middle controls
|
||||
|
@ -243,24 +220,11 @@ SubsEditBox::SubsEditBox(wxWindow *parent, agi::Context *context)
|
|||
|
||||
SetSizerAndFit(MainSizer);
|
||||
|
||||
wxColour text = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT);
|
||||
wxColour grey = wxSystemSettings::GetColour(wxSYS_COLOUR_GRAYTEXT);
|
||||
|
||||
// Setup placeholders for effect and actor boxes
|
||||
bind_focus_handler(Effect, wxEVT_SET_FOCUS, "", "Effect", text);
|
||||
bind_focus_handler(Effect, wxEVT_KILL_FOCUS, "Effect", "", grey);
|
||||
Effect->SetForegroundColour(grey);
|
||||
|
||||
bind_focus_handler(ActorBox, wxEVT_SET_FOCUS, "", "Actor", text);
|
||||
bind_focus_handler(ActorBox, wxEVT_KILL_FOCUS, "Actor", "", grey);
|
||||
ActorBox->SetForegroundColour(grey);
|
||||
|
||||
TextEdit->Bind(wxEVT_STC_MODIFIED, &SubsEditBox::OnChange, this);
|
||||
TextEdit->SetModEventMask(wxSTC_MOD_INSERTTEXT | wxSTC_MOD_DELETETEXT);
|
||||
|
||||
Bind(wxEVT_COMMAND_TEXT_UPDATED, &SubsEditBox::OnLayerEnter, this, Layer->GetId());
|
||||
Bind(wxEVT_COMMAND_SPINCTRL_UPDATED, &SubsEditBox::OnLayerChange, this, Layer->GetId());
|
||||
Bind(wxEVT_COMMAND_TEXT_UPDATED, &SubsEditBox::OnEffectChange, this, Effect->GetId());
|
||||
Bind(wxEVT_COMMAND_CHECKBOX_CLICKED, &SubsEditBox::OnCommentChange, this, CommentBox->GetId());
|
||||
|
||||
Bind(wxEVT_SIZE, &SubsEditBox::OnSize, this);
|
||||
|
@ -361,12 +325,12 @@ void SubsEditBox::OnCommit(int type) {
|
|||
change_value(MarginL, line->GetMarginString(0,false));
|
||||
change_value(MarginR, line->GetMarginString(1,false));
|
||||
change_value(MarginV, line->GetMarginString(2,false));
|
||||
Effect->ChangeValue(line->Effect.empty() ? "Effect" : line->Effect);
|
||||
Effect->ChangeValue(line->Effect);
|
||||
CommentBox->SetValue(line->Comment);
|
||||
StyleBox->Select(StyleBox->FindString(line->Style));
|
||||
|
||||
PopulateActorList();
|
||||
ActorBox->ChangeValue(line->Actor.empty() ? "Actor" : line->Actor);
|
||||
ActorBox->ChangeValue(line->Actor);
|
||||
ActorBox->SetStringSelection(line->Actor);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -45,6 +45,7 @@
|
|||
|
||||
#include "selection_controller.h"
|
||||
|
||||
namespace agi { namespace vfr { class Framerate; } }
|
||||
namespace agi { struct Context; }
|
||||
struct AssColor;
|
||||
class AssDialogue;
|
||||
|
@ -61,7 +62,7 @@ class wxStyledTextCtrl;
|
|||
class wxStyledTextEvent;
|
||||
class wxTextCtrl;
|
||||
|
||||
namespace agi { namespace vfr { class Framerate; } }
|
||||
template<class Base> class Placeholder;
|
||||
|
||||
/// DOCME
|
||||
/// @class SubsEditBox
|
||||
|
@ -92,7 +93,7 @@ class SubsEditBox : public wxPanel, protected SelectionListener<AssDialogue> {
|
|||
// Box controls
|
||||
wxCheckBox *CommentBox;
|
||||
wxComboBox *StyleBox;
|
||||
wxComboBox *ActorBox;
|
||||
Placeholder<wxComboBox> *ActorBox;
|
||||
TimeEdit *StartTime;
|
||||
TimeEdit *EndTime;
|
||||
TimeEdit *Duration;
|
||||
|
@ -100,7 +101,7 @@ class SubsEditBox : public wxPanel, protected SelectionListener<AssDialogue> {
|
|||
wxTextCtrl *MarginL;
|
||||
wxTextCtrl *MarginR;
|
||||
wxTextCtrl *MarginV;
|
||||
wxTextCtrl *Effect;
|
||||
Placeholder<wxTextCtrl> *Effect;
|
||||
wxRadioButton *ByTime;
|
||||
wxRadioButton *ByFrame;
|
||||
|
||||
|
@ -159,6 +160,8 @@ class SubsEditBox : public wxPanel, protected SelectionListener<AssDialogue> {
|
|||
void OnColorButton(AssColor (AssStyle::*field), const char *tag, const char *alt);
|
||||
void OnFontButton();
|
||||
|
||||
void SetPlaceholderCtrl(wxControl *ctrl, wxString const& value);
|
||||
|
||||
/// @brief Set the value of a tag for the currently selected text
|
||||
/// @param tag Tag to set
|
||||
/// @param value New value of tag
|
||||
|
|
Loading…
Reference in a new issue