How to connect buttons with methods, which will be called on click - c++

I started programming some custom gui application. But i need to know how to connect button with some method.
For example i have:
class Button
{
private:
string Text;
/*etc*/
public:
string GetLabel();
void SetLabel(string label);
void Connect(/*method name as param*/)
{
//TODO -this is that thing i need help here
/*this is method for connecting button with method, which will be
called on click, which im asking for help*/
}
};
class Window
{
private:
int sizeX,sizeY;
public:
void Put(Button * button,int _hpos,int _vpos);
void Show();
/*etc */
};
class MyWindow : public Window
{
public:
MyWindow()
{
InitializeComponents();
}
void Run()
{
this -> Put(btn1,10,10); //put button in window at position 10,10
this -> Show();
}
private:
Button * btn1;
void InitializeComponents()
{
btn1 = new Button();
btn1 -> Connect(on_btn1_click); //select which method will be called when user click this button, which I dont know how to do so pls help
}
void on_btn1_click() //this method is called when user click the button
{
//do something if user clicked btn1
}
};
int main()
{
MyWindow * win1 = new MyWindow();
win1 -> Run();
return 0;
}
So there is private method inside MyWindow class which will be called when user clicks the button (btn1). Method Connect() selects which method will be used for calling when user clicks the button.

You can check if the mouse is over a button/UI object, If it is than trigger a command/event.
const Button& GetObjectBelowMouse(int xMousePos, int yMousePos) // This would be equavalent to your Connect type
{
// all buttons would be your container of buttons / GUI objects.
for( const auto& button : allButtons)
{
// top,bottom,left,right would be the bounding rectangle that encapsulates the coordinates of the object
if( (xMousePos > button.left && xMousePos < button.right ) && (yMousePos > button.bottom && yMousePos < buttom.top))
{
return button;
}
}
// in this case we have a different constructor that creates a "empty" button that has no function
return Button(NO_OBJECT);
}
// the usage would be something like
int main()
{
/*
.. create button, set it's possition, etc
*/
// Option 1
if(GetObjectBelowMouse(mouse.x, mouse.y).type != NO_OBJECT)
{
// Do whatever you want, you know that the user has clicked a valid object
button.Foo();
}
// Option 2
GetObjectBelowMouse(mouse.x, mouse.y).Foo(); // You know that every button has a foo object, and that NO_OBJECT has a foo that does nothing, so you don't need to compare if it is NO_OBJECT or not.
}

Related

GTKmm popover menu items not highlighting when used with treeview

