mirror of
https://github.com/fltk/fltk.git
synced 2026-09-25 18:03:51 +08:00
Fluid: improve user dialog for new widgets
* foundation only * if a window can;t be created, give user a choice to fix the issue instead of just an error
This commit is contained in:
@@ -28,6 +28,7 @@
|
||||
#include <FL/filename.H>
|
||||
|
||||
#include <string>
|
||||
#include <exception>
|
||||
|
||||
constexpr int BROWSERWIDTH = 300;
|
||||
constexpr int BROWSERHEIGHT = 500;
|
||||
@@ -54,6 +55,23 @@ namespace widget {
|
||||
class App_Menu_Bar;
|
||||
}
|
||||
|
||||
|
||||
class Exception : public std::runtime_error {
|
||||
public:
|
||||
explicit Exception(const std::string& msg) : std::runtime_error(msg) {}
|
||||
};
|
||||
|
||||
class UserCanceledException : public Exception {
|
||||
public:
|
||||
UserCanceledException() : Exception("Operation cancelled by user") {}
|
||||
};
|
||||
|
||||
class ReadException : public Exception {
|
||||
public:
|
||||
explicit ReadException(const std::string& msg) : Exception("Read error: " + msg) {}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
Indicate the storage location for tools like layout suites and shell macros.
|
||||
\see class Fd_Shell_Command, class Layout_Suite
|
||||
|
||||
@@ -412,7 +412,15 @@ int Project_Reader::read_project(const char *filename, int merge, Strategy strat
|
||||
deselect();
|
||||
else
|
||||
proj_.reset();
|
||||
read_children(Fluid.proj.tree.current, merge, strategy);
|
||||
|
||||
try {
|
||||
read_children(Fluid.proj.tree.current, merge, strategy);
|
||||
} catch (const fluid::UserCanceledException&) {
|
||||
// User chose abort - silently return or log
|
||||
} catch (const fluid::ReadException& e) {
|
||||
fluid_alert("Error reading file: %s", e.what());
|
||||
}
|
||||
|
||||
// clear this
|
||||
Fluid.proj.tree.current = nullptr;
|
||||
// Force menu items to be rebuilt...
|
||||
@@ -448,7 +456,9 @@ int Project_Reader::read_project(const char *filename, int merge, Strategy strat
|
||||
void Project_Reader::read_error(const char *format, ...) {
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
fluid_alert(format, args);
|
||||
if (fluid_choice("Fluid: ERROR reading project file", format, "Abort", "Ignore", nullptr, args) == msg::ABORT) {
|
||||
throw fluid::UserCanceledException();
|
||||
}
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
|
||||
+84
-3
@@ -18,6 +18,11 @@
|
||||
#include "Fluid.h"
|
||||
|
||||
#include <FL/fl_ask.H>
|
||||
#include <FL/Fl_Window.H>
|
||||
#include <FL/Fl_Return_Button.H>
|
||||
#include <FL/Fl_Box.H>
|
||||
#include <FL/Fl_Check_Button.H>
|
||||
|
||||
#include "../src/flstring.h"
|
||||
|
||||
#undef min
|
||||
@@ -97,14 +102,18 @@ int fluid_choice(const char *fmt, const char *b0, const char *b1, const char *b2
|
||||
}
|
||||
|
||||
int fluid_choice(const char *fmt, const char *b0, const char *b1, const char *b2, va_list ap) {
|
||||
return fluid_choice("Fluid", fmt, b0, b1, b2, ap);
|
||||
}
|
||||
|
||||
int fluid_choice(const char *title, const char *fmt, const char *b0, const char *b1, const char *b2, va_list ap) {
|
||||
char buffer[8096];
|
||||
vsnprintf(buffer, sizeof(buffer), fmt, ap);
|
||||
if (b0 && b1 && b2) {
|
||||
return fluid::choice("Fluid", buffer, { {b0, b0[0]}, {b1, b1[0]}, {b2, b2[0]} });
|
||||
return fluid::choice(title, buffer, { {b0, b0[0]}, {b1, b1[0]}, {b2, b2[0]} });
|
||||
} else if (b0 && b1) {
|
||||
return fluid::choice("Fluid", buffer, { {b0, b0[0]}, {b1, b1[0]} });
|
||||
return fluid::choice(title, buffer, { {b0, b0[0]}, {b1, b1[0]} });
|
||||
} else if (b0) {
|
||||
return fluid::choice("Fluid", buffer, { {b0, b0[0]} });
|
||||
return fluid::choice(title, buffer, { {b0, b0[0]} });
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
@@ -220,3 +229,75 @@ int fluid::choice(const std::string &title, const std::string &message,
|
||||
}
|
||||
}
|
||||
|
||||
int fluid::big_choice(const std::string &title, const std::string &message,
|
||||
const std::vector<msg::Option> &option)
|
||||
{
|
||||
if (Fluid.console_mode()) return fluid::choice(title, message, option);
|
||||
struct Dialog {
|
||||
Fl_Window* win;
|
||||
Fl_Box* symbol, *msg;
|
||||
Fl_Button* ok;
|
||||
Fl_Button* cancel;
|
||||
Fl_Check_Button* choice[3];
|
||||
int result = msg::ESC;
|
||||
} dlg;
|
||||
dlg.win = new Fl_Window(400, 400, title.c_str());
|
||||
dlg.win->set_modal();
|
||||
dlg.win->callback(
|
||||
[](Fl_Widget* w, void* data) {
|
||||
Fl_Window* win = (Fl_Window*)w;
|
||||
Dialog* dlg = (Dialog*)data;
|
||||
dlg->result = msg::ESC;
|
||||
win->hide();
|
||||
}, &dlg);
|
||||
dlg.symbol = new Fl_Box(10, 10, 50, 50, "?");
|
||||
dlg.symbol->box(FL_THIN_UP_BOX);
|
||||
dlg.symbol->labelfont(FL_COURIER_BOLD);
|
||||
dlg.symbol->labelsize(42);
|
||||
dlg.symbol->labeltype(FL_SHADOW_LABEL);
|
||||
dlg.symbol->align(FL_ALIGN_CENTER | FL_ALIGN_INSIDE);
|
||||
dlg.symbol->labelcolor(FL_BLUE);
|
||||
dlg.symbol->color(FL_WHITE);
|
||||
fl_font(FL_HELVETICA, 12);
|
||||
int w = 320, h = 0;
|
||||
fl_measure(message.c_str(), w, h, 0);
|
||||
if (h < 50) h = 50;
|
||||
dlg.msg = new Fl_Box(70, 10, 320, h, message.c_str());
|
||||
dlg.msg->box(FL_FLAT_BOX);
|
||||
dlg.msg->align(FL_ALIGN_TOP_LEFT | FL_ALIGN_INSIDE | FL_ALIGN_WRAP);
|
||||
dlg.win->size(400, 10 + h + 18 + option.size() * 28 + 10 + 25 + 10);
|
||||
for (size_t i = 0; i < option.size(); ++i) {
|
||||
dlg.choice[i] = new Fl_Check_Button(70, 10 + h + 18 + i * 28, 320, 25, option[i].label.c_str());
|
||||
dlg.choice[i]->type(FL_RADIO_BUTTON);
|
||||
dlg.choice[i]->callback(
|
||||
[](Fl_Widget* w, void* data) {
|
||||
Fl_Window* win = w->window();
|
||||
Dialog* dlg = (Dialog*)win->user_data();
|
||||
dlg->result = fl_int(data);
|
||||
dlg->ok->activate();
|
||||
}, fl_voidptr(i));
|
||||
dlg.choice[i]->shortcut(option[i].key);
|
||||
}
|
||||
dlg.ok = new Fl_Return_Button(dlg.win->w()-220, dlg.win->h() - 10 - 25, 100, 25, "OK");
|
||||
dlg.ok->deactivate();
|
||||
dlg.ok->callback(
|
||||
[](Fl_Widget* w, void* data) {
|
||||
w->window()->hide();
|
||||
}, &dlg);
|
||||
dlg.cancel = new Fl_Button(dlg.win->w()-110, dlg.win->h() - 10 - 25, 100, 25, "Cancel");
|
||||
dlg.cancel->callback(
|
||||
[](Fl_Widget* w, void* data) {
|
||||
Dialog* dlg = (Dialog*)data;
|
||||
dlg->result = msg::ESC;
|
||||
w->window()->hide();
|
||||
}, &dlg);
|
||||
dlg.win->show();
|
||||
Fl_Window* gg = Fl::grab();
|
||||
if (gg) Fl::grab(nullptr);
|
||||
while (dlg.win->shown()) {
|
||||
Fl::wait();
|
||||
}
|
||||
delete dlg.win;
|
||||
if (gg) Fl::grab(gg);
|
||||
return dlg.result;
|
||||
}
|
||||
|
||||
+3
-1
@@ -31,6 +31,7 @@ extern void fluid_message(const char *fmt, ...);
|
||||
extern void fluid_message(const char *fmt, va_list ap);
|
||||
extern int fluid_choice(const char *fmt, const char *b0, const char *b1, const char *b2, ...);
|
||||
extern int fluid_choice(const char *fmt, const char *b0, const char *b1, const char *b2, va_list ap);
|
||||
extern int fluid_choice(const char *title, const char *fmt, const char *b0, const char *b1, const char *b2, va_list ap);
|
||||
// fl_input
|
||||
|
||||
namespace fluid {
|
||||
@@ -50,7 +51,8 @@ void alert(const std::string &title, const std::string &message);
|
||||
int error_choice(const std::string &title, const std::string &message);
|
||||
int choice(const std::string &title, const std::string &message,
|
||||
const std::vector<msg::Option> &option);
|
||||
|
||||
int big_choice(const std::string &title, const std::string &message,
|
||||
const std::vector<msg::Option> &option);
|
||||
} // namespace fluid
|
||||
|
||||
#endif // FLUID_MESSAGE_H
|
||||
|
||||
@@ -239,8 +239,32 @@ Node *Window_Node::make(Strategy strategy) {
|
||||
p = p->parent;
|
||||
}
|
||||
if (!p) {
|
||||
fluid_message("Please select a function");
|
||||
return nullptr;
|
||||
if (strategy.source() == Strategy::FROM_FILE) {
|
||||
return nullptr; // trigger a file read error
|
||||
}
|
||||
int ret = fluid::big_choice(
|
||||
"Fluid: Window Container Required",
|
||||
"A Window can only be created inside a Function or a Widget Class container.\n\n"
|
||||
"Would you like to create a new container or select an existing one?",
|
||||
{
|
||||
{"Create a &Function and add the window", 'f'},
|
||||
{"Create a &Widget Class and add the window", 'w'},
|
||||
{"&Cancel and let me select an existing container", 'c'}
|
||||
} );
|
||||
switch (ret) {
|
||||
case 0:
|
||||
p = add_new_widget_from_user("function", Strategy::AFTER_CURRENT, false);
|
||||
Fluid.proj.tree.current = anchor = p;
|
||||
strategy.placement(Strategy::AS_LAST_CHILD);
|
||||
break;
|
||||
case 1:
|
||||
p = add_new_widget_from_user("widget_class", Strategy::AFTER_CURRENT, true);
|
||||
Fluid.proj.tree.current = anchor = p;
|
||||
strategy.placement(Strategy::AS_LAST_CHILD);
|
||||
break;
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
Window_Node *myo = new Window_Node();
|
||||
if (!this->o) {// template widget
|
||||
|
||||
Reference in New Issue
Block a user