xtd icon indicating copy to clipboard operation
xtd copied to clipboard

[ENHANCEMENT] xtd.forms - message_notifier

Open gammasoft71 opened this issue 2 years ago • 3 comments

xtd.forms - xtd::forms::message_notifier

Library

xtd.forms

Enhancement

xtd::forms::message_notifier

Description

message_notifier is a small, nonblocking notification pop-up. A message_notifier is shown to users with readable message content at the bottom or top of the screen or at a specific target and disappears automatically after a few seconds (time-out). The control has various built-in options for customizing visual elements, durations, and dismissing toasts.

  • [ ] xtd::forms::message_notifier component

First draft

Unfortunately, the different OS don't manage the notifications in the same way. So, we should have as for the dialog box xtd::forms::about_dialog manage the two styles of dialog:

  • xtd::forms::dialog_style::standard (notification managed by xtd)
  • xtd::forms::dialog_style::system (notification system).

Notification system :

Use wxNotificationMessage whenever possible. But typically, on macOS it doesn't work (Maybe for a user right ?). And if the wxWidgets component doesn't match maybe call the native version for each OS (as I did for xtd::forms::message_box which was not implemented correctly in macOS and Windows).

Standard notification :

Implement our own xtd::forms::form (no border, no title and no controls). Add an image, a message and the possibility of 1, 2 or more buttons. Add an xtd::forms::timer for automatic closing.

Common

  • Add asynchronous button events (like xtd::forms::show_sheet) which allows to know which button has been clicked.
  • Add a close event.
  • add properties for the message_notifier

API

Globally the API will be close to xtd::forms::message_dialog and xtd::forms::message_box with the possibility to have custom buttons and a timer to close the notification automatically.

gammasoft71 avatar May 17 '22 12:05 gammasoft71

It may be a good approach to be able to use xtd::forms::message_notifier like this (as initial mock-up):

message_notifier mn;
mn.title("TITLE");
mn.message("MESSAGE"):
mn.notifier_style(message_notifier_style::information | warning | error); // if an icon is set, the system icon style will be overwritten with the icon.
mn.icon(xtd::drawing::icon::empty); // if icon is empty, system icon style (message_notifier_style) will be displayed instead.
mn.button_action("Ok", []() {
    xtd::diagnostics::debug::write_line("Action: Ok");
 });
mn.button_action("Cancel", []() {
    xtd::diagnostics::debug::write_line("Action: Cancel");
 });
 // .. as many button actions as we like ..
mn.click += [] {
    xtd::diagnostics::debug::write_line("Event: message notifier clicked");
};
mn.notifier_closed += [] {
    xtd::diagnostics::debug::write_line("Event: message notifier closed");
};
mn.timeout(10 * 1000); // timeout in milliseconds
mn.show();

baderouaich avatar May 20 '22 16:05 baderouaich

For the events, I prefer to have only a close event and to be able to get the result of the notifier a bit like for the message_dialog. This allows to keep the same consistency in the whole framework. If someone knows the message_dialog, he automatically knows the message_notifier and vice versa ;-).

I really see the message_notifier class as the message_dialog class. With a timeout that can be activated or not to close automatically the notification. And just the possibility to change the text of the buttons (I would also like to add this possibility to message_dialog ;-)).

Look at the following example of the message_dialog in asynchronous.

Implementation sheet...

namespace xtd {
  enum class notifier_result {
    ok,
    cancel,
    yes,
    no,
    //...
  };
  
  class notifier_closed_event_args : public xtd::event_Args {
  public:
    //...
  
    xtd::forms::notifier_result notifier_reult() const;
  
  //...
  };

  class message_notifier : public xtd::forms::componant {
  public:
    /// ...
    
    /// Properties
    
    bool close_timeout_enabled() const;
    message_notifier& close_timeout_enabled(bool value);
    
    std::chrono::milliseconds close_timeout_interval() const;
    message_notifier& close_timeout_interval(std::chrono::milliseconds value);
    message_notifier& close_timeout_interval_milliseconds(int32_t value);
    
    const xtd::string& button_ok_text() const;
    message_notifier& button_ok_text(const xtd::string& value);
    message_notifier& button_ok_text(std::nullptr_t); // Reset to default value
    
    xtd::forms::notifier_result notifier_result() const;
    
    /// ...

    xtd::forms::message_notifier_buttons buttons() const;
    message_notifier& buttons(xtd::forms::message_notifier_buttons value);
    
    const xtd::drawing::image& icon() const;
    message_notifier& icon(xtd::forms::message_notifier_icon value);
    message_notifier& icon(const xtd::drawing::image&);
    message_notifier& icon(const xtd::drawing::icon&);
    
