Add native wxSearchCtrl for GTK+ port

This commit is contained in:
AliKet
2019-12-27 16:18:14 +01:00
committed by Vadim Zeitlin
parent a7f31db8cd
commit c09db9c23d
7 changed files with 658 additions and 24 deletions

430
src/gtk/srchctrl.cpp Normal file
View File

@@ -0,0 +1,430 @@
/////////////////////////////////////////////////////////////////////////////
// Name: src/gtk/srchctrl.cpp
// Purpose: wxSearchCtrl implementation - native
// Author: Kettab Ali
// Created: 2019-12-23
// Licence: wxWindows licence
/////////////////////////////////////////////////////////////////////////////
// For compilers that support precompilation, includes "wx.h".
#include "wx/wxprec.h"
#ifdef __BORLANDC__
#pragma hdrstop
#endif
#if wxUSE_SEARCHCTRL
#include "wx/srchctrl.h"
#ifndef WX_PRECOMP
#include "wx/menu.h"
#endif //WX_PRECOMP
#include "wx/utils.h"
#include "wx/gtk/private.h"
#include "wx/gtk/private/gtk3-compat.h"
#if GTK_CHECK_VERSION(3,6,0)
// GtkSearchEntry is available only for GTK+ >= 3.6
#define wxHAS_GTK_SEARCH_ENTRY
#endif // GTK >= 3.6
namespace // anonymous
{
// A more readable way to check for GtkSearchEntry availability.
inline bool HasGtkSearchEntry()
{
#ifdef wxHAS_GTK_SEARCH_ENTRY
return wx_is_at_least_gtk3(6);
#else
return false;
#endif
}
inline GtkWidget* CreateGtkSearchEntryIfAvailable()
{
#ifdef wxHAS_GTK_SEARCH_ENTRY
if ( wx_is_at_least_gtk3(6) )
{
return gtk_search_entry_new();
}
#endif // wxHAS_GTK_SEARCH_ENTRY
// No GtkSearchEntry! fallback to the plain GtkEntry.
return gtk_entry_new();
}
}
// ============================================================================
// signal handlers implementation
// ============================================================================
extern "C" {
static void
wx_gtk_icon_press(GtkEntry* WXUNUSED(entry),
gint position,
GdkEventButton* WXUNUSED(event),
wxSearchCtrl* ctrl)
{
if ( position == GTK_ENTRY_ICON_PRIMARY )
{
// 1- Notice that contrary to the generic version, we don't generate the
// wxEVT_SEARCH event here, which is the native behaviour of the
// GtkSearchEntry (the search icon is inactive for a GtkSearchEntry).
//
// 2- If the wxSearchCtrl has a menu associated with it, we explicitly
// make the search icon clickable as a way to display the menu.
ctrl->PopupSearchMenu();
}
else // position == GTK_ENTRY_ICON_SECONDARY
{
if ( !HasGtkSearchEntry() )
{
// No need to call this for a GtkSearchEntry.
ctrl->Clear();
}
wxCommandEvent event(wxEVT_SEARCH_CANCEL, ctrl->GetId());
event.SetEventObject(ctrl);
ctrl->HandleWindowEvent(event);
}
}
}
// ============================================================================
// wxSearchCtrl implementation
// ============================================================================
wxBEGIN_EVENT_TABLE(wxSearchCtrl, wxSearchCtrlBase)
EVT_CHAR(wxSearchCtrl::OnChar)
EVT_TEXT(wxID_ANY, wxSearchCtrl::OnText)
EVT_TEXT_ENTER(wxID_ANY, wxSearchCtrl::OnTextEnter)
wxEND_EVENT_TABLE()
wxIMPLEMENT_DYNAMIC_CLASS(wxSearchCtrl, wxSearchCtrlBase);
// ----------------------------------------------------------------------------
// creation/destruction
// ----------------------------------------------------------------------------
// destruction
// -----------
wxSearchCtrl::~wxSearchCtrl()
{
#if wxUSE_MENUS
delete m_menu;
#endif // wxUSE_MENUS
}
// creation
// --------
void wxSearchCtrl::Init()
{
m_entry = NULL;
#if wxUSE_MENUS
m_menu = NULL;
#endif // wxUSE_MENUS
m_cancelButtonVisible = false;
}
bool wxSearchCtrl::Create(wxWindow *parent, wxWindowID id,
const wxString& value,
const wxPoint& pos,
const wxSize& size,
long style,
const wxValidator& validator,
const wxString& name)
{
if ( !PreCreation(parent, pos, size) ||
!CreateBase(parent, id, pos, size, style | wxTE_PROCESS_ENTER,
validator, name) )
{
wxFAIL_MSG( "wxSearchCtrl creation failed" );
return false;
}
GTKCreateSearchEntryWidget();
if ( HasFlag(wxBORDER_NONE) )
{
g_object_set (m_widget, "has-frame", FALSE, NULL);
}
GtkEntry * const entry = GetEntry();
// Theoretically m_entry cannot be null, and the test here
// is just for safety reasons.
if ( !entry )
return false;
// Set it up to trigger default item on enter key press
gtk_entry_set_activates_default(entry, !HasFlag(wxTE_PROCESS_ENTER));
gtk_editable_set_editable(GTK_EDITABLE(entry), true);
#ifdef __WXGTK3__
gtk_entry_set_width_chars(entry, 1);
#endif
m_parent->DoAddChild(this);
m_focusWidget = GTK_WIDGET(entry);
PostCreation(size);
gtk_entry_set_text(entry, wxGTK_CONV(value));
SetHint(_("Search"));
GTKConnectChangedSignal();
GTKConnectInsertTextSignal(entry);
GTKConnectClipboardSignals(GTK_WIDGET(entry));
return true;
}
void wxSearchCtrl::GTKCreateSearchEntryWidget()
{
m_widget = CreateGtkSearchEntryIfAvailable();
g_object_ref(m_widget);
m_entry = GTK_ENTRY(m_widget);
if ( !HasGtkSearchEntry() )
{
// Add the search icon and make it looks as native as one would expect
// (i.e. GtkSearchEntry).
gtk_entry_set_icon_from_icon_name(m_entry,
GTK_ENTRY_ICON_PRIMARY,
"edit-find-symbolic");
gtk_entry_set_icon_sensitive(m_entry, GTK_ENTRY_ICON_PRIMARY, FALSE);
gtk_entry_set_icon_activatable(m_entry, GTK_ENTRY_ICON_PRIMARY, FALSE);
}
g_signal_connect(m_entry, "icon-press", G_CALLBACK(wx_gtk_icon_press), this);
}
GtkEditable *wxSearchCtrl::GetEditable() const
{
return GTK_EDITABLE(m_entry);
}
void wxSearchCtrl::Clear()
{
wxTextEntry::Clear();
ShowCancelButton(false);
}
bool wxSearchCtrl::IsModified() const
{
return m_modified;
}
void wxSearchCtrl::MarkDirty()
{
m_modified = true;
}
void wxSearchCtrl::DiscardEdits()
{
m_modified = false;
}
bool wxSearchCtrl::PositionToXY(long pos, long *x, long *y ) const
{
if (pos <= GTKGetEntryTextLength(GetEntry()))
{
if ( y )
*y = 0;
if ( x )
*x = pos;
}
else
{
// index out of bounds
return false;
}
return true;
}
long wxSearchCtrl::XYToPosition(long x, long y ) const
{
if ( y != 0 || x > GTKGetEntryTextLength(GetEntry()) )
return -1;
return x;
}
int wxSearchCtrl::GetLineLength(long lineNo) const
{
const wxString str = GetLineText(lineNo);
return (int) str.length();
}
int wxSearchCtrl::GetNumberOfLines() const
{
return 1;
}
wxString wxSearchCtrl::GetLineText( long lineNo ) const
{
if ( lineNo == 0 )
return GetValue();
return wxString();
}
void wxSearchCtrl::ShowPosition( long pos )
{
gtk_editable_set_position(GetEditable(), pos);
}
// search control specific interfaces
// ----------------------------------
#if wxUSE_MENUS
void wxSearchCtrl::SetMenu( wxMenu* menu )
{
if ( menu == m_menu )
{
// no change
return;
}
delete m_menu;
m_menu = menu;
const bool hasMenu = m_menu != NULL;
gtk_entry_set_icon_sensitive(m_entry, GTK_ENTRY_ICON_PRIMARY, hasMenu);
gtk_entry_set_icon_activatable(m_entry, GTK_ENTRY_ICON_PRIMARY, hasMenu);
}
wxMenu* wxSearchCtrl::GetMenu()
{
return m_menu;
}
#endif // wxUSE_MENUS
void wxSearchCtrl::ShowSearchButton(bool WXUNUSED(show))
{
// Search button is always shown in the native control.
}
bool wxSearchCtrl::IsSearchButtonVisible() const
{
// Search button is always shown in the native control.
return true;
}
void wxSearchCtrl::ShowCancelButton(bool show)
{
// The cancel button is shown/hidden automatically by the GtkSearchEntry.
if ( HasGtkSearchEntry() )
return;
if ( show == IsCancelButtonVisible() )
{
// no change
return;
}
gtk_entry_set_icon_from_icon_name(m_entry,
GTK_ENTRY_ICON_SECONDARY,
show ? "edit-clear-symbolic" : NULL);
m_cancelButtonVisible = show;
}
bool wxSearchCtrl::IsCancelButtonVisible() const
{
if ( HasGtkSearchEntry() )
{
return !IsEmpty();
}
return m_cancelButtonVisible;
}
void wxSearchCtrl::SetDescriptiveText(const wxString& text)
{
wxTextEntry::SetHint(text);
}
wxString wxSearchCtrl::GetDescriptiveText() const
{
return wxTextEntry::GetHint();
}
// Events
// ----------
void wxSearchCtrl::OnChar(wxKeyEvent& key_event)
{
wxCHECK_RET( m_entry != NULL, "invalid search ctrl" );
if ( key_event.GetKeyCode() == WXK_RETURN )
{
if ( HasFlag(wxTE_PROCESS_ENTER) )
{
wxCommandEvent event(wxEVT_TEXT_ENTER, m_windowId);
event.SetEventObject(this);
event.SetString(GetValue());
if ( HandleWindowEvent(event) )
return;
// We disable built-in default button activation when
// wxTE_PROCESS_ENTER is used, but we still should activate it
// if the event wasn't handled, so do it from here.
if ( ClickDefaultButtonIfPossible() )
return;
}
}
key_event.Skip();
}
void wxSearchCtrl::OnText(wxCommandEvent& event)
{
ShowCancelButton(!IsEmpty());
event.Skip();
}
void wxSearchCtrl::OnTextEnter(wxCommandEvent& WXUNUSED(event))
{
if ( !IsEmpty() )
{
wxCommandEvent evt(wxEVT_SEARCH, GetId());
evt.SetEventObject(this);
evt.SetString(GetValue());
ProcessWindowEvent(evt);
}
}
#if wxUSE_MENUS
void wxSearchCtrl::PopupSearchMenu()
{
if ( m_menu )
{
const wxSize size = GetSize();
PopupMenu(m_menu, 0, size.y);
}
}
#endif // wxUSE_MENUS
#endif // wxUSE_SEARCHCTRL