wxwidgets/src/gtk/infobar.cpp
Vadim Zeitlin 3e27a53e95 Make wxInfoBar actually appear when using GTK+ 3
Due to https://bugzilla.gnome.org/show_bug.cgi?id=710888 GtkInfoBar
wasn't shown if it had been hidden before -- which is exactly what
wxInfoBar did.

Fix this using the workaround proposed in the bug report, which makes
the info bar appear immediately, without any transition, which might be
not ideal, but markedly better than failing to show it at all.
2018-07-15 20:52:10 +02:00

348 lines
9.4 KiB
C++

///////////////////////////////////////////////////////////////////////////////
// Name: src/gtk/infobar.cpp
// Purpose: wxInfoBar implementation for GTK
// Author: Vadim Zeitlin
// Created: 2009-09-27
// Copyright: (c) 2009 Vadim Zeitlin <vadim@wxwidgets.org>
// Licence: wxWindows licence
///////////////////////////////////////////////////////////////////////////////
// ============================================================================
// declarations
// ============================================================================
// ----------------------------------------------------------------------------
// headers
// ----------------------------------------------------------------------------
// for compilers that support precompilation, includes "wx.h".
#include "wx/wxprec.h"
#ifdef __BORLANDC__
#pragma hdrstop
#endif
#include "wx/infobar.h"
#if wxUSE_INFOBAR && defined(wxHAS_NATIVE_INFOBAR)
#ifndef WX_PRECOMP
#endif // WX_PRECOMP
#include "wx/vector.h"
#include "wx/stockitem.h"
#include "wx/gtk/private.h"
#include "wx/gtk/private/messagetype.h"
#include "wx/gtk/private/mnemonics.h"
// ----------------------------------------------------------------------------
// local classes
// ----------------------------------------------------------------------------
class wxInfoBarGTKImpl
{
public:
wxInfoBarGTKImpl()
{
m_label = NULL;
m_close = NULL;
}
// label for the text shown in the bar
GtkWidget *m_label;
// the default close button, NULL if not needed (m_buttons is not empty) or
// not created yet
GtkWidget *m_close;
// information about the buttons added using AddButton()
struct Button
{
Button(GtkWidget *button_, int id_)
: button(button_),
id(id_)
{
}
GtkWidget *button;
int id;
};
typedef wxVector<Button> Buttons;
Buttons m_buttons;
};
// ----------------------------------------------------------------------------
// local functions
// ----------------------------------------------------------------------------
namespace
{
inline bool UseNative()
{
// native GtkInfoBar widget is only available in GTK+ 2.18 and later
return wx_is_at_least_gtk2(18);
}
} // anonymous namespace
extern "C"
{
static void wxgtk_infobar_response(GtkInfoBar * WXUNUSED(infobar),
gint btnid,
wxInfoBar *win)
{
win->GTKResponse(btnid);
}
static void wxgtk_infobar_close(GtkInfoBar * WXUNUSED(infobar),
wxInfoBar *win)
{
win->GTKResponse(wxID_CANCEL);
}
} // extern "C" section with GTK+ callbacks
// ============================================================================
// wxInfoBar implementation
// ============================================================================
bool wxInfoBar::Create(wxWindow *parent, wxWindowID winid)
{
if ( !UseNative() )
return wxInfoBarGeneric::Create(parent, winid);
m_impl = new wxInfoBarGTKImpl;
// this control is created initially hidden
Hide();
if ( !CreateBase(parent, winid) )
return false;
// create the info bar widget itself
m_widget = gtk_info_bar_new();
wxCHECK_MSG( m_widget, false, "failed to create GtkInfoBar" );
g_object_ref(m_widget);
// also create a label which will be used to show our message
m_impl->m_label = gtk_label_new("");
gtk_widget_show(m_impl->m_label);
GtkWidget * const
contentArea = gtk_info_bar_get_content_area(GTK_INFO_BAR(m_widget));
wxCHECK_MSG( contentArea, false, "failed to get GtkInfoBar content area" );
gtk_container_add(GTK_CONTAINER(contentArea), m_impl->m_label);
// finish creation and connect to all the signals we're interested in
m_parent->DoAddChild(this);
PostCreation(wxDefaultSize);
GTKConnectWidget("response", G_CALLBACK(wxgtk_infobar_response));
GTKConnectWidget("close", G_CALLBACK(wxgtk_infobar_close));
// Work around GTK+ bug https://bugzilla.gnome.org/show_bug.cgi?id=710888
// by disabling the transition when showing it: without this, it's not
// shown at all.
//
// Compile-time check is needed because GtkRevealer is new in 3.10.
#if GTK_CHECK_VERSION(3, 10, 0)
// Run-time check is needed because the bug was introduced in 3.10 and
// fixed in 3.22.29 (see 6b4d95e86dabfcdaa805fbf068a99e19736a39a4 and a
// couple of previous commits in GTK+ repository).
if ( gtk_check_version(3, 10, 0) == NULL &&
gtk_check_version(3, 22, 29) != NULL )
{
GObject* const
revealer = gtk_widget_get_template_child(GTK_WIDGET(m_widget),
GTK_TYPE_INFO_BAR,
"revealer");
if ( revealer )
{
gtk_revealer_set_transition_type(GTK_REVEALER (revealer),
GTK_REVEALER_TRANSITION_TYPE_NONE);
gtk_revealer_set_transition_duration(GTK_REVEALER (revealer), 0);
}
}
#endif // GTK+ >= 3.10
return true;
}
wxInfoBar::~wxInfoBar()
{
delete m_impl;
}
void wxInfoBar::ShowMessage(const wxString& msg, int flags)
{
if ( !UseNative() )
{
wxInfoBarGeneric::ShowMessage(msg, flags);
return;
}
// if we don't have any buttons, create a standard close one to give the
// user at least some way to close the bar
if ( m_impl->m_buttons.empty() && !m_impl->m_close )
{
m_impl->m_close = GTKAddButton(wxID_CLOSE);
}
GtkMessageType type;
if ( wxGTKImpl::ConvertMessageTypeFromWX(flags, &type) )
gtk_info_bar_set_message_type(GTK_INFO_BAR(m_widget), type);
gtk_label_set_text(GTK_LABEL(m_impl->m_label), wxGTK_CONV(msg));
if ( !IsShown() )
Show();
UpdateParent();
}
void wxInfoBar::Dismiss()
{
if ( !UseNative() )
{
wxInfoBarGeneric::Dismiss();
return;
}
Hide();
UpdateParent();
}
void wxInfoBar::GTKResponse(int btnid)
{
wxCommandEvent event(wxEVT_BUTTON, btnid);
event.SetEventObject(this);
if ( !HandleWindowEvent(event) )
Dismiss();
}
GtkWidget *wxInfoBar::GTKAddButton(wxWindowID btnid, const wxString& label)
{
// as GTK+ lays out the buttons vertically, adding another button changes
// our best size (at least in vertical direction)
InvalidateBestSize();
GtkWidget *button = gtk_info_bar_add_button
(
GTK_INFO_BAR(m_widget),
label.empty() ?
#if defined(__WXGTK3__) && GTK_CHECK_VERSION(3,10,0)
wxGTK_CONV(wxConvertMnemonicsToGTK(wxGetStockLabel(btnid)))
#else
wxGetStockGtkID(btnid)
#endif // GTK >= 3.10 / < 3.10
: wxGTK_CONV(label),
btnid
);
wxASSERT_MSG( button, "unexpectedly failed to add button to info bar" );
return button;
}
size_t wxInfoBar::GetButtonCount() const
{
if ( !UseNative() )
return wxInfoBarGeneric::GetButtonCount();
return m_impl->m_buttons.size();
}
wxWindowID wxInfoBar::GetButtonId(size_t idx) const
{
if ( !UseNative() )
return wxInfoBarGeneric::GetButtonId(idx);
wxCHECK_MSG( idx < m_impl->m_buttons.size(), wxID_NONE,
"Invalid infobar button position" );
return m_impl->m_buttons[idx].id;
}
void wxInfoBar::AddButton(wxWindowID btnid, const wxString& label)
{
if ( !UseNative() )
{
wxInfoBarGeneric::AddButton(btnid, label);
return;
}
// if we had created the default close button before, remove it now that we
// have some user-defined button
if ( m_impl->m_close )
{
gtk_widget_destroy(m_impl->m_close);
m_impl->m_close = NULL;
}
GtkWidget * const button = GTKAddButton(btnid, label);
if ( button )
m_impl->m_buttons.push_back(wxInfoBarGTKImpl::Button(button, btnid));
}
bool wxInfoBar::HasButtonId(wxWindowID btnid) const
{
if ( !UseNative() )
return wxInfoBarGeneric::HasButtonId(btnid);
// as in the generic version, look for the button starting from the end
const wxInfoBarGTKImpl::Buttons& buttons = m_impl->m_buttons;
for ( wxInfoBarGTKImpl::Buttons::const_reverse_iterator i = buttons.rbegin();
i != buttons.rend();
++i )
{
if ( i->id == btnid )
return true;
}
return false;
}
void wxInfoBar::RemoveButton(wxWindowID btnid)
{
if ( !UseNative() )
{
wxInfoBarGeneric::RemoveButton(btnid);
return;
}
// as in the generic version, look for the button starting from the end
wxInfoBarGTKImpl::Buttons& buttons = m_impl->m_buttons;
for ( wxInfoBarGTKImpl::Buttons::reverse_iterator i = buttons.rbegin();
i != buttons.rend();
++i )
{
if (i->id == btnid)
{
gtk_widget_destroy(i->button);
buttons.erase(i.base());
// see comment in GTKAddButton()
InvalidateBestSize();
return;
}
}
wxFAIL_MSG( wxString::Format("button with id %d not found", btnid) );
}
void wxInfoBar::DoApplyWidgetStyle(GtkRcStyle *style)
{
wxInfoBarGeneric::DoApplyWidgetStyle(style);
if ( UseNative() )
GTKApplyStyle(m_impl->m_label, style);
}
#endif // wxUSE_INFOBAR