Good afternoon,
I'm trying to integrate a popover context menu to a treeview widget in GTKmm 4.
I've been successful in getting the menu to be displayed and for the respective actions to be called when clicking on the context menu options, however, I'm finding that the menu items are not being highlighted when the mouse hovers over them.
A GIF showing what I'm seeing is here:
If however, using the same code, I attach the menu and action group to another widget (such as a button or the window itself), all works as expected and the options are highlighted correctly.
Below is code for a minimal reproducible example.
Could someone help as I'm going round in circles with this??
#include <gtkmm.h>
class Window : public Gtk::Window {
public:
Window() {
list_store_ = Gtk::ListStore::create(model_);
auto row = *(list_store_->append());
row[model_.id] = 1;
row[model_.name] = "Example 1";
row = *(list_store_->append());
row[model_.id] = 2;
row[model_.name] = "Example 2";
treeview_.set_hexpand(true);
treeview_.set_vexpand(true);
treeview_.set_model(list_store_);
treeview_.append_column("ID", model_.id);
treeview_.append_column("Name", model_.name);
Glib::RefPtr<Gio::Menu> gmenu = Gio::Menu::create();
gmenu->append("_Edit", "popup.edit");
gmenu->append("_Remove", "popup.remove");
menu_.set_parent(treeview_);
menu_.set_menu_model(gmenu);
menu_.set_has_arrow(false);
Glib::RefPtr<Gio::SimpleActionGroup> action_group = Gio::SimpleActionGroup::create();
action_group->add_action("edit", sigc::mem_fun(*this, &Window::on_popup_edit));
action_group->add_action("remove", sigc::mem_fun(*this, &Window::on_popup_remove));
treeview_.insert_action_group("popup", action_group);
Glib::RefPtr<Gtk::GestureClick> gesture = Gtk::GestureClick::create();
gesture->set_button(GDK_BUTTON_SECONDARY);
gesture->signal_pressed().connect(sigc::mem_fun(*this, &Window::on_popup_button_pressed));
treeview_.add_controller(gesture);
set_child(treeview_);
}
~Window() override {
}
private:
class ExampleModel : public Gtk::TreeModel::ColumnRecord {
public:
ExampleModel() {
add(id);
add(name);
}
Gtk::TreeModelColumn<int> id;
Gtk::TreeModelColumn<Glib::ustring> name;
};
void on_popup_button_pressed(int, double x, double y) {
int cx, cy;
treeview_.convert_widget_to_bin_window_coords(x, y, cx, cy);
Gtk::TreeModel::Path path;
treeview_.get_path_at_pos(cx, cy, path);
if (!path) {
return;
}
const Gdk::Rectangle rect(x, y, 1, 1);
menu_.set_pointing_to(rect);
menu_.popup();
}
void on_popup_edit() { /* Implementation here */ }
void on_popup_remove() { /* Implementation here */ }
Gtk::TreeView treeview_;
ExampleModel model_;
Glib::RefPtr<Gtk::ListStore> list_store_;
Gtk::PopoverMenu menu_;
};
int main(int argc, char** argv) {
auto app = Gtk::Application::create("com.example.treeview");
return app->make_window_and_run<Window>(argc, argv);
}
Okay, so I recently had time to come back to this project.
This time I thought I'd attempt to re-write it in Rust using the gtk4-rs crate and see if the same happens and it does!
I've managed to work around this issue by wrapping the TreeView inside of a ScrolledWindow and then setting up the menu against the ScrolledView instead of the TreeView.
Inside the on_popup_button_pressed function, I'm still able to determine the path/item that was clicked.
Updated code (untested though - as the project is now written in Rust instead) is below:
#include <gtkmm.h>
class Window : public Gtk::Window {
public:
Window() {
list_store_ = Gtk::ListStore::create(model_);
auto row = *(list_store_->append());
row[model_.id] = 1;
row[model_.name] = "Example 1";
row = *(list_store_->append());
row[model_.id] = 2;
row[model_.name] = "Example 2";
treeview_.set_hexpand(true);
treeview_.set_vexpand(true);
treeview_.set_model(list_store_);
treeview_.append_column("ID", model_.id);
treeview_.append_column("Name", model_.name);
scrolled_window_.set_child(treeview_);
Glib::RefPtr<Gio::Menu> gmenu = Gio::Menu::create();
gmenu->append("_Edit", "popup.edit");
gmenu->append("_Remove", "popup.remove");
menu_.set_parent(scrolled_window_);
menu_.set_menu_model(gmenu);
menu_.set_has_arrow(false);
Glib::RefPtr<Gio::SimpleActionGroup> action_group = Gio::SimpleActionGroup::create();
action_group->add_action("edit", sigc::mem_fun(*this, &Window::on_popup_edit));
action_group->add_action("remove", sigc::mem_fun(*this, &Window::on_popup_remove));
scrolled_window.insert_action_group("popup", action_group);
Glib::RefPtr<Gtk::GestureClick> gesture = Gtk::GestureClick::create();
gesture->set_button(GDK_BUTTON_SECONDARY);
gesture->signal_pressed().connect(sigc::mem_fun(*this, &Window::on_popup_button_pressed));
scrolled_window_.add_controller(gesture);
set_child(scrolled_window_);
}
~Window() override {
}
private:
class ExampleModel : public Gtk::TreeModel::ColumnRecord {
public:
ExampleModel() {
add(id);
add(name);
}
Gtk::TreeModelColumn<int> id;
Gtk::TreeModelColumn<Glib::ustring> name;
};
void on_popup_button_pressed(int, double x, double y) {
int cx, cy;
treeview_.convert_widget_to_bin_window_coords(x, y, cx, cy);
Gtk::TreeModel::Path path;
treeview_.get_path_at_pos(cx, cy, path);
if (!path) {
return;
}
const Gdk::Rectangle rect(x, y, 1, 1);
menu_.set_pointing_to(rect);
menu_.popup();
}
void on_popup_edit() { /* Implementation here */ }
void on_popup_remove() { /* Implementation here */ }
Gtk::TreeView treeview_;
Gtk::ScrolledWindow scrolled_window_;
ExampleModel model_;
Glib::RefPtr<Gtk::ListStore> list_store_;
Gtk::PopoverMenu menu_;
};
int main(int argc, char** argv) {
auto app = Gtk::Application::create("com.example.treeview");
return app->make_window_and_run<Window>(argc, argv);
}

