Files
wxWidgets/src/gtk/checkbox.cpp
Vadim Zeitlin eadee05729 Don't change CSS for wxCheckNox with wxNO_BORDER in wxGTK 3
This makes the checkbox look ugly as it's clearly not supposed to be
rendered without the border at all, so it's better to do nothing than
mangle its CSS.

It could be better to add some virtual GTKTurnOffBorder() method that
could be overridden to do nothing in wxCheckBox and we should consider
doing this if there are other classes for which wxNO_BORDER breaks their
appearance, but for now, as long as it's the only case in which we need
to do this, just turn wxNO_BORDER off when calling PostCreation().
2019-11-29 04:31:14 +01:00

276 lines
7.9 KiB
C++

/////////////////////////////////////////////////////////////////////////////
// Name: src/gtk/checkbox.cpp
// Purpose:
// Author: Robert Roebling
// Copyright: (c) 1998 Robert Roebling
// Licence: wxWindows licence
/////////////////////////////////////////////////////////////////////////////
// For compilers that support precompilation, includes "wx.h".
#include "wx/wxprec.h"
#if wxUSE_CHECKBOX
#include "wx/checkbox.h"
#include "wx/gtk/private/wrapgtk.h"
#include "wx/gtk/private/eventsdisabler.h"
//-----------------------------------------------------------------------------
// data
//-----------------------------------------------------------------------------
extern bool g_blockEventsOnDrag;
//-----------------------------------------------------------------------------
// "clicked"
//-----------------------------------------------------------------------------
extern "C" {
static void gtk_checkbox_toggled_callback(GtkWidget *widget, wxCheckBox *cb)
{
if (g_blockEventsOnDrag) return;
// Transitions for 3state checkbox must be done manually, GTK's checkbox
// is 2state with additional "undetermined state" flag which isn't
// changed automatically:
if (cb->Is3State())
{
GtkToggleButton *toggle = GTK_TOGGLE_BUTTON(widget);
if (cb->Is3rdStateAllowedForUser())
{
// The 3 states cycle like this when clicked:
// checked -> undetermined -> unchecked -> checked -> ...
bool active = gtk_toggle_button_get_active(toggle) != 0;
bool inconsistent = gtk_toggle_button_get_inconsistent(toggle) != 0;
wxGtkEventsDisabler<wxCheckBox> noEvents(cb);
if (!active && !inconsistent)
{
// checked -> undetermined
gtk_toggle_button_set_active(toggle, true);
gtk_toggle_button_set_inconsistent(toggle, true);
}
else if (!active && inconsistent)
{
// undetermined -> unchecked
gtk_toggle_button_set_inconsistent(toggle, false);
}
else if (active && !inconsistent)
{
// unchecked -> checked
// nothing to do
}
else
{
wxFAIL_MSG(wxT("3state wxCheckBox in unexpected state!"));
}
}
else
{
// user's action unsets undetermined state:
gtk_toggle_button_set_inconsistent(toggle, false);
}
}
wxCommandEvent event(wxEVT_CHECKBOX, cb->GetId());
event.SetInt(cb->Get3StateValue());
event.SetEventObject(cb);
cb->HandleWindowEvent(event);
}
}
//-----------------------------------------------------------------------------
// wxCheckBox
//-----------------------------------------------------------------------------
wxCheckBox::wxCheckBox()
{
m_widgetCheckbox = NULL;
}
wxCheckBox::~wxCheckBox()
{
if (m_widgetCheckbox && m_widgetCheckbox != m_widget)
GTKDisconnect(m_widgetCheckbox);
}
bool wxCheckBox::Create(wxWindow *parent,
wxWindowID id,
const wxString &label,
const wxPoint &pos,
const wxSize &size,
long style,
const wxValidator& validator,
const wxString &name )
{
WXValidateStyle( &style );
if (!PreCreation( parent, pos, size ) ||
!CreateBase( parent, id, pos, size, style, validator, name ))
{
wxFAIL_MSG( wxT("wxCheckBox creation failed") );
return false;
}
if ( style & wxALIGN_RIGHT )
{
// VZ: as I don't know a way to create a right aligned checkbox with
// GTK we will create a checkbox without label and a label at the
// left of it
m_widgetCheckbox = gtk_check_button_new();
m_widgetLabel = gtk_label_new("");
#ifdef __WXGTK4__
g_object_set(m_widgetLabel, "xalign", 0.0f, NULL);
#else
wxGCC_WARNING_SUPPRESS(deprecated-declarations)
gtk_misc_set_alignment(GTK_MISC(m_widgetLabel), 0.0, 0.5);
wxGCC_WARNING_RESTORE()
#endif
m_widget = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
gtk_box_pack_start(GTK_BOX(m_widget), m_widgetLabel, FALSE, FALSE, 3);
gtk_box_pack_start(GTK_BOX(m_widget), m_widgetCheckbox, FALSE, FALSE, 3);
gtk_widget_show( m_widgetLabel );
gtk_widget_show( m_widgetCheckbox );
}
else
{
m_widgetCheckbox = gtk_check_button_new_with_label("");
m_widgetLabel = gtk_bin_get_child(GTK_BIN(m_widgetCheckbox));
m_widget = m_widgetCheckbox;
}
g_object_ref(m_widget);
SetLabel( label );
if ( style & wxNO_BORDER )
{
gtk_container_set_border_width(GTK_CONTAINER(m_widgetCheckbox), 0);
}
g_signal_connect (m_widgetCheckbox, "toggled",
G_CALLBACK (gtk_checkbox_toggled_callback), this);
m_parent->DoAddChild( this );
#ifdef __WXGTK3__
// CSS added if the window has wxNO_BORDER inside base class PostCreation()
// makes checkbox look broken in the default GTK 3 theme, so avoid doing
// this by temporarily turning this flag off.
if ( style & wxNO_BORDER )
ToggleWindowStyle(wxNO_BORDER);
#endif
PostCreation(size);
#ifdef __WXGTK3__
// Turn it back on if necessary.
if ( style & wxNO_BORDER )
ToggleWindowStyle(wxNO_BORDER);
#endif
return true;
}
void wxCheckBox::GTKDisableEvents()
{
g_signal_handlers_block_by_func(m_widgetCheckbox,
(gpointer) gtk_checkbox_toggled_callback, this);
}
void wxCheckBox::GTKEnableEvents()
{
g_signal_handlers_unblock_by_func(m_widgetCheckbox,
(gpointer) gtk_checkbox_toggled_callback, this);
}
void wxCheckBox::SetValue( bool state )
{
wxCHECK_RET( m_widgetCheckbox != NULL, wxT("invalid checkbox") );
if (state == GetValue())
return;
wxGtkEventsDisabler<wxCheckBox> noEvents(this);
gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON(m_widgetCheckbox), state );
}
bool wxCheckBox::GetValue() const
{
wxCHECK_MSG( m_widgetCheckbox != NULL, false, wxT("invalid checkbox") );
return gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(m_widgetCheckbox)) != 0;
}
void wxCheckBox::DoSet3StateValue(wxCheckBoxState state)
{
SetValue(state != wxCHK_UNCHECKED);
gtk_toggle_button_set_inconsistent(GTK_TOGGLE_BUTTON(m_widgetCheckbox),
state == wxCHK_UNDETERMINED);
}
wxCheckBoxState wxCheckBox::DoGet3StateValue() const
{
if (gtk_toggle_button_get_inconsistent(GTK_TOGGLE_BUTTON(m_widgetCheckbox)))
{
return wxCHK_UNDETERMINED;
}
else
{
return GetValue() ? wxCHK_CHECKED : wxCHK_UNCHECKED;
}
}
void wxCheckBox::SetLabel( const wxString& label )
{
wxCHECK_RET( m_widgetLabel != NULL, wxT("invalid checkbox") );
// If we don't hide the empty label, in some themes a focus rectangle is
// still drawn around it and this looks out of place.
if ( label.empty() )
gtk_widget_hide(m_widgetLabel);
else
gtk_widget_show(m_widgetLabel);
// save the label inside m_label in case user calls GetLabel() later
wxControl::SetLabel(label);
GTKSetLabelForLabel(GTK_LABEL(m_widgetLabel), label);
}
void wxCheckBox::DoEnable(bool enable)
{
if ( !m_widgetLabel )
return;
base_type::DoEnable(enable);
gtk_widget_set_sensitive( m_widgetLabel, enable );
if (enable)
GTKFixSensitivity();
}
void wxCheckBox::DoApplyWidgetStyle(GtkRcStyle *style)
{
GTKApplyStyle(m_widgetCheckbox, style);
GTKApplyStyle(m_widgetLabel, style);
}
GdkWindow *wxCheckBox::GTKGetWindow(wxArrayGdkWindows& WXUNUSED(windows)) const
{
return gtk_button_get_event_window(GTK_BUTTON(m_widgetCheckbox));
}
// static
wxVisualAttributes
wxCheckBox::GetClassDefaultAttributes(wxWindowVariant WXUNUSED(variant))
{
return GetDefaultAttributesFromGTKWidget(gtk_check_button_new());
}
#endif