    /// Events
    event<message_notifier, xtd::forms::notifier_closed_event_handler> notifier_closed;
    
  protected:
    virtual void on_notifier_closed(const xtd::forms::notifier_closed_event_args& e);

    // ...     
  };
}

Example of message_notifier usage :

message_notifier notfier;
notifier.title("Title");
notifier.message("message...");
notifier.notifier_icon();
notifier.icon(message_notifier_icon::question); // or: notifier.icon(xtd::drawing::image("my_icon"));
notifier.buttons(message_notifier::buttons::ok_cancel);
notifier.cancel_button_text("Dismiss");
notifier.notifier_closed += [&](const notifier_closed_event_args& e) {
  if (e.notifier_result() == notifier_result::ok) // or: if (notifier.notifier_result() == notifier_result::ok)
   //...
};
notifier.show();

gammasoft71 avatar May 20 '22 17:05 gammasoft71

After some thought and research, the following code shows the new implementation proposal:

namespace xtd {
  namespace forms {
    class notifier_button : public xtd::forms::component {
      public:
        notifier_button() = default;
        notifier_button(const xtd::ustring& text);
        
        const xtd::ustring& text() const noexcept;
        notifier_button& text(const xtd::ustring& value);
    };
    
    using notifier_button_ref = std::reference_wrapper<notifier_button>;

    class notifier_button_click_event_args : public xtd::event_args {
    public:
      //...
    
      xtd::forms::notifier_button button() const;
    
    //...
    };
  
    class notifier_closed_event_args : public xtd::event_args {
    public:
      //...
    
      std::optional<xtd::forms::notifier_button> button() const;
      bool close_on_timeout() const noexcept;
      bool close_on_click_message() const noexcept;
    
      //...
    };

    class message_notifier : public xtd::forms::componant {
    public:
      /// ...

      /// @name Alias
      
      using notifier_button_collection = xtd::forms::layout::arranged_element_collection<notifier_button_ref>;
      
      /// Properties
      
      const notifier_button_collection& buttons() const noexcept;
      notifier_button_collection& buttons();

      
      bool close_timeout_enabled() const noexcept;
      message_notifier& close_timeout_enabled(bool value);
      
      std::chrono::milliseconds close_timeout_interval() const noexcept;
      message_notifier& close_timeout_interval(std::chrono::milliseconds value);
      message_notifier& close_timeout_interval_milliseconds(int32_t value);
      
      std::optional<xtd::forms::notifier_button> notifier_button_clicked() const;
      
      /// ...
  
      const xtd::drawing::image& icon() const;
      message_notifier& icon(const xtd::drawing::image&);
      message_notifier& icon(const xtd::drawing::icon&);
      
      /// Events
      
      event<message_notifier, xtd::forms::notifier_button_click_event_handler> button_click;
      event<message_notifier, xtd::forms::notifier_closed_event_handler> notifier_closed;
      
    protected:
      virtual void on_button_click(const xtd::forms::message_notifier_button_click_event_args& e);
      virtual void on_notifier_closed(const xtd::forms::notifier_closed_event_args& e);

      // ...     
    };
  }
}

And usage :

#include <xtd/xtd>

using namespace xtd;
using namespace std::literals;
using namespace xtd::forms;

namespace examples {
  class form1 : public form {
  public:
    form1() {
      text("Message notifier example");
      
      button1.parent(*this);
      button1.location({10, 10});
      button1.text("Notify");
      button1.click += [&] {
        message_notifier1.show();
      };
      
      message_notifier1.buttons().push_back_range({start_message_notifier_button, cancel_message_notifier_button});
      message_notifier1.close_timeout_enabled(true);
      message_notifier1.close_timeout_interval(2s);
      message_notifier1.icon(xtd::drawing::system_icons::question());
      message_notifier1.message("Start the auto backup now");
      message_notifier1.notifier_style(notifier_style::standard);
      message_notifier1.title("Backup");
      message_notifier.button_click += {*this, &form1::on_message_notifier_button_click};
    }
    
  private:
    void on_message_notifier_button_click(object& sender, const on_message_notifier_button_click_event_args& e) {
      if (e.button() == start_message_notifier_button)
        diagnostics::debug::write_line("Start backup");
      else
        diagnostics::debug::write_line("Cancel backup");
    }

    button button1;
    message_notifier_button start_message_notifier_button {"&Start"};
    message_notifier_button cancel_message_notifier_button {"&Cancel"};
    message_notifier message_notifier1;
  };
}

int main() {
  application::run(examples::form1());
}

gammasoft71 avatar Jun 24 '22 20:06 gammasoft71