Trigger checkbutton hover event from another widget

I made a custom Right-to-Left check button widget using a Gtk::CheckButton without the label and a Gtk::Label inside Gtk::EventBox.
The reason I went this way instead of simply calling set_direction(Gtk::TEXT_DIR_RTL) is that I want to manually specify the alignment for these widgets.
I've figured out activating/deactivating checkbutton state when the label's clicked but I'm having trouble triggering the hover event from label to checkbutton. The default Gtk::CheckButton behavior triggers the hover effect on the clickable square button to indicate it's being activated when text is hovered too. How do I achieve that same behavior on my custom right-to-left checkbutton widget?
Below is the minimal reproducible code for testing:
#include <gtkmm.h>
using Gtk::Application;
using Gtk::Button;
using Gtk::CheckButton;
using Gtk::EventBox;
using Gtk::Grid;
using Gtk::Label;
using Gtk::Window;
class MyWindow : public Window {
Button button;
CheckButton checkbutton;
EventBox eventbox;
Grid grid;
Label label;
bool on_label_clicked(GdkEventButton *);
void arrange_content_layout();
public:
MyWindow();
};
bool MyWindow::on_label_clicked(GdkEventButton *event)
{
if (event->button == 1 && event->type == GDK_BUTTON_PRESS) {
checkbutton.set_active(!checkbutton.get_active());
return true;
}
return false;
}
void MyWindow::arrange_content_layout()
{
checkbutton.set_margin_start(6);
button.set_margin_top(24);
grid.child_property_width(button) = 2;
grid.set_margin_top(24);
grid.set_margin_start(24);
grid.set_margin_end(24);
grid.set_margin_bottom(24);
}
MyWindow::MyWindow()
: button("Click to steal focus")
, label ("Our custom RTL label")
{
eventbox.add(label);
eventbox.signal_button_press_event().connect(sigc::mem_fun(
*this, &MyWindow::on_label_clicked));
grid.add(eventbox);
grid.add(checkbutton);
grid.attach(button, 0, 1);
add(grid);
arrange_content_layout();
set_default_size(120, 60);
show_all();
}
int main()
{
auto app = Application::create("domain.reverse.sample.rtl-checkbutton");
MyWindow window;
return app->run(window);
}
One way is to use set_state_flags() and trick the visual output of your Right-to-Left checkbutton when the label is being hovered.
First, you need to enable these two masks for eventbox to capture enter and leave event:
Gdk::ENTER_NOTIFY_MASK
Gdk::LEAVE_NOTIFY_MASK
eventbox.add_events(Gdk::ENTER_NOTIFY_MASK | Gdk::LEAVE_NOTIFY_MASK);
And connect eventbox signals
class MyWindow : public Window {
...
bool on_label_entered(GdkEventCrossing *);
bool on_label_exited(GdkEventCrossing *);
...
}
MyWindow::MyWindow()
...
{
...
eventbox.signal_enter_notify_event().connect(sigc::mem_fun(
*this, &MyWindow::on_label_entered));
eventbox.signal_leave_notify_event().connect(sigc::mem_fun(
*this, &MyWindow::on_label_exited));
...
And finally, use Gtk::STATE_FLAG_PRELIGHT to do the UI trick
// Enter event
bool MyWindow::on_label_entered(GdkEventCrossing *event)
{
if (event->mode != GDK_CROSSING_NORMAL) return false;
checkbutton.set_state_flags(checkbutton.get_state_flags() | Gtk::STATE_FLAG_PRELIGHT);
return true;
}
// Leave event
bool MyWindow::on_label_exited(GdkEventCrossing *event)
{
if (event->mode != GDK_CROSSING_NORMAL) return false;
checkbutton.unset_state_flags(Gtk::STATE_FLAG_PRELIGHT);
return true;
}

How can I program the function "go back" as a Menu Bar option of a MainWindow in QT?

I am working in a Program in QT where I have a LOGIN Dialog, if the person is logged successfully then the program shows a MainWindow object. There's a Menu Bar in that object. One of the options of the Menu Bar is LOGOUT, then what can I do to make the MainWindow object close or hide and the LOGIN Dialog show again?
Here's what I tried:
In the main.cpp
Dialog login;
if (login.exec() == QDialog::Rejected)
{
return -1;
}
MainWindow mainWindow;
mainWindow.show();
In the LOGOUT function from the Menu Bar:
void MainWindow::on_actionLogOut_triggered()
{
close();
//What else can I do here to make the LOGIN Dialog appear again?
}
I also tried to create a new LOGIN object in the on_actionLogOut_triggered() method but it goes out of the scope and the new Dialog object disappear immediately.
Here you would need a finite state machine...
Depending on which state your application is in and which functions are called will determine the behavior of the overall system. You will want to have your own functions to show a login window, to show the main window, to log in, to log out, etc... then you need to build your structure with the appropriate logic.
Pseudo Example:
enum class AppState {
LOGGED_IN,
LOGGED_OUT,
};
class Application {
AppState state_;
public:
Application() : state_{AppState::LOGGED_OUT} {
run();
}
~Application() {
exitApp();
}
void exitApp() {
// clean up resources
closeMainWindow(true);
}
void run() {
do {
presentLoginScreen();
} while(state_ != AppState::LOGGED_IN);
if (state_ == AppState::LOGGED_IN) {
while (state_ != AppState::LOGGED_OUT)
// do stuff
}
}
void logIn(/*user input*/) {
// test input
}
void logOut() {
if(state_ == AppState::LOGGED_IN) {
closeMainWindow();
state_ = AppState::LOGGED_OUT;
presentLogginScreen();
}
}
void presentLogginScreen() {
// display the login screen
// get user input
logIn(/*user input*/);
// if user input matches credentials sign them in and show the main window
if( credentials == valid_credentials ) {
showMainWindow();
state_ = AppState::LOGGED_IN;
}
}
void closeMainWindow(bool exitApp) {
if(exitApp) // cleanup memory and shutdown
else // otherwise just close window and present login screen
}
};

C++ public and private classes, with screen movements?

I am stuck, I need help, I can't post all files here because this is a project with almost 20 files of c++. But it basically all boils down to movements on a screen.
-The class "MyClass" is the screen, it has a constructor which calls the functions. the screen has a small-box at the bottom. when you press the mouse down,up, or move it, it sends a signal or 'Event" and one of the three functions below is called;
-function-mouseMotion, it should move the small-box to where your mouse is pointing when you are moving the mouse, sort of drag. this function should be active whenever you press down your mouse, say whenever mouse is down
-function-mouseButtonDown, when u press your mouse down, the small-box should move from the bottom of screen to where your cursor is.
-function-mouseButtonUp, when you unclick your mouse, this function will be called and all the movements of small box should stop.
Right now as the code is, whenever I bring the ouse to the screen, the small box immediately jumps and starts following the mouse. But our professor wants us to change the functions such that when I introduce the moue on the screen, nothing should happen. When I click down the mouse, the box should then come to where the mouse is. If I click and hold the mouse down as I move it, the box should keep following the mouse. Finally, if i unclick the mouse, the box needs to remain where i unclicked it from.
What alograithm would you use, am new to c++
using namespace WALY;
class MyClass : public Frame {
private:
void(*switchAction) (void);
Frame* box;
static void mouseButtonDown (Frame* f, const Event* e);
static void mouseButtonUp (Frame* f, const Event* e);
static void mouseMotion (Frame* f, const Event* e);
public:
MyClass (Frame* parent, void (*switchFunc) (void));
void activate (void);
void deactivate (void);
};
MyClass::MyClass(Frame *parent, void (*switchFunc)(void)) : Frame (parent), switchAction (switchFunc)
{
box = new Frame (this, 250, 700); // creating the box on the screen
box->setAlign (ALIGN_C);
Rect boxRect;
boxRect.x = boxRect.y = 0;
boxRect.w = 80;
boxRect.h = 50;
box->setScrollRect (boxRect);
box->useSolidBackground (0x808080);
setCallbackFunc (MOUSE_DOWN, mouseButtonDown); // registering functions into wally library
setCallbackFunc (MOUSE_UP, mouseButtonUp);
setCallbackFunc (MOUSE_MOTION, mouseMotion);
deactivate ();
}
void MyClass::activate(void)
{
setActive(true);
setVisible(true);
}
void MyClass::deactivate(void)
{
setVisible(false);
setActive(false);
}
void
MyClass::mouseMotion (Frame* f, const Event* e)
{
MyClass* wywtcyc = (MyClass*)f;
wywtcyc->box->setX (e->motion.x);
wywtcyc->box->setY (e->motion.y);
}
void MyClass::mouseButtonDown (Frame* f, const Event* e)
{
// (e->button.x)
}
void MyClass::mouseButtonUp (Frame* f, const Event* e)
{
}
The logic could be like below:
Declare variables:
mouseIsDown = False;
released = False;
In mouseMotion:
if (mouseIsDown && ! released) {
// your current code
}
In mouseButtonDown:
if (mouseIsDown) {
released = True;
} else {
mouseIsDown = True;
released = False;
}
In mouseButtonUp:
if (mouseIsDown) {
mouseIsDown = False;
released = True;
}

Repositioning a label with mouseevents to anywhere on the screen

I've created a label and used setPixmap to attach a .png image to the label. I've also setWindowFlags to disable the title bar and create a frameless window. Because I've disabled those, it also disables the ability to drag anything around, so I want to create mouseevents (unless there's a better method) to position my label anywhere on the screen exactly like dragging the frame of a window. How would I do that? An example and brief explanation would be greatly appreciated.
reimplement the QMouseEvents you need .... like
void MyLabel::mousePressEvent(QMouseEvent* e)
{
m_moveDatWidget = true;
// so when the mousebutton got pressed, you set something to
// tell your code to move the widget ... consider maybe you
// want to move it only on right button pressed ...
}
void MyLabel::mouseReleaseEvent(QMouseEvent* e)
{
m_moveDatWidget = false;
// after releasing do not forget to reset the movement variable
}
void MyLabel::mouseMoveEvent(QMouseEvent* e)
{
// when in 'moving state' ...
if (m_moveDatWidget)
{
// move your widget around using QWidget::move(qreal,qreal)
}
}
this is only a really basic implementation but it should do well if you calculate the desired movement correct :)
I would implement the label dragging by mouse in the following way:
class Label : public QLabel
{
public:
// Implement the constructor(s)
protected:
void Label::mouseMoveEvent(QMouseEvent* event)
{
if (!m_offset.isNull()) {
move(event->globalPos() - m_offset);
}
QLabel::mouseMoveEvent(event);
}
void Label::mousePressEvent(QMouseEvent* event)
{
// Get the mouse offset in the label's coordinates system.
m_offset = event->globalPos() - pos();
QLabel::mousePressEvent(event);
}
void Notifier::mouseReleaseEvent(QMouseEvent* event)
{
m_offset = QPoint();
QLabel::mouseReleaseEvent(event);
}
private:
// The mouse pointer offset from the top left corner of the label.
QPoint m_offset;
};