diff --git a/doc/settings.md b/doc/settings.md index ffa9c095f4..81e4499723 100644 --- a/doc/settings.md +++ b/doc/settings.md @@ -875,8 +875,8 @@ Notes - - + + @@ -890,7 +890,7 @@ Notes - + diff --git a/src/netxs/apps.hpp b/src/netxs/apps.hpp index 399913fdd7..148e45f95a 100644 --- a/src/netxs/apps.hpp +++ b/src/netxs/apps.hpp @@ -683,7 +683,7 @@ namespace netxs::app::shared auto& backup = boss.base::template field(); boss.on(tier::mouserelease, input::key::MouseAny, [&](hids& gear) { - if (gear.cause == input::key::MouseDown) + if ((gear.cause & input::key::MouseAnyButtonMask) == input::key::MouseDown) { if (backup.empty()) { @@ -698,6 +698,7 @@ namespace netxs::app::shared gear.setfree(); boss.set(backup); backup.clear(); + gear.dismiss_dblclick(); } }); }); diff --git a/src/netxs/apps/desk.hpp b/src/netxs/apps/desk.hpp index d1d2042413..dee9b8640a 100644 --- a/src/netxs/apps/desk.hpp +++ b/src/netxs/apps/desk.hpp @@ -157,7 +157,11 @@ namespace netxs::app::desk }; }); auto app_label = item_area->attach(slot::_1, ui::item::ctor(ansi::add(utf8).mgl(0).wrp(wrap::off).jet(bias::left))) + ->active() ->setpad({ tall + 1, 0, tall, tall }) + ->template plugin(" Running application \n" + " LeftClick to activate \n" + " DoubleLeftClick to fly to ") ->flexible() ->drawdots() ->shader(cF, e2::form::state::focus::count, data_src); diff --git a/src/netxs/desktopio/application.hpp b/src/netxs/desktopio/application.hpp index c8c1fa0b9a..623ccd34aa 100644 --- a/src/netxs/desktopio/application.hpp +++ b/src/netxs/desktopio/application.hpp @@ -22,7 +22,7 @@ namespace netxs::app namespace netxs::app::shared { - static const auto version = "v2025.05.19"; + static const auto version = "v2025.05.20"; static const auto repository = "https://github.com/directvt/vtm"; static const auto usr_config = "~/.config/vtm/settings.xml"s; static const auto sys_config = "/etc/vtm/settings.xml"s; diff --git a/src/netxs/desktopio/console.hpp b/src/netxs/desktopio/console.hpp index 33c74604e5..a6a8f98d18 100644 --- a/src/netxs/desktopio/console.hpp +++ b/src/netxs/desktopio/console.hpp @@ -606,13 +606,18 @@ namespace netxs::ui { auto& gear = *gear_ptr; if (gear.mouse_disabled) continue; - if (auto tooltip_page_sptr = gear.tooltip.get_render()) + auto [tooltip_page_sptr, tooltip_offset] = gear.tooltip.get_render_sptr_and_offset(); + if (tooltip_page_sptr) { auto& tooltip_page = *tooltip_page_sptr; - auto full_area = full; - full_area.coor = std::max(dot_00, twod{ gear.coord } - twod{ 4, tooltip_page.size() + 1 }); - full_area.size.x = dot_mx.x; // Prevent line wrapping. - canvas.full(full_area); + auto fs_area = full; + auto page_area = full; + page_area.coor = tooltip_offset + twod{ gear.coord }; + page_area.size = tooltip_page.limits(); + fs_area.size = std::max(dot_00, fs_area.size - page_area.size); + page_area.coor = fs_area.clamp(page_area.coor); + page_area.size.x = dot_mx.x; // Prevent line wrapping. + canvas.full(page_area); canvas.cup(dot_00); canvas.output(tooltip_page, cell::shaders::color(props.tooltip_colors)); } @@ -627,9 +632,10 @@ namespace netxs::ui { auto& gear = *gear_ptr; if (gear.mouse_disabled) continue; - if (auto v = gear.tooltip.get()) + if (auto v = gear.tooltip.get_fresh_qiew()) { - list.thing.push(ext_gear_id, v.value()); + auto tooltip_qiew = v.value(); + list.thing.push(ext_gear_id, tooltip_qiew, props.tooltip_colors.fgc(), props.tooltip_colors.bgc()); } } list.thing.sendby(canal); diff --git a/src/netxs/desktopio/directvt.hpp b/src/netxs/desktopio/directvt.hpp index 4f12b1ef8a..f11ddc037f 100644 --- a/src/netxs/desktopio/directvt.hpp +++ b/src/netxs/desktopio/directvt.hpp @@ -907,7 +907,7 @@ namespace netxs::directvt STRUCT_macro(frame_element, (blob, data)) STRUCT_macro(jgc_element, (ui64, token) (text, cluster)) - STRUCT_macro(tooltip_element, (id_t, gear_id) (text, utf8)) + STRUCT_macro(tooltip_element, (id_t, gear_id) (text, utf8) (argb, fgc) (argb, bgc)) STRUCT_macro(mouse_event, (id_t, gear_id) (si32, ctlstat) (hint, cause) diff --git a/src/netxs/desktopio/gui.hpp b/src/netxs/desktopio/gui.hpp index 107d2efb64..e2284208ce 100644 --- a/src/netxs/desktopio/gui.hpp +++ b/src/netxs/desktopio/gui.hpp @@ -49,6 +49,7 @@ namespace netxs::gui : hdc{}, hWnd{}, prev{ .coor = dot_mx }, + area{ .size = dot_11 }, live{ faux } { } void hide() { live = faux; } @@ -1490,14 +1491,15 @@ namespace netxs::gui struct task { static constexpr auto _counter = 1 + __COUNTER__; - static constexpr auto blink = 1 << (__COUNTER__ - _counter); - static constexpr auto moved = 1 << (__COUNTER__ - _counter); - static constexpr auto sized = 1 << (__COUNTER__ - _counter); - static constexpr auto grips = 1 << (__COUNTER__ - _counter); - static constexpr auto hover = 1 << (__COUNTER__ - _counter); - static constexpr auto inner = 1 << (__COUNTER__ - _counter); - static constexpr auto header = 1 << (__COUNTER__ - _counter); - static constexpr auto footer = 1 << (__COUNTER__ - _counter); + static constexpr auto blink = 1 << (__COUNTER__ - _counter); + static constexpr auto moved = 1 << (__COUNTER__ - _counter); + static constexpr auto sized = 1 << (__COUNTER__ - _counter); + static constexpr auto grips = 1 << (__COUNTER__ - _counter); + static constexpr auto hover = 1 << (__COUNTER__ - _counter); + static constexpr auto inner = 1 << (__COUNTER__ - _counter); + static constexpr auto header = 1 << (__COUNTER__ - _counter); + static constexpr auto footer = 1 << (__COUNTER__ - _counter); + static constexpr auto tooltip = 1 << (__COUNTER__ - _counter); static constexpr auto all = -1; }; struct vkey @@ -1727,6 +1729,18 @@ namespace netxs::gui auto& item = lock.thing; owner.base::riseup(tier::preview, e2::form::prop::ui::footer, item.utf8); } + void handle(s11n::xs::tooltips lock) + { + for (auto& tooltip : lock.thing) + { + gears->tooltip.visible = tooltip.utf8.size(); + if (gears->tooltip.visible) + { + gears->tooltip.set_text(tooltip.utf8, tooltip.fgc, tooltip.bgc); + } + } + owner.update_tooltip(); + } void handle(s11n::xs::clipdata lock) { auto& item = lock.thing; @@ -1740,11 +1754,6 @@ namespace netxs::gui { s11n::recycle_cliprequest(intio, lock); } - void handle(s11n::xs::tooltips lock) - { - auto copy = lock.thing; - //todo implement like as in ui::dtvt - } //todo use xs::screenmode void handle(s11n::xs::fullscrn /*lock*/) { @@ -1879,6 +1888,15 @@ namespace netxs::gui layer blinky; // winbase: Layer for blinking characters. layer header; // winbase: Layer for Header. layer footer; // winbase: Layer for Footer. + layer tooltip_layer; // winbase: Layer for Tooltip. + std::array, 5> layers = + { + master, + blinky, + footer, + header, + tooltip_layer, + }; fonts fcache; // winbase: Font cache. glyph gcache; // winbase: Glyph cache. blink blinks; // winbase: Blinking layer state. @@ -1905,6 +1923,7 @@ namespace netxs::gui twod normcs; // winbase: Cell size for normal mode. face h_grid; // winbase: Header layer cell grid. face f_grid; // winbase: Footer layer cell grid. + face tooltip_grid; // winbase: Tooltip layer cell grid. rect grip_l; // winbase: Resizing grips left segment area. rect grip_r; // winbase: Resizing grips right segment area. rect grip_t; // winbase: Resizing grips top segment area. @@ -2094,16 +2113,35 @@ namespace netxs::gui } void update_header() { - size_title(h_grid, titles.head_page); + page_to_grid(faux, h_grid, titles.head_page, cell::shaders::contrast); sync_pixel_layout(); netxs::set_flag(reload); } void update_footer() { - size_title(f_grid, titles.foot_page); + page_to_grid(faux, f_grid, titles.foot_page, cell::shaders::contrast); sync_pixel_layout(); netxs::set_flag(reload); } + void update_tooltip() + { + auto& tooltip = stream.gears->tooltip; + auto [render_sptr, tooltip_offset] = tooltip.get_render_sptr_and_offset(); + if (render_sptr) + { + auto& tooltip_page = *render_sptr; + auto tooltip_clrs = cell{}.bgc(tooltip.default_bgc).fgc(tooltip.default_fgc); + page_to_grid(true, tooltip_grid, tooltip_page, cell::shaders::color(tooltip_clrs)); + tooltip_layer.area.coor = mcoord + tooltip_offset * cellsz; + tooltip_layer.area.size = tooltip_grid.size() * cellsz; + tooltip_layer.show(); + } + else + { + tooltip_layer.hide(); + } + netxs::set_flag(reload); + } void set_font_list(auto& flist) { log("%%Font list changed: ", prompt::gui, flist); @@ -2112,7 +2150,11 @@ namespace netxs::gui } auto move_window(twod delta) { - for (auto p : { &master, &blinky, &footer, &header }) p->area.coor += delta; + for (auto& l : layers) + { + auto& p = l.get(); + p.area.coor += delta; + } netxs::set_flag(reload); } void drop_grips() @@ -2149,7 +2191,11 @@ namespace netxs::gui } else if (fsmode == winstate::minimized) { - for (auto p : { &master, &blinky, &footer, &header }) p->hide(); + for (auto& l : layers) + { + auto& p = l.get(); + p.hide(); + } } else if (fsmode == winstate::maximized) { @@ -2188,7 +2234,11 @@ namespace netxs::gui { log("%%Set window to minimized state (implicit)", prompt::gui); fsmode = winstate::minimized; - for (auto p : { &master, &blinky, &footer, &header }) p->hide(); + for (auto& l : layers) + { + auto& p = l.get(); + p.hide(); + } } else if (auto delta = coor - master.area.coor) { @@ -2198,6 +2248,12 @@ namespace netxs::gui }); } } + void fit_to_displays(rect& layer_area) + { + auto fs_area = window_get_fs_area(master.area); + fs_area.size = std::max(dot_00, fs_area.size - layer_area.size); + layer_area.coor = fs_area.clamp(layer_area.coor); + } void check_fsmode() { if (fsmode == winstate::undefined) return; @@ -2243,20 +2299,24 @@ namespace netxs::gui } if (fsmode != winstate::minimized) { - for (auto p : { &master, &blinky, &footer, &header }) p->prev.coor = dot_mx; // Windows moves our windows the way it wants, breaking the layout. + for (auto& l : layers) + { + auto& p = l.get(); + p.prev.coor = dot_mx; // Windows moves our windows the way it wants, breaking the layout. + } netxs::set_flag(reload); } update_gui(); }); } - void size_title(ui::face& title_grid, ui::page& title_page) + void page_to_grid(bool update_all, ui::face& target_grid, ui::page& source_page, auto fuse) { auto grid_size = gridsz; - title_grid.calc_page_height(title_page, grid_size); - title_grid.size(grid_size); - title_grid.wipe(); - title_grid.cup(dot_00); - title_grid.output(title_page, cell::shaders::contrast); + target_grid.get_page_size(source_page, grid_size, update_all); + target_grid.size(grid_size); + target_grid.wipe(); + target_grid.cup(dot_00); + target_grid.output(source_page, fuse); } void size_window(twod size_delta = {}) { @@ -2269,8 +2329,8 @@ namespace netxs::gui master.area = blinky.area + border; if (fsmode != winstate::maximized) { - size_title(h_grid, titles.head_page); - size_title(f_grid, titles.foot_page); + page_to_grid(faux, h_grid, titles.head_page, cell::shaders::contrast); + page_to_grid(faux, f_grid, titles.foot_page, cell::shaders::contrast); sync_pixel_layout(); } if (sizechanged) @@ -2453,15 +2513,19 @@ namespace netxs::gui } } } - void draw_title(layer& s, auto& facedata) //todo just output ui::core + void draw_grid(layer& s, auto& facedata, bool apply_contour = true) //todo just output ui::core { auto canvas = layer_get_bits(s, true); fill_grid(canvas, facedata, shadow_dent.corner()); - netxs::misc::contour(canvas); // 1ms + if (apply_contour) + { + netxs::misc::contour(canvas); // 1ms + } s.strike(canvas.area()); } - void draw_header() { draw_title(header, h_grid); } - void draw_footer() { draw_title(footer, f_grid); } + void draw_header() { draw_grid(header, h_grid); } + void draw_footer() { draw_grid(footer, f_grid); } + void draw_tooltip() { draw_grid(tooltip_layer, tooltip_grid, faux); } void check_blinky() { auto changed = std::exchange(blinks.show, !!blinks.poll) != blinks.show; @@ -2520,7 +2584,15 @@ namespace netxs::gui if (what & (task::sized | task::header)) draw_header(); if (what & (task::sized | task::footer)) draw_footer(); } - for (auto p : { &master, &blinky, &footer, &header }) layer_present(*p); + if (what & task::tooltip && tooltip_layer.live) + { + fit_to_displays(tooltip_layer.area); + draw_tooltip(); + } + for (auto& l : layers) + { + layer_present(l); + } } isbusy.exchange(faux); } @@ -3239,7 +3311,8 @@ namespace netxs::gui if (!(layer_create(master, this, wincoord, gridsize, border, cellsz) && layer_create(blinky) && layer_create(header) - && layer_create(footer))) return; + && layer_create(footer) + && layer_create(tooltip_layer))) return; else { auto lock = bell::sync(); @@ -3309,7 +3382,7 @@ namespace netxs::gui window_shutdown(); // Interrupt dispatching. }}; window_message_pump(); - //for (auto p : { &master, &blinky, &footer, &header }) layer_delete(*p); + //for (auto& l : layers) layer_delete(l); stream.intio.shut(); // Close link to server. Interrupt binary reading loop. base::dequeue(); // Clear task queue. winio.join(); @@ -3723,12 +3796,15 @@ namespace netxs::gui } void layer_move_all() { - auto layers = { &master, &blinky, &footer, &header }; auto lock = ::BeginDeferWindowPos((si32)layers.size()); - for (auto p : layers) if (p->prev.coor(p->live ? p->area.coor : p->hidden)) + for (auto& l : layers) { - lock = ::DeferWindowPos(lock, (HWND)p->hWnd, 0, p->prev.coor.x, p->prev.coor.y, 0, 0, SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOOWNERZORDER); - if (!lock) { log("%%DeferWindowPos returns unexpected result: %ec%", prompt::gui, ::GetLastError()); } + auto& p = l.get(); + if (p.prev.coor(p.live ? p.area.coor : p.hidden)) + { + lock = ::DeferWindowPos(lock, (HWND)p.hWnd, 0, p.prev.coor.x, p.prev.coor.y, 0, 0, SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOOWNERZORDER); + if (!lock) { log("%%DeferWindowPos returns unexpected result: %ec%", prompt::gui, ::GetLastError()); } + } } ::EndDeferWindowPos(lock); } @@ -3763,7 +3839,10 @@ namespace netxs::gui { //log("\t", rect{{ update_area.left, update_area.top }, { update_area.right - update_area. left, update_area.bottom - update_area.top }}); auto ok = ::UpdateLayeredWindowIndirect((HWND)s.hWnd, &update_info); - if constexpr (debugmode) if (!ok) log("%%UpdateLayeredWindowIndirect call failed", prompt::gui); + if constexpr (debugmode) if (!ok) + { + log("%%UpdateLayeredWindowIndirect call failed (%%)", prompt::gui, ::GetLastError()); + } }; //static auto clr = 0; clr++; for (auto r : s.sync) @@ -4026,7 +4105,11 @@ namespace netxs::gui ::RemoveMenu(ctxmenu, SC_SIZE, MF_BYCOMMAND); // The first ShowWindow() call ignores SW_SHOW. auto mode = SW_SHOW; - for (auto p : { &master, &blinky, &footer, &header }) ::ShowWindow((HWND)p->hWnd, std::exchange(mode, SW_SHOWNA)); + for (auto& l : layers) + { + auto& p = l.get(); + ::ShowWindow((HWND)p.hWnd, std::exchange(mode, SW_SHOWNA)); + } ::AddClipboardFormatListener((HWND)master.hWnd); // It posts WM_CLIPBOARDUPDATE to sync clipboard anyway. sync_clipboard(); // Clipboard should be in sync at (before) startup. window_make_foreground(); diff --git a/src/netxs/desktopio/input.hpp b/src/netxs/desktopio/input.hpp index f5c301d776..4588988875 100644 --- a/src/netxs/desktopio/input.hpp +++ b/src/netxs/desktopio/input.hpp @@ -340,6 +340,8 @@ namespace netxs::input mouse_list #undef X + static constexpr auto MouseAnyButtonMask = 0xFF00; + #undef mouse_list #undef key_list @@ -1282,7 +1284,7 @@ namespace netxs::input string = utf8; page_sptr.reset(); } - auto get_render() + auto get_render_sptr() { if (!page_sptr) { @@ -1428,7 +1430,22 @@ namespace netxs::input bool changed_visibility = {}; // tooltip: Tooltip changes its visibility. ui32 digest = {}; // tooltip: Digest for tracking current tooltip updates. twod coor = {}; // tooltip: Mouse position when tooltip shown. + argb default_fgc = {}; // tooltip: Default fgc color. + argb default_bgc = {}; // tooltip: Default bgc color. + void set_text(qiew utf8, argb fgc, argb bgc) + { + if (!current_sptr) + { + current_sptr = ptr::shared(utf8); + } + else + { + current_sptr->set(utf8); + } + default_fgc = fgc; + default_bgc = bgc; + } void set(netxs::sptr new_current_sptr = {}) { current_sptr = new_current_sptr; @@ -1439,10 +1456,15 @@ namespace netxs::input boss.base::raw_riseup(tier::mouserelease, input::key::MouseHover, gear); if (!ptr::is_equal(prev_sptr, current_sptr)) { - visible = faux; - changed_visibility = true; canceled = !current_sptr || current_sptr->get().empty(); - if (!canceled) + if (canceled) + { + if (visible) + { + fresh = true; + } + } + else { time_to_run = datetime::now() + timeout; digest = current_sptr->digest; @@ -1451,20 +1473,24 @@ namespace netxs::input { digest = current_sptr->digest; } + visible = faux; + changed_visibility = true; } } - auto get_render() + auto get_render_sptr_and_offset() { if (visible && current_sptr) { - return current_sptr->get_render(); + auto render_sptr = current_sptr->get_render_sptr(); + auto page_offset = -twod{ 4, render_sptr->size() + 1 }; + return std::pair{ render_sptr, page_offset }; } else { - return netxs::sptr{}; + return std::pair{ netxs::sptr{}, dot_00 }; } } - auto get() + auto get_fresh_qiew() { if (fresh) { @@ -1501,6 +1527,7 @@ namespace netxs::input } } else if (deed == input::key::MouseWheel // Hide tooltip on wheeling. + || deed == input::key::MouseLeave // Hide tooltip on mouse leave. || (deed >> 8 == input::key::MouseDown >> 8)) // Hide tooltip on any press. { hide(); @@ -1733,7 +1760,7 @@ namespace netxs::input auto saved_cause = mouse::cause; boss.base::signal(tier_id, mouse::cause, *this); mouse::cause = saved_cause; - auto any_bttn_event = mouse::cause & 0xFF00; // Set button_bits = 0. + auto any_bttn_event = mouse::cause & input::key::MouseAnyButtonMask; // Set button_bits = 0. if (alive && mouse::cause != any_bttn_event) { boss.base::signal(tier_id, any_bttn_event, *this); diff --git a/src/netxs/desktopio/richtext.hpp b/src/netxs/desktopio/richtext.hpp index c7bbc74dde..0f16e69908 100644 --- a/src/netxs/desktopio/richtext.hpp +++ b/src/netxs/desktopio/richtext.hpp @@ -2466,7 +2466,7 @@ namespace netxs::ui textpage.stream(find); } } - auto calc_page_height(page& object, twod& size) + auto get_page_size(page& object, twod& size, bool update_all) { auto cp = dot_00; flow::reset(); @@ -2477,7 +2477,14 @@ namespace netxs::ui }; object.stream(publish); auto& cover = flow::minmax(); - size.y = cover.size.y; + if (update_all) + { + size = cover.size; + } + else + { + size.y = cover.size.y; + } return cp; } // face: Reflow text page on the canvas and hold position diff --git a/src/netxs/desktopio/system.hpp b/src/netxs/desktopio/system.hpp index c064063451..38e10af128 100644 --- a/src/netxs/desktopio/system.hpp +++ b/src/netxs/desktopio/system.hpp @@ -2645,7 +2645,7 @@ namespace netxs::os #endif } - auto execvpe(text cmd, text env) + auto execvpe([[maybe_unused]] text cmd, [[maybe_unused]] text env) { #if defined(_WIN32) #else @@ -2845,7 +2845,7 @@ namespace netxs::os #endif } - void spawn(text cmd, text cwd, text env) + void spawn([[maybe_unused]] text cmd, [[maybe_unused]] text cwd, [[maybe_unused]] text env) { #if defined(_WIN32) #else diff --git a/src/vtm.xml b/src/vtm.xml index 1fca2810f4..4f04c8e70b 100644 --- a/src/vtm.xml +++ b/src/vtm.xml @@ -235,8 +235,8 @@ R"==( - - + + @@ -250,7 +250,7 @@ R"==( - + )=="