From 4675dbb29dce871a96b89a6b297956eea4939e31 Mon Sep 17 00:00:00 2001 From: Thomas Goyne Date: Fri, 20 Jan 2012 05:14:50 +0000 Subject: [PATCH] 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. --- .../aegisub_vs2008/aegisub_vs2008.vcproj | 4 + aegisub/src/placeholder_ctrl.h | 104 ++++++++++++++++++ aegisub/src/subs_edit_box.cpp | 56 ++-------- aegisub/src/subs_edit_box.h | 9 +- 4 files changed, 124 insertions(+), 49 deletions(-) create mode 100644 aegisub/src/placeholder_ctrl.h diff --git a/aegisub/build/aegisub_vs2008/aegisub_vs2008.vcproj b/aegisub/build/aegisub_vs2008/aegisub_vs2008.vcproj index 9991bd29c..f8bfacfa2 100644 --- a/aegisub/build/aegisub_vs2008/aegisub_vs2008.vcproj +++ b/aegisub/build/aegisub_vs2008/aegisub_vs2008.vcproj @@ -633,6 +633,10 @@ RelativePath="..\..\src\help_button.h" > + + diff --git a/aegisub/src/placeholder_ctrl.h b/aegisub/src/placeholder_ctrl.h new file mode 100644 index 000000000..0b98ef6ca --- /dev/null +++ b/aegisub/src/placeholder_ctrl.h @@ -0,0 +1,104 @@ +// Copyright (c) 2012, Thomas Goyne +// +// 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 +#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 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::Create(wxWindow *parent, wxSize const& size, long style) { + wxComboBox::Create(parent, -1, "", wxDefaultPosition, size, 0, 0, style); +} diff --git a/aegisub/src/subs_edit_box.cpp b/aegisub/src/subs_edit_box.cpp index 95cde3922..1e2024962 100644 --- a/aegisub/src/subs_edit_box.cpp +++ b/aegisub/src/subs_edit_box.cpp @@ -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 -struct FocusHandler : std::unary_function { - 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 -void bind_focus_handler(T *control, Event event, wxString value, wxString alt, wxColor color) { - FocusHandler 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(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(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); } } diff --git a/aegisub/src/subs_edit_box.h b/aegisub/src/subs_edit_box.h index 1cfddfea3..e53d74e57 100644 --- a/aegisub/src/subs_edit_box.h +++ b/aegisub/src/subs_edit_box.h @@ -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 Placeholder; /// DOCME /// @class SubsEditBox @@ -92,7 +93,7 @@ class SubsEditBox : public wxPanel, protected SelectionListener { // Box controls wxCheckBox *CommentBox; wxComboBox *StyleBox; - wxComboBox *ActorBox; + Placeholder *ActorBox; TimeEdit *StartTime; TimeEdit *EndTime; TimeEdit *Duration; @@ -100,7 +101,7 @@ class SubsEditBox : public wxPanel, protected SelectionListener { wxTextCtrl *MarginL; wxTextCtrl *MarginR; wxTextCtrl *MarginV; - wxTextCtrl *Effect; + Placeholder *Effect; wxRadioButton *ByTime; wxRadioButton *ByFrame; @@ -159,6 +160,8 @@ class SubsEditBox : public wxPanel, protected SelectionListener { 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