mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-07-27 21:02:23 +00:00
Merge branch 'OrcaSlicer:main' into harktech/k2-discovery
This commit is contained in:
16
src/slic3r/GUI/DeepLinkHandlerMac.h
Normal file
16
src/slic3r/GUI/DeepLinkHandlerMac.h
Normal file
@@ -0,0 +1,16 @@
|
||||
#ifndef slic3r_GUI_DeepLinkHandlerMac_h_
|
||||
#define slic3r_GUI_DeepLinkHandlerMac_h_
|
||||
|
||||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
|
||||
// Re-registers a Cocoa Apple Event handler for kInternetEventClass/kAEGetURL.
|
||||
// Works around a regression observed after upgrading to wxWidgets 3.3.2 on
|
||||
// macOS Tahoe (#13119) where wxWidgets' built-in handler is registered but
|
||||
// never fires for orcaslicer:// deep links.
|
||||
void register_mac_deep_link_handler();
|
||||
|
||||
} // namespace GUI
|
||||
} // namespace Slic3r
|
||||
|
||||
#endif
|
||||
41
src/slic3r/GUI/DeepLinkHandlerMac.mm
Normal file
41
src/slic3r/GUI/DeepLinkHandlerMac.mm
Normal file
@@ -0,0 +1,41 @@
|
||||
#import <AppKit/AppKit.h>
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#include <boost/log/trivial.hpp>
|
||||
|
||||
#include "DeepLinkHandlerMac.h"
|
||||
#include "GUI_App.hpp"
|
||||
|
||||
@interface OrcaDeepLinkHandler : NSObject
|
||||
- (void)handleGetURLEvent:(NSAppleEventDescriptor *)event withReplyEvent:(NSAppleEventDescriptor *)reply;
|
||||
@end
|
||||
|
||||
@implementation OrcaDeepLinkHandler
|
||||
- (void)handleGetURLEvent:(NSAppleEventDescriptor *)event withReplyEvent:(NSAppleEventDescriptor *)reply
|
||||
{
|
||||
NSString *url = [[event paramDescriptorForKeyword:keyDirectObject] stringValue];
|
||||
if (url == nil || url.length == 0)
|
||||
return;
|
||||
BOOST_LOG_TRIVIAL(info) << "Deep link received: " << [url UTF8String];
|
||||
Slic3r::GUI::wxGetApp().MacOpenURL(wxString::FromUTF8([url UTF8String]));
|
||||
}
|
||||
@end
|
||||
|
||||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
|
||||
void register_mac_deep_link_handler()
|
||||
{
|
||||
static OrcaDeepLinkHandler *handler = nil;
|
||||
if (handler == nil)
|
||||
handler = [[OrcaDeepLinkHandler alloc] init];
|
||||
|
||||
[[NSAppleEventManager sharedAppleEventManager]
|
||||
setEventHandler:handler
|
||||
andSelector:@selector(handleGetURLEvent:withReplyEvent:)
|
||||
forEventClass:kInternetEventClass
|
||||
andEventID:kAEGetURL];
|
||||
}
|
||||
|
||||
} // namespace GUI
|
||||
} // namespace Slic3r
|
||||
@@ -324,9 +324,18 @@ wxWindow* BitmapChoiceRenderer::CreateEditorCtrl(wxWindow* parent, wxRect labelR
|
||||
else
|
||||
c_editor->SetSelection(atoi(data.GetText().c_str()) - 1);
|
||||
|
||||
// Open the dropdown immediately when the editor is focused.
|
||||
c_editor->Bind(wxEVT_SET_FOCUS, [c_editor](wxFocusEvent& evt) {
|
||||
#ifdef __WXGTK__
|
||||
// On wxGTK the data-view editor may receive focus before its native
|
||||
// window is mapped. Opening the popup one event later avoids creating
|
||||
// the GTK popup without a valid toplevel parent.
|
||||
c_editor->CallAfter([c_editor]() {
|
||||
if (c_editor->IsShownOnScreen())
|
||||
c_editor->ForceDropdownOpen();
|
||||
});
|
||||
#else
|
||||
c_editor->ForceDropdownOpen();
|
||||
#endif
|
||||
evt.Skip();
|
||||
});
|
||||
|
||||
@@ -392,4 +401,3 @@ wxSize TextRenderer::GetSize() const
|
||||
return GetTextExtent(m_value);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -663,7 +663,9 @@ void GLTexture::render_texture(unsigned int tex_id, float left, float right, flo
|
||||
void GLTexture::render_sub_texture(unsigned int tex_id, float left, float right, float bottom, float top, const GLTexture::Quad_UVs& uvs)
|
||||
{
|
||||
glsafe(::glEnable(GL_BLEND));
|
||||
glsafe(::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA));
|
||||
// Orca: fix washed-out toolbar icons on Wayland: keep destination alpha at 1.0 so the compositor
|
||||
// does not treat anti-aliased icon edges as window transparency.
|
||||
glsafe(::glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA));
|
||||
|
||||
glsafe(::glEnable(GL_TEXTURE_2D));
|
||||
glsafe(::glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE));
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include "Downloader.hpp"
|
||||
#include <boost/chrono/duration.hpp>
|
||||
#include <boost/log/detail/native_typeof.hpp>
|
||||
#include <libslic3r/Config.hpp>
|
||||
#include <wx/event.h>
|
||||
|
||||
// Localization headers: include libslic3r version first so everything in this file
|
||||
@@ -100,6 +101,9 @@
|
||||
#include "Mouse3DController.hpp"
|
||||
#include "RemovableDriveManager.hpp"
|
||||
#include "InstanceCheck.hpp"
|
||||
#ifdef __APPLE__
|
||||
#include "DeepLinkHandlerMac.h"
|
||||
#endif
|
||||
#include "NotificationManager.hpp"
|
||||
#include "UnsavedChangesDialog.hpp"
|
||||
#include "SavePresetDialog.hpp"
|
||||
@@ -783,9 +787,20 @@ void GUI_App::post_init()
|
||||
mainframe->select_tab(size_t(MainFrame::tp3DEditor));
|
||||
plater_->select_view_3D("3D");
|
||||
//BBS init the opengl resource here
|
||||
//#ifdef __linux__
|
||||
if (plater_->canvas3D()->get_wxglcanvas()->IsShownOnScreen()&&plater_->canvas3D()->make_current_for_postinit()) {
|
||||
//#endif
|
||||
if (!plater_->canvas3D()->get_wxglcanvas()->IsShownOnScreen() ||
|
||||
!plater_->canvas3D()->make_current_for_postinit()) {
|
||||
BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << ": glcontext not ready, postpone init";
|
||||
plater_->canvas3D()->enable_render(true);
|
||||
plater_->canvas3D()->set_as_dirty();
|
||||
#ifdef __linux__
|
||||
// Wayland/EGL may not have committed the GL surface yet; ask the
|
||||
// idle loop to retry post_init when the canvas is actually mapped.
|
||||
// Without this, GL function pointers stay null and the first
|
||||
// Preview focus crashes in Camera::apply_viewport.
|
||||
m_post_initialized = false;
|
||||
return;
|
||||
#endif
|
||||
} else {
|
||||
Size canvas_size = plater_->canvas3D()->get_canvas_size();
|
||||
wxGetApp().imgui()->set_display_size(static_cast<float>(canvas_size.get_width()), static_cast<float>(canvas_size.get_height()));
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ", start to init opengl";
|
||||
@@ -805,14 +820,7 @@ void GUI_App::post_init()
|
||||
plater_->canvas3D()->render(false);
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ", finished rendering a first frame for test";
|
||||
}
|
||||
//#ifdef __linux__
|
||||
}
|
||||
else {
|
||||
BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << "Found glcontext not ready, postpone the init";
|
||||
plater_->canvas3D()->enable_render(true);
|
||||
plater_->canvas3D()->set_as_dirty();
|
||||
}
|
||||
//#endif
|
||||
if (is_editor())
|
||||
mainframe->select_tab(size_t(0));
|
||||
if (app_config->get("default_page") == "1")
|
||||
@@ -1900,6 +1908,7 @@ void GUI_App::init_networking_callbacks()
|
||||
}
|
||||
if (return_code == 5) {
|
||||
GUI::wxGetApp().CallAfter([this, provider = event.provider] {
|
||||
BOOST_LOG_TRIVIAL(info) << "logout: login expired";
|
||||
this->request_user_logout(provider);
|
||||
MessageDialog msg_dlg(nullptr, _L("Login information expired. Please login again."), "", wxAPPLY | wxOK);
|
||||
if (msg_dlg.ShowModal() == wxOK) {
|
||||
@@ -2546,6 +2555,12 @@ std::string get_system_info()
|
||||
bool GUI_App::on_init_inner()
|
||||
{
|
||||
wxLog::SetActiveTarget(new wxBoostLog());
|
||||
|
||||
#ifdef __APPLE__
|
||||
// Override wxWidgets' kAEGetURL handler so orcaslicer:// deep links keep
|
||||
// working after the wxWidgets 3.3.2 upgrade on macOS (#13119).
|
||||
register_mac_deep_link_handler();
|
||||
#endif
|
||||
#if BBL_RELEASE_TO_PUBLIC
|
||||
wxLog::SetLogLevel(wxLOG_Message);
|
||||
#endif
|
||||
@@ -4526,7 +4541,8 @@ std::string GUI_App::handle_web_request(std::string cmd)
|
||||
}
|
||||
else if (command_str.compare("homepage_logout") == 0) {
|
||||
CallAfter([this] {
|
||||
wxGetApp().request_user_logout();
|
||||
BOOST_LOG_TRIVIAL(info) << "logout: homepage_logout";
|
||||
request_user_logout();
|
||||
});
|
||||
}
|
||||
else if (command_str.compare("get_orca_login_info") == 0) {
|
||||
@@ -4539,18 +4555,22 @@ std::string GUI_App::handle_web_request(std::string cmd)
|
||||
CallAfter([this] { request_login(true, BBL_CLOUD_PROVIDER); });
|
||||
}
|
||||
else if (command_str.compare("homepage_bambu_logout") == 0) {
|
||||
CallAfter([this] { request_user_logout(BBL_CLOUD_PROVIDER); });
|
||||
CallAfter([this] {
|
||||
BOOST_LOG_TRIVIAL(info) << "logout: homepage_bambu_logout";
|
||||
request_user_logout(BBL_CLOUD_PROVIDER);
|
||||
});
|
||||
}
|
||||
else if (command_str.compare("homepage_orca_login_or_register") == 0) {
|
||||
CallAfter([this] { request_login(true, ORCA_CLOUD_PROVIDER); });
|
||||
}
|
||||
else if (command_str.compare("homepage_orca_logout") == 0) {
|
||||
CallAfter([this] { request_user_logout(ORCA_CLOUD_PROVIDER); });
|
||||
CallAfter([this] {
|
||||
BOOST_LOG_TRIVIAL(info) << "logout: homepage_orca_logout";
|
||||
request_user_logout(ORCA_CLOUD_PROVIDER);
|
||||
});
|
||||
}
|
||||
else if (command_str.compare("homepage_modeldepot") == 0) {
|
||||
CallAfter([this] {
|
||||
wxGetApp().open_mall_page_dialog();
|
||||
});
|
||||
CallAfter([this] { open_mall_page_dialog(); });
|
||||
}
|
||||
else if (command_str.compare("homepage_newproject") == 0) {
|
||||
this->request_open_project("<new>");
|
||||
@@ -4826,14 +4846,15 @@ void GUI_App::on_http_error(wxCommandEvent &evt)
|
||||
if (status == 401) {
|
||||
if (m_agent) {
|
||||
if (m_agent->is_user_login(provider)) {
|
||||
BOOST_LOG_TRIVIAL(warning) << "logout: http error 401.";
|
||||
this->request_user_logout(provider);
|
||||
|
||||
if (!m_show_http_errpr_msgdlg) {
|
||||
if (!m_show_http_error_msgdlg) {
|
||||
MessageDialog msg_dlg(nullptr, _L("Login information expired. Please login again."), "", wxAPPLY | wxOK);
|
||||
m_show_http_errpr_msgdlg = true;
|
||||
m_show_http_error_msgdlg = true;
|
||||
auto modal_result = msg_dlg.ShowModal();
|
||||
if (modal_result == wxOK || modal_result == wxCLOSE) {
|
||||
m_show_http_errpr_msgdlg = false;
|
||||
m_show_http_error_msgdlg = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -4841,6 +4862,40 @@ void GUI_App::on_http_error(wxCommandEvent &evt)
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// No need to show dialog for 410: 410 means resource has been deleted from the server.
|
||||
if (status == 410) {
|
||||
BOOST_LOG_TRIVIAL(info) << "Http error 410.";
|
||||
return;
|
||||
}
|
||||
|
||||
static bool m_is_error_shown = false;
|
||||
// Show general error notification for Orca Cloud API failures (not Bambu)
|
||||
if (provider == ORCA_CLOUD_PROVIDER && status >= 400 && code != HttpErrorVersionLimited) {
|
||||
wxString msg;
|
||||
if (!error.empty()) {
|
||||
msg = wxString::Format(_L("API error (HTTP %u): %s"), status, wxString::FromUTF8(error));
|
||||
} else {
|
||||
msg = wxString::Format(_L("API error (HTTP %u)"), status);
|
||||
}
|
||||
|
||||
if (app_config->get_bool("developer_mode")) {
|
||||
// Use notification manager if ImGui is ready; fall back to wxMessageBox on Linux
|
||||
// where ImGui may not be initialized until the user switches to the Prepare tab.
|
||||
if (wxGetApp().plater() != nullptr && wxGetApp().imgui()->display_initialized()) {
|
||||
wxGetApp()
|
||||
.plater()
|
||||
->get_notification_manager()
|
||||
->push_notification(NotificationType::PlaterError, NotificationManager::NotificationLevel::WarningNotificationLevel,
|
||||
msg.ToUTF8().data());
|
||||
}
|
||||
}
|
||||
|
||||
if (!m_is_error_shown) {
|
||||
m_is_error_shown = true;
|
||||
wxMessageBox(msg, _L("Orca Cloud API Error"), wxOK | wxICON_ERROR, wxGetApp().mainframe);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GUI_App::enable_user_preset_folder(bool enable)
|
||||
@@ -5599,6 +5654,7 @@ void GUI_App::show_check_privacy_dlg(wxCommandEvent& evt)
|
||||
privacy_dlg.Bind(EVT_PRIVACY_UPDATE_CANCEL, [this, provider](wxCommandEvent &e) {
|
||||
app_config->set_bool("privacy_update_checked", false);
|
||||
if (m_agent) {
|
||||
BOOST_LOG_TRIVIAL(info) << "logout: Privacy update dialog cancelled.";
|
||||
m_agent->user_logout(false, provider);
|
||||
post_logout_to_webview(provider);
|
||||
}
|
||||
@@ -6766,6 +6822,7 @@ void GUI_App::stop_sync_user_preset()
|
||||
void GUI_App::on_stealth_mode_enter()
|
||||
{
|
||||
stop_sync_user_preset();
|
||||
BOOST_LOG_TRIVIAL(info) << "logout: on_stealth_mode_enter";
|
||||
request_user_logout(ORCA_CLOUD_PROVIDER);
|
||||
request_user_logout(BBL_CLOUD_PROVIDER);
|
||||
if (mainframe && mainframe->m_webview) {
|
||||
|
||||
@@ -324,7 +324,7 @@ private:
|
||||
bool m_is_dark_mode{ false };
|
||||
bool m_adding_script_handler { false };
|
||||
bool m_side_popup_status{false};
|
||||
bool m_show_http_errpr_msgdlg{false};
|
||||
bool m_show_http_error_msgdlg{false};
|
||||
bool m_show_error_msgdlg{false};
|
||||
wxString m_info_dialog_content;
|
||||
HttpServer m_http_server;
|
||||
|
||||
@@ -561,14 +561,23 @@ wxString GridCellSupportEditor::ms_stringValues[2] = { wxT(""), wxT("") };
|
||||
|
||||
void GridCellSupportEditor::DoActivate(int row, int col, wxGrid* grid)
|
||||
{
|
||||
ObjectGrid* local_table = dynamic_cast<ObjectGrid*>(grid);
|
||||
wxGridBlocks cell_array = grid->GetSelectedBlocks();
|
||||
|
||||
auto left_col = cell_array.begin()->GetLeftCol();
|
||||
auto right_col = cell_array.begin()->GetRightCol();
|
||||
auto top_row = cell_array.begin()->GetTopRow();
|
||||
auto bottom_row = cell_array.begin()->GetBottomRow();
|
||||
|
||||
auto iter = cell_array.begin();
|
||||
|
||||
int left_col, right_col, top_row, bottom_row;
|
||||
if (iter == cell_array.end()) {
|
||||
// wxWidgets 3.3.x returns an empty range when nothing is selected;
|
||||
// fall back to the cell that triggered activation so the single-cell
|
||||
// branch below handles it.
|
||||
left_col = right_col = col;
|
||||
top_row = bottom_row = row;
|
||||
} else {
|
||||
left_col = iter->GetLeftCol();
|
||||
right_col = iter->GetRightCol();
|
||||
top_row = iter->GetTopRow();
|
||||
bottom_row = iter->GetBottomRow();
|
||||
}
|
||||
|
||||
if ((left_col == right_col) &&
|
||||
(top_row == bottom_row)) {
|
||||
wxGridCellBoolEditor::DoActivate(row, col, grid);
|
||||
|
||||
@@ -373,12 +373,13 @@ public:
|
||||
//BBS
|
||||
static int TOOLBAR_WINDOW_FLAGS;
|
||||
|
||||
bool display_initialized() const;
|
||||
|
||||
private:
|
||||
void init_font(bool compress);
|
||||
void init_input();
|
||||
void init_style();
|
||||
void render_draw_data(ImDrawData *draw_data);
|
||||
bool display_initialized() const;
|
||||
void destroy_font();
|
||||
std::vector<unsigned char> load_svg(const std::string& bitmap_name, unsigned target_width, unsigned target_height, unsigned *outwidth, unsigned *outheight);
|
||||
|
||||
|
||||
58
src/slic3r/GUI/MultiChoiceDialog.cpp
Normal file
58
src/slic3r/GUI/MultiChoiceDialog.cpp
Normal file
@@ -0,0 +1,58 @@
|
||||
#include "MultiChoiceDialog.hpp"
|
||||
|
||||
#include "GUI_App.hpp"
|
||||
#include "MainFrame.hpp"
|
||||
|
||||
namespace Slic3r { namespace GUI {
|
||||
|
||||
MultiChoiceDialog::MultiChoiceDialog(
|
||||
wxWindow* parent,
|
||||
const wxString& message,
|
||||
const wxString& caption,
|
||||
const wxArrayString& choices
|
||||
)
|
||||
: DPIDialog(parent ? parent : static_cast<wxWindow *>(wxGetApp().mainframe), wxID_ANY, caption, wxDefaultPosition, wxDefaultSize, wxCAPTION | wxCLOSE_BOX)
|
||||
{
|
||||
SetBackgroundColour(*wxWHITE);
|
||||
|
||||
wxBoxSizer* w_sizer = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
if(!message.IsEmpty()){
|
||||
wxStaticText *msg = new wxStaticText(this, wxID_ANY, message);
|
||||
msg->SetFont(Label::Body_13);
|
||||
msg->Wrap(-1);
|
||||
w_sizer->Add(msg, 0, wxRIGHT | wxLEFT | wxTOP, FromDIP(10));
|
||||
}
|
||||
|
||||
m_check_list = new CheckList(this, choices);
|
||||
|
||||
w_sizer->Add(m_check_list, 1, wxRIGHT | wxLEFT | wxTOP | wxEXPAND, FromDIP(10));
|
||||
|
||||
auto dlg_btns = new DialogButtons(this, {"OK", "Cancel"});
|
||||
|
||||
dlg_btns->GetOK()->Bind( wxEVT_BUTTON, [this](wxCommandEvent &e) {EndModal(wxID_OK);});
|
||||
dlg_btns->GetCANCEL()->Bind(wxEVT_BUTTON, [this](wxCommandEvent &e) {EndModal(wxID_CANCEL);});
|
||||
|
||||
w_sizer->Add(dlg_btns, 0, wxEXPAND);
|
||||
|
||||
SetSizer(w_sizer);
|
||||
Layout();
|
||||
w_sizer->Fit(this);
|
||||
wxGetApp().UpdateDlgDarkUI(this);
|
||||
}
|
||||
|
||||
wxArrayInt MultiChoiceDialog::GetSelections() const
|
||||
{
|
||||
return m_check_list->GetSelections();
|
||||
}
|
||||
|
||||
void MultiChoiceDialog::SetSelections(wxArrayInt sel_array)
|
||||
{
|
||||
m_check_list->SetSelections(sel_array);
|
||||
}
|
||||
|
||||
MultiChoiceDialog::~MultiChoiceDialog() {}
|
||||
|
||||
void MultiChoiceDialog::on_dpi_changed(const wxRect &suggested_rect) {}
|
||||
|
||||
}} // namespace Slic3r::GUI
|
||||
37
src/slic3r/GUI/MultiChoiceDialog.hpp
Normal file
37
src/slic3r/GUI/MultiChoiceDialog.hpp
Normal file
@@ -0,0 +1,37 @@
|
||||
#ifndef slic3r_GUI_MultiChoiceDialog_hpp_
|
||||
#define slic3r_GUI_MultiChoiceDialog_hpp_
|
||||
|
||||
#include "Widgets/CheckList.hpp"
|
||||
#include "Widgets/DialogButtons.hpp"
|
||||
|
||||
#include <wx/wx.h>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
|
||||
|
||||
namespace Slic3r { namespace GUI {
|
||||
|
||||
class MultiChoiceDialog : public DPIDialog
|
||||
{
|
||||
public:
|
||||
MultiChoiceDialog(
|
||||
wxWindow* parent = nullptr,
|
||||
const wxString& message = wxEmptyString,
|
||||
const wxString& caption = wxEmptyString,
|
||||
const wxArrayString& choices = wxArrayString()
|
||||
);
|
||||
~MultiChoiceDialog();
|
||||
|
||||
wxArrayInt GetSelections() const;
|
||||
|
||||
void SetSelections(wxArrayInt sel_array);
|
||||
|
||||
protected:
|
||||
CheckList* m_check_list;
|
||||
wxArrayInt m_selected_indices;
|
||||
void on_dpi_changed(const wxRect &suggested_rect) override;
|
||||
};
|
||||
|
||||
}} // namespace Slic3r::GUI
|
||||
|
||||
#endif
|
||||
@@ -23,6 +23,81 @@
|
||||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
|
||||
#ifdef __linux__
|
||||
// Workaround for #7210: WebKitGTK crashes on vue-resize's hidden <object> probe used by
|
||||
// older Fluidd/Mainsail pages. Swap that <object> for a <div> shim at appendChild time
|
||||
// and bridge resize events through a fake contentDocument.defaultView so vue-resize keeps
|
||||
// working. Workaround proposed by @VittC.
|
||||
static void inject_vue_resize_workaround(wxWebView *webView)
|
||||
{
|
||||
webView->AddUserScript(
|
||||
"(function() {"
|
||||
" 'use strict';"
|
||||
" function isVueResizeObject(el) {"
|
||||
" return el && el.tagName === 'OBJECT'"
|
||||
" && el.type === 'text/html'"
|
||||
" && el.getAttribute('aria-hidden') === 'true'"
|
||||
" && el.getAttribute('tabindex') === '-1';"
|
||||
" }"
|
||||
" function isResizeObserverParent(p) {"
|
||||
" return p && p.classList && p.classList.contains('resize-observer');"
|
||||
" }"
|
||||
" function makeShim(orig, parentForRO) {"
|
||||
" var shim = document.createElement('div');"
|
||||
" shim.setAttribute('aria-hidden', 'true');"
|
||||
" shim.setAttribute('tabindex', '-1');"
|
||||
" shim.style.display = 'none';"
|
||||
" var fakeWin = document.createElement('div');"
|
||||
" var ro = null;"
|
||||
" var origRemoveEL = fakeWin.removeEventListener.bind(fakeWin);"
|
||||
" fakeWin.removeEventListener = function(type, fn, opts) {"
|
||||
" origRemoveEL(type, fn, opts);"
|
||||
" if (type === 'resize' && ro) { ro.disconnect(); ro = null; }"
|
||||
" };"
|
||||
" Object.defineProperty(shim, 'contentDocument', {"
|
||||
" configurable: true,"
|
||||
" get: function() { return { defaultView: fakeWin }; }"
|
||||
" });"
|
||||
" Object.defineProperty(shim, 'contentWindow', {"
|
||||
" configurable: true,"
|
||||
" get: function() { return fakeWin; }"
|
||||
" });"
|
||||
" if (typeof orig.onload === 'function') { shim.onload = orig.onload; }"
|
||||
" queueMicrotask(function() {"
|
||||
" if (parentForRO && typeof ResizeObserver !== 'undefined') {"
|
||||
" ro = new ResizeObserver(function() {"
|
||||
" fakeWin.dispatchEvent(new Event('resize'));"
|
||||
" });"
|
||||
" ro.observe(parentForRO);"
|
||||
" }"
|
||||
" if (typeof shim.onload === 'function') {"
|
||||
" try { shim.onload(new Event('load')); } catch (e) {}"
|
||||
" }"
|
||||
" shim.dispatchEvent(new Event('load'));"
|
||||
" });"
|
||||
" return shim;"
|
||||
" }"
|
||||
" var origAppend = Node.prototype.appendChild;"
|
||||
" Node.prototype.appendChild = function(child) {"
|
||||
" if (isResizeObserverParent(this) && isVueResizeObject(child)) {"
|
||||
" return origAppend.call(this, makeShim(child, this));"
|
||||
" }"
|
||||
" return origAppend.call(this, child);"
|
||||
" };"
|
||||
" var origInsertBefore = Node.prototype.insertBefore;"
|
||||
" Node.prototype.insertBefore = function(child, ref) {"
|
||||
" if (isResizeObserverParent(this) && isVueResizeObject(child)) {"
|
||||
" return origInsertBefore.call(this, makeShim(child, this), ref);"
|
||||
" }"
|
||||
" return origInsertBefore.call(this, child, ref);"
|
||||
" };"
|
||||
" console.log('[vr-fix] vue-resize WebKitGTK patch active');"
|
||||
"})();",
|
||||
wxWEBVIEW_INJECT_AT_DOCUMENT_START
|
||||
);
|
||||
}
|
||||
#endif
|
||||
|
||||
PrinterWebView::PrinterWebView(wxWindow *parent)
|
||||
: wxPanel(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize)
|
||||
, m_browser(nullptr)
|
||||
@@ -43,6 +118,8 @@ PrinterWebView::PrinterWebView(wxWindow *parent)
|
||||
}
|
||||
|
||||
#ifdef __linux__
|
||||
inject_vue_resize_workaround(m_browser);
|
||||
|
||||
auto cookiesPath = boost::filesystem::path(data_dir() + "/cache/cookies.db");
|
||||
auto wv = static_cast<WebKitWebView*>(m_browser->GetNativeBackend());
|
||||
auto wv_ctx = webkit_web_view_get_context(wv);
|
||||
@@ -165,6 +242,10 @@ void PrinterWebView::SendAPIKey()
|
||||
)",
|
||||
m_apikey);
|
||||
m_browser->RemoveAllUserScripts();
|
||||
#ifdef __linux__
|
||||
// Re-inject the vue-resize/WebKitGTK workaround that RemoveAllUserScripts just cleared.
|
||||
inject_vue_resize_workaround(m_browser);
|
||||
#endif
|
||||
|
||||
m_browser->AddUserScript(script);
|
||||
m_browser->Reload();
|
||||
|
||||
@@ -40,7 +40,7 @@
|
||||
#include "UnsavedChangesDialog.hpp"
|
||||
#include "SavePresetDialog.hpp"
|
||||
#include "EditGCodeDialog.hpp"
|
||||
|
||||
#include "MultiChoiceDialog.hpp"
|
||||
#include "MsgDialog.hpp"
|
||||
#include "Notebook.hpp"
|
||||
|
||||
@@ -4500,6 +4500,8 @@ void TabPrinter::build_fff()
|
||||
line.append_option(optgroup->get_option("fan_speedup_overhangs"));
|
||||
optgroup->append_line(line);
|
||||
optgroup->append_single_option_line("fan_kickstart", "printer_basic_information_cooling_fan#fan-kick-start-time");
|
||||
// ORCA: PWM floor for fans that won't spool at low duty cycles.
|
||||
optgroup->append_single_option_line("part_cooling_fan_min_pwm", "printer_basic_information_cooling_fan#minimum-non-zero-part-cooling-fan-speed");
|
||||
|
||||
optgroup = page->new_optgroup(L("Extruder Clearance"), "param_extruder_clearance");
|
||||
optgroup->append_single_option_line("extruder_clearance_radius", "printer_basic_information_extruder_clearance#radius");
|
||||
@@ -7002,7 +7004,15 @@ wxSizer* Tab::compatible_widget_create(wxWindow* parent, PresetDependencies &dep
|
||||
presets.Add(from_u8(preset.name));
|
||||
}
|
||||
|
||||
wxMultiChoiceDialog dlg(parent, deps.dialog_title, deps.dialog_label, presets);
|
||||
if(deps.type == Preset::TYPE_PRINTER){
|
||||
deps.dialog_title = "Compatible printers";
|
||||
deps.dialog_label = "Select printers";
|
||||
}else{
|
||||
deps.dialog_title = "Compatible process profiles";
|
||||
deps.dialog_label = "Select profiles";
|
||||
}
|
||||
|
||||
MultiChoiceDialog dlg(parent, deps.dialog_label, deps.dialog_title, presets);
|
||||
wxGetApp().UpdateDlgDarkUI(&dlg);
|
||||
// Collect and set indices of depending_presets marked as compatible.
|
||||
wxArrayInt selections;
|
||||
@@ -7015,13 +7025,16 @@ wxSizer* Tab::compatible_widget_create(wxWindow* parent, PresetDependencies &dep
|
||||
break;
|
||||
}
|
||||
dlg.SetSelections(selections);
|
||||
dlg.SetSize(FromDIP(wxSize(360, 480)));
|
||||
std::vector<std::string> value;
|
||||
// Show the dialog.
|
||||
if (dlg.ShowModal() == wxID_OK) {
|
||||
selections.Clear();
|
||||
selections = dlg.GetSelections();
|
||||
for (auto idx : selections)
|
||||
value.push_back(presets[idx].ToUTF8().data());
|
||||
// leave list empty if all items checked. this will check "All" checkbox automatically. also fixes unnecessary config change
|
||||
if(selections.GetCount() != presets.GetCount())
|
||||
for (auto idx : selections)
|
||||
value.push_back(presets[idx].ToUTF8().data());
|
||||
if (value.empty()) {
|
||||
deps.checkbox->SetValue(1);
|
||||
deps.btn->Disable();
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include <boost/filesystem/operations.hpp>
|
||||
#include <boost/filesystem/path.hpp>
|
||||
#include <boost/iostreams/detail/select.hpp>
|
||||
#include <boost/log/trivial.hpp>
|
||||
#include <string.h>
|
||||
#include "I18N.hpp"
|
||||
#include "libslic3r/AppConfig.hpp"
|
||||
@@ -516,7 +517,9 @@ void GuideFrame::OnScriptMessage(wxWebViewEvent &evt)
|
||||
if (agent) {
|
||||
agent->set_country_code(country_code);
|
||||
if (wxGetApp().is_user_login()) {
|
||||
agent->user_logout();
|
||||
BOOST_LOG_TRIVIAL(info) << "logout: user_logout on user_guide_finish";
|
||||
// agent->user_logout();
|
||||
wxGetApp().request_user_logout();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
225
src/slic3r/GUI/Widgets/CheckList.cpp
Normal file
225
src/slic3r/GUI/Widgets/CheckList.cpp
Normal file
@@ -0,0 +1,225 @@
|
||||
#include "CheckList.hpp"
|
||||
|
||||
#include "slic3r/GUI/GUI_App.hpp"
|
||||
|
||||
CheckList::CheckList(
|
||||
wxWindow* parent,
|
||||
const wxArrayString& choices,
|
||||
long scroll_style
|
||||
)
|
||||
: wxWindow(parent, wxID_ANY)
|
||||
, m_search(this, "search", 16)
|
||||
, m_menu(this, "filter", 16)
|
||||
, m_first_load(true)
|
||||
{
|
||||
Freeze();
|
||||
w_sizer = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
f_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
f_bar = new wxPanel(this, wxID_ANY);
|
||||
f_bar->SetBackgroundColour(parent->GetBackgroundColour());
|
||||
m_filter_box = new TextInput(f_bar, "", "", "", wxDefaultPosition, wxDefaultSize, wxTE_PROCESS_ENTER);
|
||||
m_filter_box->SetIcon(m_search.bmp());
|
||||
m_filter_box->SetMinSize(FromDIP(wxSize(200,24)));
|
||||
m_filter_box->SetSize(FromDIP(wxSize(-1,24)));
|
||||
m_filter_box->SetFocus();
|
||||
m_filter_ctrl = m_filter_box->GetTextCtrl();
|
||||
m_filter_ctrl->SetFont(Label::Body_13);
|
||||
m_filter_ctrl->SetSize(wxSize(-1, FromDIP(16))); // Centers text vertically
|
||||
m_filter_ctrl->SetHint(_L("Type to filter..."));
|
||||
m_filter_ctrl->Bind(wxEVT_TEXT, [this](auto &e) {Filter(m_filter_ctrl->GetValue());});
|
||||
m_filter_ctrl->Bind(wxEVT_TEXT_ENTER, [this](auto &e) {Filter(m_filter_ctrl->GetValue());});
|
||||
m_filter_ctrl->Bind(wxEVT_SET_FOCUS, [this](auto &e) {Filter(m_filter_ctrl->GetValue());e.Skip();});
|
||||
m_filter_ctrl->Bind(wxEVT_KILL_FOCUS, [this](auto &e) {Filter(m_filter_ctrl->GetValue());e.Skip();});
|
||||
f_sizer->Add(m_filter_box, 1, wxEXPAND);
|
||||
Bind(wxEVT_SET_FOCUS, [this](auto &e) {m_filter_box->SetFocus();});
|
||||
|
||||
fb_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
auto create_btn = [this] (wxString title, bool select){
|
||||
auto btn = new wxStaticText(f_bar, wxID_ANY, title);
|
||||
btn->SetForegroundColour("#009687");
|
||||
btn->SetCursor(wxCURSOR_HAND);
|
||||
btn->SetFont(Label::Body_13);
|
||||
btn->Bind(wxEVT_LEFT_DOWN, [this, select](wxMouseEvent &e) {SelectAll(select);});
|
||||
fb_sizer->Add(btn, 0, wxLEFT | wxALIGN_CENTER_VERTICAL, FromDIP(10));
|
||||
};
|
||||
f_sizer->Add(fb_sizer,0 ,wxALIGN_CENTER_VERTICAL);
|
||||
create_btn(_L("All") , true);
|
||||
create_btn(_L("None"), false);
|
||||
|
||||
m_menu_button = new wxStaticBitmap(f_bar, wxID_ANY, m_menu.bmp());
|
||||
m_menu_button->SetCursor(wxCURSOR_HAND);
|
||||
m_menu_button->Bind(wxEVT_LEFT_DOWN, &CheckList::ShowMenu, this);
|
||||
f_sizer->Add(m_menu_button,0 ,wxLEFT | wxALIGN_CENTER_VERTICAL, FromDIP(10));
|
||||
|
||||
f_bar->SetSizerAndFit(f_sizer);
|
||||
w_sizer->Add(f_bar, 0, wxEXPAND);
|
||||
|
||||
auto spacer = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxSize(-1, FromDIP(3)));
|
||||
spacer->SetBackgroundColour(parent->GetBackgroundColour());
|
||||
w_sizer->Add(spacer, 0, wxEXPAND);
|
||||
|
||||
s_sizer = new wxBoxSizer(wxVERTICAL);
|
||||
m_scroll_area = new wxScrolledWindow(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, scroll_style);
|
||||
m_scroll_area->SetScrollRate(0, 10);
|
||||
m_scroll_area->SetSizer(s_sizer);
|
||||
m_scroll_area->SetBackgroundColour(parent->GetBackgroundColour());
|
||||
m_scroll_area->Bind(wxEVT_RIGHT_DOWN, &CheckList::ShowMenu, this);
|
||||
m_scroll_area->DisableFocusFromKeyboard();
|
||||
|
||||
m_info = new wxStaticText(m_scroll_area, wxID_ANY, "");
|
||||
m_info->SetFont(Label::Body_13);
|
||||
s_sizer->Add(m_info, 1, wxALIGN_CENTER_HORIZONTAL | wxALL, FromDIP(10));
|
||||
m_info->Hide();
|
||||
|
||||
m_info_nonsel = _L("No selected items...");
|
||||
m_info_allsel = _L("All items selected...");
|
||||
m_info_empty = _L("No matching items...");
|
||||
|
||||
SetBackgroundColour(StateColor::darkModeColorFor("#DBDBDB")); // draws border on wxScrolledWindow
|
||||
|
||||
w_sizer->Add(m_scroll_area, 1, wxEXPAND | wxALL, FromDIP(1)); // 1 for border
|
||||
s_sizer->Layout();
|
||||
|
||||
m_list_size = choices.size();
|
||||
|
||||
m_checks.reserve(m_list_size);
|
||||
|
||||
auto margin = FromDIP(2);
|
||||
wxCheckBox* cb;
|
||||
|
||||
for (size_t i = 0; i < m_list_size; ++i){
|
||||
cb = new wxCheckBox(m_scroll_area, wxID_ANY, choices[i]);
|
||||
m_checks.emplace_back(cb);
|
||||
s_sizer->Add(cb, 0, wxALL, margin);
|
||||
}
|
||||
|
||||
m_scroll_area->FitInside();
|
||||
s_sizer->Layout();
|
||||
|
||||
SetSizer(w_sizer);
|
||||
Layout();
|
||||
Thaw();
|
||||
}
|
||||
|
||||
void CheckList::SetSelections(wxArrayInt sel_array){
|
||||
if(!m_first_load){
|
||||
for (size_t i = 0; i < m_list_size; ++i)
|
||||
Check(i, false);
|
||||
m_first_load = false;
|
||||
}
|
||||
|
||||
for (int i : sel_array)
|
||||
Check(i, true);
|
||||
}
|
||||
|
||||
wxArrayInt CheckList::GetSelections()
|
||||
{
|
||||
wxArrayInt checks;
|
||||
for (size_t i = 0; i < m_list_size; ++i)
|
||||
if (m_checks[i]->GetValue())
|
||||
checks.push_back(i);
|
||||
return checks;
|
||||
}
|
||||
|
||||
void CheckList::Check(int i, bool checked)
|
||||
{
|
||||
if (i > -1 && i < m_list_size)
|
||||
m_checks[i]->SetValue(checked);
|
||||
}
|
||||
|
||||
void CheckList::SelectAll(bool value)
|
||||
{
|
||||
for (size_t i = 0; i < m_list_size; ++i)
|
||||
Check(i, value);
|
||||
}
|
||||
|
||||
void CheckList::SelectVisible(bool value)
|
||||
{
|
||||
auto filter = m_filter_ctrl->GetValue().Lower();
|
||||
if((!value && filter == "::unsel") || (value && filter == "::sel"))
|
||||
m_filter_ctrl->SetValue("");
|
||||
for (size_t i = 0; i < m_list_size; ++i)
|
||||
if(m_checks[i]->IsShown())
|
||||
Check(i, value);
|
||||
}
|
||||
|
||||
bool CheckList::IsChecked(int i)
|
||||
{
|
||||
if (i > -1 && i < m_list_size)
|
||||
return false;
|
||||
return m_checks[i]->GetValue();
|
||||
}
|
||||
|
||||
void CheckList::Filter(const wxString& filterText)
|
||||
{
|
||||
Freeze();
|
||||
auto filter = filterText.Lower();
|
||||
auto c_text = m_filter_ctrl->GetValue().Lower();
|
||||
|
||||
if(filter == "::sel" || filter == "::nonsel"){
|
||||
if(c_text != filter){ // not text input
|
||||
m_filter_ctrl->SetValue(filter);
|
||||
m_filter_ctrl->SetSelection(0,-1);
|
||||
}
|
||||
fb_sizer->Show(false);
|
||||
if (filter == "::sel")
|
||||
for (auto& cb : m_checks) cb->Show(cb->GetValue());
|
||||
else
|
||||
for (auto& cb : m_checks) cb->Show(!cb->GetValue());
|
||||
}
|
||||
else{
|
||||
bool clear = filterText.IsEmpty();
|
||||
fb_sizer->Show(clear);
|
||||
for (auto& cb : m_checks)
|
||||
cb->Show(clear || cb->GetLabel().Lower().Contains(filter));
|
||||
}
|
||||
|
||||
m_info->Show();
|
||||
for (size_t i = 0; i < m_list_size; ++i) {
|
||||
if (m_checks[i]->IsShown()){
|
||||
m_info->Hide();
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (m_info->IsShown())
|
||||
m_info->SetLabel(filter == "::sel" ? m_info_nonsel : filter == "::nonsel" ? m_info_allsel : m_info_empty);
|
||||
|
||||
m_scroll_area->FitInside();
|
||||
f_sizer->Layout();
|
||||
Thaw();
|
||||
}
|
||||
|
||||
void CheckList::ShowMenu(wxMouseEvent &evt)
|
||||
{
|
||||
bool filtering = !m_filter_ctrl->GetValue().IsEmpty();
|
||||
bool list_empty = m_info->IsShown();
|
||||
|
||||
wxMenu m;
|
||||
m.Append(wxID_FILE1, _L("Select All" ))->Enable(!filtering);
|
||||
m.Append(wxID_FILE2, _L("Deselect All"))->Enable(!filtering);
|
||||
m.AppendSeparator();
|
||||
m.Append(wxID_FILE3, _L("Select visible" ))->Enable(!list_empty && filtering);
|
||||
m.Append(wxID_FILE4, _L("Deselect visible"))->Enable(!list_empty && filtering);
|
||||
m.AppendSeparator();
|
||||
m.Append(wxID_FILE5, _L("Filter selected" ));
|
||||
m.Append(wxID_FILE6, _L("Filter nonSelected"));
|
||||
|
||||
m.Bind(wxEVT_MENU, [this](wxCommandEvent& e) {
|
||||
switch (e.GetId()){
|
||||
case wxID_FILE1: SelectAll(true) ; break;
|
||||
case wxID_FILE2: SelectAll(false) ; break;
|
||||
case wxID_FILE3: SelectVisible(true) ; break;
|
||||
case wxID_FILE4: SelectVisible(false); break;
|
||||
case wxID_FILE5: Filter("::sel") ; break;
|
||||
case wxID_FILE6: Filter("::nonsel") ; break;
|
||||
default: break;
|
||||
}
|
||||
},wxID_FILE1, wxID_FILE6);
|
||||
|
||||
wxWindow* src = dynamic_cast<wxWindow*>(evt.GetEventObject());
|
||||
if (!src) return;
|
||||
wxPoint screen_pos = src->ClientToScreen(evt.GetPosition());
|
||||
wxPoint local_pos = ScreenToClient(screen_pos);
|
||||
PopupMenu(&m, local_pos);
|
||||
}
|
||||
59
src/slic3r/GUI/Widgets/CheckList.hpp
Normal file
59
src/slic3r/GUI/Widgets/CheckList.hpp
Normal file
@@ -0,0 +1,59 @@
|
||||
#ifndef slic3r_GUI_CHECKLIST_hpp_
|
||||
#define slic3r_GUI_CHECKLIST_hpp_
|
||||
|
||||
#include "../wxExtensions.hpp"
|
||||
|
||||
#include "Label.hpp"
|
||||
#include "TextInput.hpp"
|
||||
|
||||
#include <wx/wx.h>
|
||||
#include <vector>
|
||||
#include <wx/scrolwin.h>
|
||||
#include <wx/menu.h>
|
||||
|
||||
class CheckList : public wxWindow
|
||||
{
|
||||
public:
|
||||
CheckList(
|
||||
wxWindow* parent,
|
||||
const wxArrayString& choices = wxArrayString(),
|
||||
long scroll_style = wxVSCROLL
|
||||
);
|
||||
|
||||
wxArrayInt GetSelections();
|
||||
void SetSelections(wxArrayInt sel_array);
|
||||
void Check(int i, bool checked);
|
||||
bool IsChecked(int i);
|
||||
|
||||
void Filter(const wxString& filterText);
|
||||
void SelectAll(bool value);
|
||||
void SelectVisible(bool value);
|
||||
|
||||
private:
|
||||
void ShowMenu(wxMouseEvent &e);
|
||||
|
||||
std::vector<wxCheckBox*> m_checks;
|
||||
int m_list_size;
|
||||
bool m_first_load;
|
||||
wxBoxSizer* w_sizer;
|
||||
|
||||
wxPanel* f_bar;
|
||||
wxBoxSizer* f_sizer;
|
||||
TextInput* m_filter_box;
|
||||
wxTextCtrl* m_filter_ctrl;
|
||||
wxBoxSizer* fb_sizer;
|
||||
wxStaticBitmap* m_menu_button;
|
||||
|
||||
wxScrolledWindow* m_scroll_area;
|
||||
wxBoxSizer* s_sizer;
|
||||
|
||||
wxStaticText* m_info;
|
||||
wxString m_info_nonsel;
|
||||
wxString m_info_allsel;
|
||||
wxString m_info_empty;
|
||||
|
||||
ScalableBitmap m_search;
|
||||
ScalableBitmap m_menu;
|
||||
};
|
||||
|
||||
#endif // !slic3r_GUI_CheckList_hpp_
|
||||
@@ -167,6 +167,30 @@ bool DropDown::HasDismissLongTime()
|
||||
(now - dismissTime).total_milliseconds() >= 20;
|
||||
}
|
||||
|
||||
void DropDown::Popup(wxWindow *focus)
|
||||
{
|
||||
#ifdef __WXGTK__
|
||||
if (!mainDropDown && m_widget) {
|
||||
// Data-view cell editors can receive focus before wxGTK infers a
|
||||
// native popup parent, so provide the current toplevel explicitly.
|
||||
GtkWindow *transient_parent = nullptr;
|
||||
for (wxWindow *win = GetParent(); win; win = win->GetParent()) {
|
||||
GtkWidget *widget = static_cast<GtkWidget *>(win->GetHandle());
|
||||
if (!widget)
|
||||
continue;
|
||||
GtkWidget *top = gtk_widget_get_toplevel(widget);
|
||||
if (GTK_IS_WINDOW(top)) {
|
||||
transient_parent = GTK_WINDOW(top);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (transient_parent)
|
||||
gtk_window_set_transient_for(GTK_WINDOW(m_widget), transient_parent);
|
||||
}
|
||||
#endif
|
||||
PopupWindow::Popup(focus);
|
||||
}
|
||||
|
||||
void DropDown::paintEvent(wxPaintEvent& evt)
|
||||
{
|
||||
// depending on your system you may need to look at double-buffered dcs
|
||||
@@ -554,7 +578,8 @@ void DropDown::messureSize()
|
||||
wxWindow::SetSize(szContent);
|
||||
#ifdef __WXGTK__
|
||||
// Gtk has a wrapper window for popup widget
|
||||
gtk_window_resize (GTK_WINDOW (m_widget), szContent.x, szContent.y);
|
||||
if (szContent.x > 0 && szContent.y > 0)
|
||||
gtk_window_resize (GTK_WINDOW (m_widget), szContent.x, szContent.y);
|
||||
#endif
|
||||
if (!groups.empty() && subDropDown == nullptr) {
|
||||
subDropDown = new DropDown(items);
|
||||
@@ -564,11 +589,6 @@ void DropDown::messureSize()
|
||||
subDropDown->use_content_width = true;
|
||||
subDropDown->Create(GetParent());
|
||||
#ifdef __WXGTK__
|
||||
// Orca: Keep the wx parent as the combobox so wxPopupTransientWindow installs
|
||||
// its capture handlers on the main dropdown, but make the native GTK
|
||||
// popup transient for the currently open popup to satisfy Wayland's
|
||||
// xdg-shell rule that a popup's parent must be the topmost mapped popup.
|
||||
gtk_window_set_transient_for(GTK_WINDOW(subDropDown->GetHandle()), GTK_WINDOW(GetHandle()));
|
||||
// Orca: On Wayland, while the sub holds an xdg_popup grab, motion events for
|
||||
// the cursor over main may not be delivered (Mutter drops motion
|
||||
// outside the grabbing surface). Poll on idle and synthesize a
|
||||
@@ -636,8 +656,11 @@ void DropDown::autoPosition()
|
||||
// may exceed
|
||||
auto drect = wxDisplay(GetParent()).GetGeometry();
|
||||
if (GetPosition().y + size.y + 10 > drect.GetBottom()) {
|
||||
int available_height = drect.GetBottom() - GetPosition().y - 10;
|
||||
if (available_height < rowSize.y * 2)
|
||||
return;
|
||||
if (use_content_width && count <= 15) size.x += 6;
|
||||
size.y = drect.GetBottom() - GetPosition().y - 10;
|
||||
size.y = available_height;
|
||||
wxWindow::SetSize(size);
|
||||
if (selection >= 0) {
|
||||
if (offset.y + rowSize.y * (selection + 1) > size.y)
|
||||
@@ -727,6 +750,13 @@ void DropDown::mouseMove(wxMouseEvent &event)
|
||||
drop.group = items[-index - 2].group_key;
|
||||
drop.need_sync = true;
|
||||
drop.messureSize();
|
||||
#ifdef __WXGTK__
|
||||
// wxGTK wraps popup contents in a native GtkWindow. Make the submenu
|
||||
// transient for the currently mapped parent popup window before
|
||||
// positioning/showing it, so wlroots/Hyprland sees the topmost parent.
|
||||
if (m_widget && drop.m_widget)
|
||||
gtk_window_set_transient_for(GTK_WINDOW(drop.m_widget), GTK_WINDOW(m_widget));
|
||||
#endif
|
||||
drop.autoPosition();
|
||||
drop.paintNow();
|
||||
if (!drop.IsShown())
|
||||
|
||||
@@ -105,6 +105,8 @@ public:
|
||||
|
||||
bool HasDismissLongTime();
|
||||
|
||||
void Popup(wxWindow *focus = nullptr) override;
|
||||
|
||||
protected:
|
||||
void Dismiss() override;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user