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