diff --git a/examples/UWord/UWord.upp b/examples/UWord/UWord.upp index d4aa47e3d..1d70abb30 100644 --- a/examples/UWord/UWord.upp +++ b/examples/UWord/UWord.upp @@ -12,5 +12,6 @@ file mainconfig "" = "GUI", + "" = "GUI FORCE_CSD", "" = "GUI X11"; diff --git a/reference/CustomTitleBar/main.cpp b/reference/CustomTitleBar/main.cpp index 20285afaa..24add873d 100644 --- a/reference/CustomTitleBar/main.cpp +++ b/reference/CustomTitleBar/main.cpp @@ -3,22 +3,15 @@ using namespace Upp; struct MyApp : TopWindow { - FrameTop bararea; // we represent whole TitleBar area as frame - ParentCtrl barrect; // to do custom caption clipping MenuBar menubar; LineEdit editor; Label title; - void MainMenu(Bar& bar) - { - bar.Sub("File", [=](Bar& bar) { - bar.Add("Exit", [=] { Break(); }); - }); - } - void SetMenuBar() { menubar.Set([=](Bar& bar) { - MainMenu(bar); + bar.Sub("File", [=](Bar& bar) { + bar.Add("Exit", [=] { Break(); }); + }); }); } @@ -34,23 +27,16 @@ struct MyApp : TopWindow { Icon(CtrlImg::new_doc()); int h = menubar.GetStdHeight(); - CustomTitleBar(h); // h is suggested minimum height + Ctrl *custom_bar = CustomTitleBar(SColorFace()); - if(IsCustomTitleBar()) { + if(custom_bar) { menubar.Transparent(); auto cm = GetCustomTitleBarMetrics(); - bararea.Height(cm.height); - AddFrame(bararea); - bararea << barrect.VSizePos().HSizePos(cm.lm, cm.rm); - ImageBuffer m(1, 2); - m[0][0] = Blend(SWhite(), SLtCyan()); - m[0][1] = Blend(SWhite(), SLtMagenta()); - bararea.Background(Image(m)); // simple gradient - barrect << menubar; + *custom_bar << menubar; SetMenuBar(); // run it here to get GetWidth menubar.LeftPos(0, menubar.GetWidth()).TopPos((cm.height - h) / 2, h); - barrect << title.HSizePos(menubar.GetWidth(), cm.rm).VSizePos(); - title.SetLabel("This is CustomTitleBar example"); + *custom_bar << title.HSizePos(menubar.GetWidth(), cm.rm).VSizePos(); + title.SetLabel("\1[g This is [* CustomTitleBar] [/ example]"); title.AlignCenter(); title.AlignVCenter(); } diff --git a/s b/s new file mode 100644 index 000000000..0c75281c5 --- /dev/null +++ b/s @@ -0,0 +1,28 @@ + DarkModeContent +* DisplayPopup3 + NewDisplayPopup + _t + animations + bad_alloc + blocksel + custom_caption + custom_titlebar + diagram_dev + display_popup + ext2 + gui_sizeof + ide_ext + ide_external + ime + layzoom ++ llbm_ide + logs + master + mt_painter + newmouse + next2025_1 + pixelmode + skin + stable + wchar + diff --git a/uppsrc/CtrlCore/CtrlCore.h b/uppsrc/CtrlCore/CtrlCore.h index d188fe02d..c7c31d454 100644 --- a/uppsrc/CtrlCore/CtrlCore.h +++ b/uppsrc/CtrlCore/CtrlCore.h @@ -675,6 +675,8 @@ private: static Point dndpos; static bool dndframe; static PasteClip dndclip; + + static int last_mouse_action; void DnD(Point p, PasteClip& clip); static void DnDRepeat(); @@ -700,8 +702,11 @@ private: Rect GetPreeditScreenRect(); void SyncPreedit(); void ShowPreedit(const WString& text, int cursor = INT_MAX); - static void HidePreedit(); - static void PreeditSync(void (*enable_preedit)(Ctrl *top, bool enable)); + + static void HidePreedit(); + static void PreeditSync(void (*enable_preedit)(Ctrl *top, bool enable)); + + static bool MouseActiveCtrl(Ctrl *w, Point p); // System window interface... void WndShow(bool b); diff --git a/uppsrc/CtrlCore/CtrlCore.iml b/uppsrc/CtrlCore/CtrlCore.iml index 390948ddc..da5bcaf10 100644 --- a/uppsrc/CtrlCore/CtrlCore.iml +++ b/uppsrc/CtrlCore/CtrlCore.iml @@ -11,10 +11,22 @@ IMAGE_ID(DndNone98) IMAGE_ID(DndCopy) IMAGE_ID(DndCopy98) IMAGE_ID(DndData) +IMAGE_ID(GtkBarButton__UHD) +IMAGE_ID(GtkBarButtonH__UHD) +IMAGE_ID(GtkBarClose__UHD) +IMAGE_ID(GtkBarMaximize__UHD) +IMAGE_ID(GtkBarMinimize__UHD) +IMAGE_ID(GtkBarOverlap__UHD) IMAGE_ID(WinClose) IMAGE_ID(WinMinimize) IMAGE_ID(WinMaximize) IMAGE_ID(WinMaximized) +IMAGE_ID(GtkBarButton) +IMAGE_ID(GtkBarButtonH) +IMAGE_ID(GtkBarClose) +IMAGE_ID(GtkBarMaximize) +IMAGE_ID(GtkBarMinimize) +IMAGE_ID(GtkBarOverlap) IMAGE_BEGIN_DATA IMAGE_DATA(120,156,237,150,91,10,194,64,16,4,61,142,231,240,254,119,138,136,54,129,128,178,143,217,157,238,118,10,242,23,72,117) @@ -97,23 +109,111 @@ IMAGE_DATA(32,123,164,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0) IMAGE_END_DATA(1152, 4) IMAGE_BEGIN_DATA -IMAGE_DATA(120,156,237,152,191,106,21,65,20,198,207,35,88,228,1,182,201,35,216,164,178,21,83,104,149,194,62,24,31,32,77,64) -IMAGE_DATA(236,82,72,192,20,86,193,34,104,103,170,96,99,33,232,3,8,130,88,104,17,66,2,17,3,33,66,98,140,70,248,220) -IMAGE_DATA(147,153,235,157,204,206,236,206,122,119,206,68,114,62,248,184,115,103,151,249,125,103,118,119,246,15,85,84,145,35,80,89) -IMAGE_DATA(193,122,218,250,154,52,159,101,51,204,215,158,17,206,128,145,108,134,69,225,12,112,85,32,3,124,9,103,104,240,133,51) -IMAGE_DATA(4,249,130,25,162,124,161,12,173,124,129,12,157,252,204,25,186,152,35,111,214,126,92,251,78,237,169,92,124,106,174,71) -IMAGE_DATA(204,93,175,253,136,242,172,143,141,122,3,124,102,223,165,60,247,7,159,181,25,200,192,252,91,25,216,68,227,227,187,68) -IMAGE_DATA(102,158,23,3,124,238,231,185,159,206,196,191,94,251,62,141,207,239,42,144,193,157,3,255,220,12,249,166,29,39,69,92) -IMAGE_DATA(215,13,186,120,110,181,205,1,86,55,128,123,43,192,220,67,224,246,146,49,183,185,207,238,63,107,51,164,136,153,83,212) -IMAGE_DATA(60,190,177,57,192,219,247,192,218,75,96,249,185,217,246,224,169,105,115,159,221,247,73,237,133,68,126,76,177,57,192,167) -IMAGE_DATA(29,224,245,59,224,197,27,179,237,217,43,211,230,190,1,249,68,227,227,185,238,242,191,28,52,214,167,115,127,216,26,156) -IMAGE_DATA(207,199,100,198,114,71,235,15,190,29,3,187,251,192,231,93,195,251,184,109,218,220,55,48,127,148,193,125,62,197,201,79) -IMAGE_DATA(224,240,8,248,122,104,120,123,7,166,205,125,25,248,190,112,246,27,56,253,5,124,63,5,142,127,24,115,155,251,36,248) -IMAGE_DATA(9,206,201,231,235,122,214,50,98,238,115,253,247,85,101,199,94,104,113,159,245,79,165,42,163,75,250,49,160,24,223,185) -IMAGE_DATA(217,22,227,23,204,16,122,1,41,198,47,144,161,193,23,206,16,228,11,102,136,242,133,50,180,242,5,50,116,242,51,103) -IMAGE_DATA(232,98,138,214,79,242,235,81,163,222,18,124,143,37,153,33,196,144,230,7,251,91,50,164,188,12,76,156,171,141,159,241) -IMAGE_DATA(90,141,158,139,161,126,255,154,241,247,233,155,197,103,197,198,236,226,123,191,255,82,255,223,255,129,26,46,100,114,237,214) -IMAGE_DATA(60,73,253,41,25,59,230,63,84,75,106,132,65,248,82,245,251,34,129,250,19,156,179,254,62,195,229,168,63,121,32,250) -IMAGE_DATA(127,235,31,236,183,47,60,67,6,85,76,149,126,176,82,169,84,42,149,74,165,114,117,213,31,134,7,123,128,158,164,254) -IMAGE_DATA(33,248,164,245,107,253,90,191,126,12,80,69,245,7,245,218,88,188,0,0,0,0,0,0,0,0,0,0,0,0,0,0) -IMAGE_END_DATA(608, 3) +IMAGE_DATA(120,156,237,154,63,107,20,81,20,197,223,71,176,240,3,108,99,99,111,147,202,86,180,208,202,194,94,140,31,32,8,130) +IMAGE_DATA(164,179,16,65,11,43,177,16,237,180,10,54,22,130,126,0,65,16,11,45,68,12,81,20,130,130,241,191,112,221,231,204) +IMAGE_DATA(184,147,217,55,51,111,118,222,223,51,231,192,33,147,201,50,231,222,251,219,59,187,217,68,205,212,76,213,36,42,174,164) +IMAGE_DATA(244,161,210,7,66,231,107,149,53,156,157,123,45,112,13,82,169,172,97,35,112,13,82,87,132,26,164,169,192,53,44,229) +IMAGE_DATA(7,174,193,152,31,176,134,214,252,64,53,116,230,7,168,161,55,223,115,13,125,153,149,183,230,190,54,247,169,185,15,250) +IMAGE_DATA(202,87,203,247,35,157,123,123,238,43,202,207,253,113,169,95,67,190,206,62,163,252,188,62,52,179,182,12,53,232,252,227) +IMAGE_DATA(30,178,149,90,240,189,168,138,57,111,24,242,245,121,61,251,67,158,242,143,204,125,94,45,158,223,51,67,13,245,25,52) +IMAGE_DATA(159,155,38,31,43,175,99,35,221,215,81,181,255,185,213,53,3,185,126,95,228,220,85,145,211,155,34,39,47,22,214,199) +IMAGE_DATA(250,92,249,248,19,101,13,54,210,153,7,213,50,223,182,25,200,147,103,34,55,31,136,92,190,91,252,236,210,173,226,88) +IMAGE_DATA(159,43,31,123,99,238,117,203,252,54,181,205,64,94,190,21,121,244,84,228,222,227,226,103,119,30,22,199,250,156,195,124) +IMAGE_DATA(165,22,60,111,215,243,223,239,46,221,159,254,249,249,107,231,249,154,201,90,153,91,221,127,228,243,158,200,246,71,145,87) +IMAGE_DATA(219,69,222,139,55,197,177,62,231,56,191,170,161,254,254,84,190,253,20,249,244,69,228,195,167,34,239,221,110,113,172,207) +IMAGE_DATA(121,200,111,74,126,255,17,249,241,75,228,235,15,145,189,239,133,245,177,62,23,34,223,194,62,243,245,94,159,40,51,218) +IMAGE_DATA(60,100,255,135,106,86,94,123,189,195,67,238,127,20,21,71,137,126,24,16,45,191,246,98,27,45,63,98,13,166,95,64) +IMAGE_DATA(162,229,71,168,97,41,63,112,13,198,252,128,53,180,230,7,170,161,51,63,64,13,189,249,158,107,232,203,12,218,191,10) +IMAGE_DATA(127,63,90,234,55,70,126,35,43,100,13,166,140,208,249,198,243,29,53,216,252,50,48,186,174,174,124,143,187,218,250,92) +IMAGE_DATA(52,157,111,238,76,243,49,67,107,105,102,181,93,179,47,191,241,117,149,254,255,127,111,232,97,95,77,117,215,123,30,211) +IMAGE_DATA(191,77,141,61,243,55,245,98,91,130,147,252,80,253,55,165,2,244,111,97,159,253,15,185,156,143,254,173,47,164,242,237) +IMAGE_DATA(223,217,215,161,225,30,106,160,218,52,227,7,86,20,69,81,20,69,81,117,77,253,205,176,179,55,208,99,250,119,145,175) +IMAGE_DATA(216,63,251,103,255,252,48,128,106,213,225,11,234,66,236,26,188,105,115,115,83,198,56,118,253,212,66,99,89,146,121,90) +IMAGE_DATA(10,197,147,188,195,105,232,236,119,118,118,70,153,172,253,43,20,75,215,204,99,207,45,117,165,194,115,44,239,216,115,76) +IMAGE_DATA(77,57,48,93,133,117,236,185,198,86,206,92,201,185,93,72,92,201,121,191,80,185,218,114,142,61,127,95,154,10,215,41) +IMAGE_DATA(114,158,34,87,27,206,177,185,140,21,185,226,114,38,91,92,198,100,139,203,152,92,199,113,142,205,175,75,100,139,203,152) +IMAGE_DATA(108,113,25,147,45,46,99,178,197,101,76,182,184,140,201,22,151,49,217,226,50,38,91,108,198,100,27,151,113,40,182,228) +IMAGE_DATA(27,135,175,47,198,100,139,205,152,108,227,219,23,95,178,77,199,174,25,243,190,156,150,93,223,167,201,54,61,187,226,203) +IMAGE_DATA(221,77,211,174,118,152,108,211,245,88,190,220,221,180,61,118,135,201,54,125,175,202,151,187,155,135,87,221,97,178,205,199) +IMAGE_DATA(67,249,114,119,243,242,208,29,38,219,252,76,190,216,182,229,75,182,249,218,134,49,249,230,107,242,197,118,31,95,178,205) +IMAGE_DATA(223,93,140,201,55,127,147,47,182,201,23,219,109,124,201,22,199,38,198,228,139,99,242,197,54,249,98,155,124,177,221,228) +IMAGE_DATA(75,182,120,110,254,205,144,124,177,76,190,216,38,95,108,147,47,182,201,23,219,228,139,109,242,197,54,249,98,155,124,177) +IMAGE_DATA(77,190,216,38,95,108,147,47,182,201,23,219,228,139,109,242,197,54,255,6,140,107,254,255,6,182,201,23,219,228,139,109) +IMAGE_DATA(242,197,182,137,47,25,99,184,141,45,249,98,152,124,177,77,190,216,238,226,75,198,121,187,143,45,249,230,109,242,197,182) +IMAGE_DATA(13,95,50,206,211,182,108,201,55,79,175,202,151,140,211,119,147,87,31,95,238,112,94,30,202,150,59,156,143,87,217,93) +IMAGE_DATA(238,112,62,94,149,45,119,56,125,143,217,93,238,112,250,30,203,150,59,156,174,93,236,46,119,56,93,187,98,203,29,78) +IMAGE_DATA(207,46,119,151,59,156,158,93,179,37,227,116,236,139,109,147,47,25,199,101,235,131,47,25,99,179,53,49,142,221,247,84) +IMAGE_DATA(28,138,109,147,47,25,135,101,27,130,47,25,99,179,37,99,124,182,100,140,207,150,140,241,217,86,34,99,92,182,149,200) +IMAGE_DATA(24,151,109,37,50,198,101,91,137,140,113,217,86,106,214,76,206,253,92,115,97,91,137,140,113,217,86,34,99,92,182,117) +IMAGE_DATA(145,51,38,215,186,76,253,77,129,115,91,223,177,121,248,210,84,56,79,141,107,93,109,189,35,112,238,234,45,246,220,67) +IMAGE_DATA(11,137,51,185,182,43,103,206,228,106,175,174,89,165,196,186,175,206,216,115,76,93,125,243,11,205,219,182,158,216,115,203) +IMAGE_DATA(81,182,179,117,197,124,104,94,236,249,32,105,232,236,125,57,246,28,166,34,242,156,158,200,114,191,254,2,145,219,214,121) +IMAGE_END_DATA(1248, 4) + +IMAGE_BEGIN_DATA +IMAGE_DATA(120,156,237,218,65,78,195,64,16,5,81,206,195,9,89,230,138,57,17,172,44,18,43,182,199,158,177,187,187,92,95,170) +IMAGE_DATA(53,201,60,53,27,248,254,249,250,249,162,238,241,120,252,246,20,253,249,221,255,122,45,53,207,181,171,60,245,190,110,123) +IMAGE_DATA(223,254,249,124,118,165,245,249,187,202,114,180,121,244,187,101,95,22,207,94,239,232,119,204,182,10,166,71,172,163,223,53) +IMAGE_DATA(122,149,93,117,94,30,201,85,231,247,81,93,91,157,163,223,255,172,221,197,245,142,206,119,116,109,113,142,118,233,157,174) +IMAGE_DATA(92,103,109,185,198,218,114,141,117,237,115,142,246,91,155,182,92,99,109,185,198,218,114,141,181,229,26,107,203,53,214,150) +IMAGE_DATA(107,172,45,215,88,91,182,177,182,177,198,87,217,234,27,227,123,150,177,182,108,99,109,227,59,203,87,219,60,141,54,246) +IMAGE_DATA(247,114,174,70,255,158,214,54,95,163,124,189,221,156,141,186,97,109,243,214,235,235,237,230,174,247,134,181,205,223,81,95) +IMAGE_DATA(111,183,70,71,111,88,219,58,237,245,245,118,107,181,247,134,181,173,151,190,236,90,125,181,173,91,139,177,190,117,211,151) +IMAGE_DATA(221,150,175,182,245,91,51,214,183,126,250,178,211,151,221,146,175,182,156,62,25,235,203,73,95,118,250,178,211,151,221,220) +IMAGE_DATA(87,91,94,243,191,25,234,203,74,95,118,250,178,211,151,157,190,236,244,101,167,47,59,125,217,233,203,78,95,118,250,178) +IMAGE_DATA(211,151,157,190,236,244,101,167,47,59,255,6,204,205,255,223,96,167,47,59,125,217,233,203,238,147,175,198,140,150,108,245) +IMAGE_DATA(101,164,47,59,125,217,173,249,106,92,187,45,91,125,107,167,47,187,22,95,141,107,214,106,171,111,205,142,250,106,156,191) +IMAGE_DATA(185,215,150,175,55,92,171,189,182,222,112,157,142,220,174,55,92,167,163,182,222,112,254,122,110,215,27,206,95,175,173,55) +IMAGE_DATA(156,183,17,183,235,13,231,109,148,173,55,156,175,145,183,235,13,231,107,180,173,198,121,58,203,118,238,171,113,172,237,25) +IMAGE_DATA(190,26,179,109,63,25,71,127,239,187,116,149,237,220,87,227,107,109,175,240,213,152,109,171,49,223,86,99,190,173,198,124) +IMAGE_DATA(219,105,26,115,109,167,105,204,181,157,166,49,215,118,154,198,92,219,105,243,207,172,243,182,107,21,219,105,26,115,109,167) +IMAGE_DATA(105,204,181,125,157,206,76,215,215,125,250,126,119,112,94,250,222,209,30,103,237,46,206,119,115,125,221,210,119,39,56,175) +IMAGE_DATA(125,183,232,119,191,122,36,103,93,151,87,217,89,215,246,173,189,85,38,235,173,207,25,253,142,217,183,245,126,87,123,183) +IMAGE_DATA(126,158,232,119,171,184,214,183,29,101,190,247,231,69,191,15,105,123,223,254,172,162,223,225,46,211,243,126,211,242,125,127) +IMAGE_DATA(247,87,34,215,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0) +IMAGE_END_DATA(640, 1) + +IMAGE_BEGIN_DATA +IMAGE_DATA(120,156,237,216,75,10,194,80,16,68,81,215,227,10,221,125,156,58,8,226,39,239,117,117,115,14,100,222,212,133,16,189) +IMAGE_DATA(63,110,143,27,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,176,220,81,125,192,137,196,155,58,58,94) +IMAGE_DATA(158,20,137,55,117,116,156,60,213,18,111,234,232,108,199,234,61,19,111,234,44,105,207,164,91,38,73,216,53,225,134,201) +IMAGE_DATA(42,247,213,118,143,138,157,181,221,107,231,222,218,214,216,177,187,182,181,86,238,175,109,134,21,29,180,205,114,101,15,109) +IMAGE_DATA(51,93,209,69,219,108,255,244,209,182,135,95,58,105,219,203,55,189,180,237,233,147,110,218,246,246,174,159,182,51,104,59) +IMAGE_DATA(159,182,243,105,59,155,190,115,121,63,207,229,251,106,46,191,143,230,242,255,198,92,223,116,211,184,151,95,122,105,220,195) +IMAGE_DATA(63,157,52,206,118,69,31,141,51,93,217,69,227,44,43,122,104,156,97,101,7,141,107,237,216,95,227,26,59,119,215,120) +IMAGE_DATA(175,138,189,53,222,163,114,103,141,215,74,216,55,225,134,137,146,118,77,186,101,130,196,61,19,111,234,44,113,199,196,155) +IMAGE_DATA(58,75,220,49,241,166,206,18,119,76,188,9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,70,120,2) +IMAGE_DATA(156,202,31,25,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0) +IMAGE_END_DATA(320, 1) + +IMAGE_BEGIN_DATA +IMAGE_DATA(120,156,237,209,193,9,2,64,16,4,65,227,49,66,179,215,8,244,119,220,180,84,193,254,135,222,231,235,241,122,0,0) +IMAGE_DATA(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,147,222,241,227,183,219,255,241,223,179,110,255,199,127,207,186,253) +IMAGE_DATA(31,255,61,235,246,127,252,247,172,219,255,241,223,179,42,221,42,59,215,84,186,85,118,174,169,116,171,236,92,83,233,86) +IMAGE_DATA(217,185,166,210,173,178,115,77,165,91,101,231,154,74,183,202,206,53,149,110,149,157,107,42,221,42,59,215,84,186,85,118) +IMAGE_DATA(174,169,116,171,236,92,83,233,86,217,185,166,210,173,178,115,77,165,91,101,231,154,74,183,202,206,53,149,110,149,157,107) +IMAGE_DATA(42,221,42,59,215,84,186,85,118,174,169,116,171,236,92,83,233,86,217,185,166,210,173,178,115,77,165,91,101,231,154,74) +IMAGE_DATA(183,202,206,53,149,110,149,157,107,42,221,42,59,215,84,186,85,118,174,169,116,171,236,92,83,233,86,217,185,166,210,173) +IMAGE_DATA(178,115,77,165,91,101,231,154,74,183,202,206,53,149,110,149,157,107,42,221,42,59,215,84,186,85,118,174,169,116,171,236) +IMAGE_DATA(92,83,233,86,217,185,230,91,183,202,241,219,237,255,248,239,89,183,255,227,191,103,221,254,143,255,158,117,251,63,254,123) +IMAGE_DATA(214,237,255,248,47,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,252,137,15,205,212,237,88,0,0,0,0) +IMAGE_END_DATA(320, 1) + +IMAGE_BEGIN_DATA +IMAGE_DATA(120,156,237,209,65,13,0,32,12,4,65,244,160,176,238,193,1,191,230,82,50,147,172,130,221,181,106,1,0,0,0,0) +IMAGE_DATA(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,48) +IMAGE_DATA(214,25,30,111,233,63,254,246,74,255,241,183,87,250,143,191,189,210,127,252,237,149,254,227,47,0,0,0,0,0,0,0) +IMAGE_DATA(0,0,0,0,0,0,0,0,0,124,226,2,64,145,32,10,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0) +IMAGE_END_DATA(128, 1) + +IMAGE_BEGIN_DATA +IMAGE_DATA(120,156,237,209,193,9,2,81,16,68,193,141,199,8,61,110,230,107,2,202,162,48,244,255,109,21,204,125,232,247,120,30) +IMAGE_DATA(207,3,0,0,0,0,0,0,0,0,0,0,0,0,0,32,237,60,207,107,183,75,111,182,147,116,43,125,103,165,91,233) +IMAGE_DATA(59,43,221,74,223,89,233,86,250,206,74,183,210,247,127,233,219,77,223,110,250,118,211,183,155,190,221,244,189,117,109,114) +IMAGE_DATA(111,233,123,43,221,77,223,89,233,110,250,206,74,119,211,119,86,186,155,190,179,210,221,244,157,245,213,158,171,253,163,239) +IMAGE_DATA(45,125,187,233,219,77,223,110,250,118,211,183,155,190,221,244,237,166,111,55,125,187,233,219,77,223,110,250,118,211,183,155) +IMAGE_DATA(190,221,244,237,166,111,55,125,187,233,219,77,223,110,250,118,211,183,155,190,221,244,237,166,111,55,125,187,233,219,109,235) +IMAGE_DATA(190,220,90,109,207,213,254,217,221,106,123,174,246,207,238,62,237,185,218,241,155,116,55,125,103,165,187,233,59,43,221,77) +IMAGE_DATA(223,89,233,110,250,206,74,119,211,23,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,10,189,0,154,158,72) +IMAGE_DATA(59,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0) +IMAGE_END_DATA(288, 1) diff --git a/uppsrc/CtrlCore/CtrlCore.upp b/uppsrc/CtrlCore/CtrlCore.upp index 3d60540e7..8ffe5113b 100644 --- a/uppsrc/CtrlCore/CtrlCore.upp +++ b/uppsrc/CtrlCore/CtrlCore.upp @@ -97,12 +97,12 @@ file GtkCtrl.h, GtkCtrl.cpp, GtkCapture.cpp, - GtkCSD.cpp, GtkWnd.cpp, GtkCreate.cpp, GtkEvent.cpp, GtkTop.h, GtkTop.cpp, + GtkCustomBar.cpp, GtkClip.cpp, GtkDnD.cpp, GtkApp.cpp, diff --git a/uppsrc/CtrlCore/CtrlMouse.cpp b/uppsrc/CtrlCore/CtrlMouse.cpp index 42a3d27ca..80da93945 100644 --- a/uppsrc/CtrlCore/CtrlMouse.cpp +++ b/uppsrc/CtrlCore/CtrlMouse.cpp @@ -21,6 +21,8 @@ Point Ctrl::middlemousepos = Null; PenInfo Ctrl::pen; bool Ctrl::is_pen_event; +int Ctrl::last_mouse_action; + dword GetMouseFlags() { dword style = 0; if(GetAlt()) style |= K_ALT; @@ -67,6 +69,7 @@ void Ctrl::LogMouseEvent(const char *f, const Ctrl *ctrl, int event, Point p, in Image Ctrl::FrameMouseEventH(int event, Point p, int zdelta, dword keyflags) { GuiLock __; + last_mouse_action = event; Ptr this_ = this; for(int i = 0; i < mousehook().GetCount(); i++) if(this_ && (*mousehook()[i])(this, true, event, p, zdelta, keyflags)) @@ -104,6 +107,7 @@ Image Ctrl::MouseEvent0(int event, Point p, int zdelta, dword keyflags) Image Ctrl::MouseEventH(int event, Point p, int zdelta, dword keyflags) { GuiLock __; + last_mouse_action = event; Ptr this_ = this; for(int i = 0; i < mousehook().GetCount(); i++) if(this_ && (*mousehook()[i])(this, false, event, p, zdelta, keyflags)) diff --git a/uppsrc/CtrlCore/CtrlPos.cpp b/uppsrc/CtrlCore/CtrlPos.cpp index 945d52a93..3b37454d9 100644 --- a/uppsrc/CtrlCore/CtrlPos.cpp +++ b/uppsrc/CtrlCore/CtrlPos.cpp @@ -154,7 +154,7 @@ void Ctrl::SyncLayout(int force) GuiLock __; if(destroying) return; - LLOG("SyncLayout " << Name() << " size: " << GetSize()); + LLOG("SyncLayout " << Name() << " size: " << GetSize() << " force: " << force); fullrefresh = false; bool refresh = false; Rect oview = GetView(); @@ -174,7 +174,6 @@ void Ctrl::SyncLayout(int force) if(oview.Size() != view.Size() || force > 1) { for(Ctrl& q : *this) { q.rect = q.CalcRect(rect, view); - LLOG("Layout set rect " << q.Name() << " " << q.rect); q.SyncLayout(force > 1 ? force : 0); } Refresh(); @@ -264,10 +263,11 @@ void Ctrl::UpdateRect0(bool sync) Rect pwa = GetPrimaryWorkArea(); rect = OffsetMegaRect(CalcRect(pwa, pwa)); } - LLOG("UpdateRect0 " << Name() << " to " << rect); + + LLOG("UpdateRect0 " << Name() << " to " << rect << ", sync: " << sync); LTIMING("UpdateRect0 SyncLayout"); if(sync) - SyncLayout(); + SyncLayout(sync); } diff --git a/uppsrc/CtrlCore/EncodeRTF.cpp b/uppsrc/CtrlCore/EncodeRTF.cpp index 7236c101a..440840728 100644 --- a/uppsrc/CtrlCore/EncodeRTF.cpp +++ b/uppsrc/CtrlCore/EncodeRTF.cpp @@ -41,7 +41,7 @@ private: void PutTxt(const RichTxt& txt, int nesting, int dot_width); void PutTable(const RichTable& table, int nesting, int dot_width); void PutParts(const Array& parts, - RichPara::CharFormat& base, int bpart, int boff, int epart, int eoff); + RichPara::CharFormat& base, int bpart, int boff, int epart, int eoff); void Begin() { stream.Put('{'); } void Begin(const char *cmd) { Begin(); Command(cmd); } diff --git a/uppsrc/CtrlCore/Gtk.h b/uppsrc/CtrlCore/Gtk.h index b0c3c017c..850fca07b 100644 --- a/uppsrc/CtrlCore/Gtk.h +++ b/uppsrc/CtrlCore/Gtk.h @@ -163,25 +163,6 @@ public: ~ImageGdk(); }; -class GtkCSD final : Rect { // wayland client side decoration handling - bool enabled = false; - -public: - static bool IsSSDSupported(); - - void Create(GdkWindowTypeHint hint); - - bool IsEnabled() const { return enabled; } - - int ExtraWidth() const { return left + right; } - int ExtraHeight() const { return top + bottom; } - - int LeftMargin() const { return left; } - int RightMargin() const { return right; } - int TopMargin() const { return top; } - int BottomMargin() const { return bottom; } -}; - String FilesClipFromUrisFree(gchar **uris); String ImageClipFromPixbufUnref(GdkPixbuf *pixbuf); @@ -200,13 +181,23 @@ Vector GetPropertyInts(GdkWindow *w, const char *property); #define GUIPLATFORM_CTRL_TOP_DECLS \ GtkWidget *window; \ GtkWidget *header = nullptr; \ - GtkWidget *drawing_area = nullptr; \ + GtkWidget *client = nullptr; \ + GtkWidget *header_area = nullptr; \ + Ptr hdr_screen[2]; \ GtkIMContext *im_context = nullptr; \ GtkIMContext *im_context_simple; \ GtkIMContext *im_context_multi; \ - GtkCSD csd; \ int64 cursor_id; \ int id; \ + bool csd = false; \ + bool sync_rect = true; \ + Rect client_rect; \ + Rect header_rect; \ + Rect screen_rect; \ + String bar_css_class; \ + Color bar_color; \ + int bar_cy; \ + bool draw_after_configure = false; \ #define GUIPLATFORM_CTRL_DECLS_INCLUDE diff --git a/uppsrc/CtrlCore/GtkApp.cpp b/uppsrc/CtrlCore/GtkApp.cpp index 82ed9aa26..7fd413e06 100644 --- a/uppsrc/CtrlCore/GtkApp.cpp +++ b/uppsrc/CtrlCore/GtkApp.cpp @@ -97,6 +97,10 @@ void Ctrl::UseWayland() sUseWayland = true; } +extern Event custom_titlebar_metrics__; +extern Function is_custom_titlebar__; +extern Function custom_titlebar_make__; + bool InitGtkApp(int argc, char **argv, const char **envptr) { LLOG(rmsecs() << " InitGtkApp"); @@ -154,6 +158,8 @@ bool InitGtkApp(int argc, char **argv, const char **envptr) g_signal_connect_swapped(settings, "notify::gtk-application-prefer-dark-theme", G_CALLBACK(Ctrl::ThemeChanged), NULL); } + TopWindow::Init(); + return true; } diff --git a/uppsrc/CtrlCore/GtkCSD.cpp b/uppsrc/CtrlCore/GtkCSD.cpp deleted file mode 100644 index 3bf885259..000000000 --- a/uppsrc/CtrlCore/GtkCSD.cpp +++ /dev/null @@ -1,76 +0,0 @@ -#include - -#ifdef GUI_GTK - -namespace Upp { - -bool GtkCSD::IsSSDSupported() -{ - if (Ctrl::IsX11()) { - return false; - } - - // TODO: Rewrite to negotiate with window manager once XDG decoration protocol will be - // stable. (https://wayland.app/protocols/xdg-decoration-unstable-v1) - - // NOTE: Server side decoration are optional. It might be supported by Window manager or - // not. Let's have a list of desktop environments on which we tested it works correctly. - auto desktop = GetEnv("XDG_SESSION_DESKTOP"); - if (desktop == "KDE") { - return true; - //return true; - } - - return false; -} - -void GtkCSD::Create(GdkWindowTypeHint hint) -{ - enabled = false; - left = right = top = bottom = 0; - if(!Ctrl::IsWayland()) - return; - if(IsSSDSupported() && hint != GDK_WINDOW_TYPE_HINT_POPUP_MENU || - hint == GDK_WINDOW_TYPE_HINT_COMBO) - return; - - enabled = true; - GtkWidget* win = gtk_window_new(GTK_WINDOW_TOPLEVEL); - GtkWidget* header; - if (findarg(hint, GDK_WINDOW_TYPE_HINT_POPUP_MENU) >= 0) { - header = gtk_drawing_area_new(); - gtk_widget_set_size_request(header, 1, 1); - gtk_window_set_titlebar((GtkWindow *)win, header); - } else { - header = gtk_header_bar_new(); - gtk_header_bar_set_show_close_button(GTK_HEADER_BAR(header), TRUE); - gtk_window_set_titlebar((GtkWindow *)win, header); - } - GtkWidget* drawing_area = gtk_drawing_area_new(); - gtk_container_add(GTK_CONTAINER(win), drawing_area); - gtk_widget_show_all(win); - - gdk_window_get_origin(gtk_widget_get_window(drawing_area), &left, &top); - - gint drawing_area_width = gtk_widget_get_allocated_width(drawing_area); - gint drawing_area_height = gtk_widget_get_allocated_height(drawing_area); - - gint win_width = gtk_widget_get_allocated_width(win); - gint win_height = gtk_widget_get_allocated_height(win); - - right = win_width - left - drawing_area_width; - bottom = win_height - top - drawing_area_height; - - left = Ctrl::SCL(left); - right = Ctrl::SCL(right); - bottom = Ctrl::SCL(bottom); - top = Ctrl::SCL(top); - - gtk_widget_destroy(drawing_area); - gtk_widget_destroy(header); - gtk_widget_destroy(win); -} - -}; - -#endif diff --git a/uppsrc/CtrlCore/GtkCapture.cpp b/uppsrc/CtrlCore/GtkCapture.cpp index a07cca91b..61181dbc2 100644 --- a/uppsrc/CtrlCore/GtkCapture.cpp +++ b/uppsrc/CtrlCore/GtkCapture.cpp @@ -25,13 +25,20 @@ GdkDevice *Ctrl::GetMouseDevice() #endif } +// (*) it is not really possible to do system-wide grab as there is conflict with csd +// so we depend on gtk doing automatic grab when mouse is down + bool Ctrl::MouseIsGrabbed() { + if(grabwindow && grabwindow->top && grabwindow->utop->csd) // (*) + return true; return gdk_display_device_is_grabbed(gdk_display_get_default(), GetMouseDevice()); } bool Ctrl::GrabMouse() { + if(top && utop->csd) // (*) + return true; return #if GTK_CHECK_VERSION(3, 20, 0) gdk_seat_grab(GetSeat(), gdk(), GDK_SEAT_CAPABILITY_ALL_POINTING, true, NULL, NULL, NULL, 0) @@ -43,10 +50,12 @@ bool Ctrl::GrabMouse() void Ctrl::UngrabMouse() { + if(grabwindow && grabwindow->top && grabwindow->utop->csd) // (*) + return; #if GTK_CHECK_VERSION(3, 20, 0) - gdk_seat_ungrab(GetSeat()); + gdk_seat_ungrab(GetSeat()); #else - gdk_device_ungrab(GetMouseDevice(), GDK_CURRENT_TIME); + gdk_device_ungrab(GetMouseDevice(), GDK_CURRENT_TIME); #endif } @@ -92,7 +101,6 @@ bool Ctrl::ReleaseWndCapture0() { GuiLock __; ASSERT(IsMainThread()); - LLOG("ReleaseWndCapture"); if(grabwindow) { UngrabMouse(); grabwindow = NULL; diff --git a/uppsrc/CtrlCore/GtkCreate.cpp b/uppsrc/CtrlCore/GtkCreate.cpp index 229c4b688..a935da7e3 100644 --- a/uppsrc/CtrlCore/GtkCreate.cpp +++ b/uppsrc/CtrlCore/GtkCreate.cpp @@ -6,6 +6,46 @@ namespace Upp { #define LLOG(x) // DLOG(x) +void Ctrl::SetCustomBarColor(Color c) +{ + GuiLock __; + + Top *top = GetTop(); + if(!top || !top->header) + return; + + String new_css_class; + + static VectorMap providers; + + int q = providers.Find(c); + auto ClassName = [&] { return "upp_custom_titlebar_" + AsString(q); }; + if(q < 0) { + if(providers.GetCount() > 64) + Panic("Too many custom bar colors !"); + q = providers.GetCount(); + String css; + css << "." << ClassName() << " { background-image: none; background-color: " + << Format("#%02x%02x%02x;", c.GetR(), c.GetG(), c.GetB()) << "; }"; + GtkCssProvider *provider = gtk_css_provider_new(); + gtk_css_provider_load_from_data (provider, css, -1, NULL); + gtk_style_context_add_provider_for_screen(gtk_widget_get_screen(top->window), + GTK_STYLE_PROVIDER (provider), + GTK_STYLE_PROVIDER_PRIORITY_USER); + providers.Add(c, provider); + } + new_css_class = ClassName(); + + if(top->bar_css_class == new_css_class) + return; + + if(top->bar_css_class.GetCount()) + gtk_style_context_remove_class (gtk_widget_get_style_context (top->header), ~top->bar_css_class); + + top->bar_css_class = new_css_class; + gtk_style_context_add_class (gtk_widget_get_style_context (top->header), new_css_class); +} + void Ctrl::Create(Ctrl *owner, bool popup) { MemoryIgnoreLeaksBlock ___; @@ -14,7 +54,7 @@ void Ctrl::Create(Ctrl *owner, bool popup) LLOG("Create " << Name() << " " << GetRect()); ASSERT(!IsChild() && !IsOpen()); LLOG("Ungrab1"); - + Top *top = new Top; SetTop(top); top->window = gtk_window_new(popup && owner ? GTK_WINDOW_POPUP : GTK_WINDOW_TOPLEVEL); @@ -28,7 +68,6 @@ void Ctrl::Create(Ctrl *owner, bool popup) w.ctrl = this; w.gtk = top->window; w.gdk = nullptr; - w.drawing_area = nullptr; TopWindow *tw = dynamic_cast(this); GdkWindowTypeHint type_hint; @@ -45,58 +84,79 @@ void Ctrl::Create(Ctrl *owner, bool popup) gtk_window_set_type_hint(gtk(), type_hint); Rect r = GetRect(); - - top->csd.Create(type_hint); - if(top->csd.IsEnabled()) { - top->header = gtk_header_bar_new(); - if (findarg(type_hint, GDK_WINDOW_TYPE_HINT_POPUP_MENU) >= 0) - gtk_widget_set_size_request(top->header, 1, 1); - else - gtk_header_bar_set_show_close_button(GTK_HEADER_BAR(top->header), TRUE); + bool custom_bar = tw && tw->custom_bar; + static bool need_csd = IsWayland() && GetEnv("XDG_SESSION_DESKTOP") != "KDE"; + top->csd = !popup && (need_csd || custom_bar); +#ifndef flagFORCE_CSD // Force using client side decorations even when server side is available + if(tw && tw->force_csd) +#endif + top->csd = !popup; + if(top->csd) { + ONCELOCK { + UpdateWindowDecorationsGeometry(); + } + top->client = gtk_drawing_area_new(); + if(custom_bar) { + top->header = gtk_event_box_new (); + top->header_area = gtk_drawing_area_new(); + gtk_widget_set_size_request(top->header_area, -1, LSCH(tw->GetCustomTitleBarMetrics().height)); + gtk_container_add (GTK_CONTAINER (top->header), top->header_area); + gtk_widget_show_all(top->header); + + SetCustomBarColor(tw->custom_titlebar_bk); + } + else { + top->header = gtk_header_bar_new(); + if(findarg(type_hint, GDK_WINDOW_TYPE_HINT_POPUP_MENU) >= 0) + gtk_widget_set_size_request(top->header, 1, 1); + else + gtk_header_bar_set_show_close_button(GTK_HEADER_BAR(top->header), TRUE); + + gtk_header_bar_set_has_subtitle(GTK_HEADER_BAR(top->header), false); + } gtk_window_set_titlebar(gtk(), top->header); - gtk_header_bar_set_has_subtitle(GTK_HEADER_BAR(top->header), false); - - top->drawing_area = gtk_drawing_area_new(); - gtk_widget_set_can_focus(top->drawing_area, TRUE); - r.left -= top->csd.LeftMargin(); - r.right += top->csd.RightMargin(); - r.top -= top->csd.TopMargin(); - r.bottom += top->csd.BottomMargin(); + gtk_widget_set_can_focus(top->client, TRUE); + + gtk_container_add(GTK_CONTAINER(top->window), top->client); g_signal_connect(top->window, "delete-event", G_CALLBACK(GtkEvent), (gpointer)(uintptr_t)top->id); } else { - top->drawing_area = top->window; + ONCELOCK { + UpdateWindowFrameMargins(); + } + top->client = top->window; } - w.drawing_area = top->drawing_area; - top->cursor_id = -1; - gtk_widget_set_events(top->drawing_area, GDK_ALL_EVENTS_MASK & ~GDK_POINTER_MOTION_HINT_MASK & ~GDK_SMOOTH_SCROLL_MASK); - g_signal_connect(top->drawing_area, "event", G_CALLBACK(GtkEvent), (gpointer)(uintptr_t)top->id); - g_signal_connect(top->drawing_area, "draw", G_CALLBACK(GtkDraw), (gpointer)(uintptr_t)top->id); - - GdkWindowTypeHint hint = gtk_window_get_type_hint(gtk()); - if(tw && findarg(hint, GDK_WINDOW_TYPE_HINT_NORMAL, GDK_WINDOW_TYPE_HINT_DIALOG, GDK_WINDOW_TYPE_HINT_UTILITY) >= 0) - tw->SyncSizeHints(); - - gtk_window_set_default_size(gtk(), LSC(r.GetWidth()), LSC(r.GetHeight())); - gtk_window_move(gtk(), LSC(r.left), LSC(r.top)); - gtk_window_resize(gtk(), LSC(r.GetWidth()) - top->csd.ExtraWidth(), - LSC(r.GetHeight()) - top->csd.ExtraHeight()); - - if (top->header) { - gtk_container_add(GTK_CONTAINER(top->window), top->drawing_area); - gtk_widget_show_all(top->window); - } else { - gtk_widget_realize(top->window); - } + auto SetupEvents = [&](GtkWidget *w) { + gtk_widget_set_events(w, GDK_ALL_EVENTS_MASK & ~GDK_POINTER_MOTION_HINT_MASK & ~GDK_SMOOTH_SCROLL_MASK); + }; + SetupEvents(GTK_WIDGET(gtk())); + g_signal_connect(gtk(), "event", G_CALLBACK(TopGtkEvent), (gpointer)(uintptr_t)top->id); + auto SetupEvents2 = [&](GtkWidget *w) { + SetupEvents(w); + g_signal_connect(w, "event", G_CALLBACK(GtkEvent), (gpointer)(uintptr_t)top->id); + g_signal_connect(w, "draw", G_CALLBACK(GtkDraw), (gpointer)(uintptr_t)top->id); + }; + + SetupEvents2(top->client); + + if(top->header_area) + SetupEvents2(top->header_area); + + if(tw) + if(findarg(gtk_window_get_type_hint(gtk()), GDK_WINDOW_TYPE_HINT_NORMAL, GDK_WINDOW_TYPE_HINT_DIALOG, GDK_WINDOW_TYPE_HINT_UTILITY) >= 0) + tw->SyncSizeHints(); + + gtk_widget_show_all(top->window); + w.gdk = gtk_widget_get_window(top->window); - + if(owner && owner->top) gtk_window_set_transient_for(gtk(), owner->gtk()); gtk_widget_set_app_paintable(top->window, TRUE); @@ -111,6 +171,7 @@ void Ctrl::Create(Ctrl *owner, bool popup) g_signal_connect(top->im_context, "preedit-end", G_CALLBACK(IMPreeditEnd), (gpointer)(uintptr_t)top->id); g_signal_connect(top->im_context, "commit", G_CALLBACK(IMCommit), (gpointer)(uintptr_t)top->id); + WndSetPos(r); WndShow(IsShown()); SweepConfigure(true); @@ -124,12 +185,13 @@ void Ctrl::Create(Ctrl *owner, bool popup) StateH(OPEN); - GdkModifierType mod; - Point m = GetMouseInfo(gdk(), mod); + Point m = prev_mouse_pos; r = GetWndScreenRect(); if(r.Contains(m)) DispatchMouse(MOUSEMOVE, m); + top->sync_rect = true; + WndRectsSync(); RefreshLayoutDeep(); } @@ -163,10 +225,10 @@ void Ctrl::WndDestroy() if(q >= 0) wins.Remove(q); if(owner) { - if(owner->top && owner->utop && owner->utop->csd.IsEnabled()) { + if(owner->top && owner->utop && owner->utop->csd) { // TODO: This fix the problem with keyboard when going back to original window, but // the previous control is not being focused like it should be. - gtk_window_set_focus(owner->gtk(), owner->utop->drawing_area); + gtk_window_set_focus(owner->gtk(), owner->utop->client); } owner->WndUpdate(); } @@ -219,3 +281,4 @@ void Ctrl::PopUp(Ctrl *owner, bool savebits, bool activate, bool, bool) } #endif + diff --git a/uppsrc/CtrlCore/GtkCtrl.h b/uppsrc/CtrlCore/GtkCtrl.h index d998ca1c2..149ba1b12 100644 --- a/uppsrc/CtrlCore/GtkCtrl.h +++ b/uppsrc/CtrlCore/GtkCtrl.h @@ -12,12 +12,6 @@ static void ThemeChanged(void *); -/* -_DBG_ - static void AddEvent(gpointer user_data, int type, const Value& value); - static void DoMouseEvent(int state, Point pos); -*/ - bool DispatchMouseIn(int act, int zd); Image GtkMouseEvent(int action, int act, int zd); void GtkButtonEvent(int action); @@ -31,6 +25,8 @@ _DBG_ static gboolean GtkEvent(GtkWidget *widget, GdkEvent *key, gpointer user_data); static gboolean GtkDraw(GtkWidget *widget, cairo_t *cr, gpointer data); + static gboolean TopGtkEvent(GtkWidget *widget, GdkEvent *event, gpointer user_data); + static Point GetMouseInfo(GdkWindow *win, GdkModifierType& mod); static GdkDevice *GetMouseDevice(); #if GTK_CHECK_VERSION(3, 20, 0) @@ -39,7 +35,7 @@ _DBG_ static bool MouseIsGrabbed(); bool GrabMouse(); static void UngrabMouse(); - + static int scale; // in case GUI is scaling (e.g. in UHD mode) enum { @@ -47,7 +43,7 @@ _DBG_ EVENT_TEXT, EVENT_FOCUS_CHANGE, }; - + struct GEvent0 { int time; int windowid; @@ -56,6 +52,9 @@ _DBG_ Point mousepos; guint state; int count; + GdkDevice *device; + double x_root; + double y_root; bool pen; bool pen_barrel; @@ -65,7 +64,7 @@ _DBG_ double pen_rotation; Pointf pen_tilt; }; - + struct GEvent : Moveable, GEvent0 { GdkEvent *event; @@ -77,12 +76,11 @@ _DBG_ GEvent(); ~GEvent(); }; - + struct Win : Moveable { int id; GtkWidget *gtk; GdkWindow *gdk; - GtkWidget *drawing_area; Ptr ctrl; Vector invalid; // areas invalidated to be processed at next opportunity }; @@ -90,7 +88,12 @@ _DBG_ void Proc(); bool SweepConfigure(bool wait); bool SweepFocus(bool wait); - void SyncWndRect(const Rect& rect); + void SyncWndRect(); + void InvalidateScreenRect(); + + void SetCustomBarColor(Color c); + void WndRectsSync() const; + static BiVector Events; static Vector> activePopup; // created with 'activate' flag - usually menu @@ -106,13 +109,13 @@ _DBG_ static int FindCtrl(Ctrl *ctrl); static int FindGtkWindow(GtkWidget *gtk); static int FindGdkWindow(GdkWindow *gdk); - + static Ctrl *GetTopCtrlFromId(int id); static Ctrl *GetTopCtrlFromId(gpointer user_data) { return GetTopCtrlFromId((uint32)(uintptr_t)user_data); } static void SyncPopupCapture(); void ReleasePopupCapture(); - + static void FocusSync(); static gboolean TimeHandler(GtkWidget *); static void InvalidateMousePos(); @@ -120,9 +123,6 @@ _DBG_ static void StartGrabPopup(); static bool ReleaseWndCapture0(); static void DoCancelPreedit(); - - static Rect frameMargins; - static Rect GetFrameMargins(); static Index dnd_targets; static String dnd_text_target; @@ -141,7 +141,7 @@ _DBG_ static Vector dnd_fmts; static int dnd_result; static Image dnd_icon; - + static void GtkSelectionDataSet(GtkSelectionData *selection_data, const String& fmt, const String& data); static void GtkGetClipData(GtkClipboard *clipboard, GtkSelectionData *selection_data, guint info, gpointer user_data); static void AddFmt(GtkTargetList *list, const String& fmt, int info); @@ -169,6 +169,22 @@ _DBG_ guint time, gpointer user_data, bool paste); static bool ProcessInvalids(); + static Rect csd_border; + static int csd_std_header_cy; + static Rect frameMargins; + + static void UpdateWindowDecorationsGeometry(); + static void UpdateWindowFrameMargins(); + + + static bool prevent_custombar_drag; + static bool custom_titlebar_drag_click; + + static int GetGtkTitleBarHeight(const TopWindow *tw); + static int GetGtkTitleBarButtonWidth(); + static void SetCustomBarDragPrevention(); + void SyncPreventCustomBarDragPrevention(); + friend bool InitGtkApp(int argc, char **argv, const char **envptr); friend void GuiPlatformGripResize(TopWindow *q); @@ -188,30 +204,35 @@ public: // really private: static Gclipboard& gclipboard(); static Gclipboard& gselection(); static String RenderPrimarySelection(const Value& fmt); - + static Vector> hotkey; static Vector keyhot; static Vector modhot; - static guint MouseState; + static guint prev_state; + static Point prev_mouse_pos; static int SCL(int x) { return scale * x; } + static int SCL(double x) { return scale * x; } static Rect SCL(int x, int y, int cx, int cy) { return RectC(SCL(x), SCL(y), SCL(cx), SCL(cy)); } static double LSC(int x) { return (double)x / scale; } - + static int LSCH(int x) { return (x + scale - 1) / scale; } + static Rect LSCH(int x, int y, int cx, int cy){ return Rect(LSC(x), LSC(y), LSCH(x + cx), LSCH(y + cy)); } + static Rect LSCH(const Rect& r) { return LSCH(r.left, r.top, r.GetWidth(), r.GetHeight()); } + static int GetCaretBlinkTime() { return 500; } public: static void EndSession() {} static bool IsEndSession() { return false; } static void PanicMsgBox(const char *title, const char *text); - + static bool IsX11(); static bool IsWayland(); static bool IsRunningOnWayland(); static bool IsXWayland() { return IsX11() && IsRunningOnWayland(); } - + static void UseWayland(); - + static Point CurrentMousePos; static guint CurrentState; static guint32 CurrentTime; diff --git a/uppsrc/CtrlCore/GtkCustomBar.cpp b/uppsrc/CtrlCore/GtkCustomBar.cpp new file mode 100644 index 000000000..8dcb7eefe --- /dev/null +++ b/uppsrc/CtrlCore/GtkCustomBar.cpp @@ -0,0 +1,207 @@ +#include + +#ifdef GUI_GTK + +namespace Upp { + +extern Event custom_titlebar_metrics__; +extern Function is_custom_titlebar__; +extern Function custom_titlebar_make__; + +void TopWindow::CustomBarIcon::Paint(Draw& w) +{ + w.DrawImage(0, 0, HasMouse() ? CtrlCoreImg::GtkBarButtonH() : CtrlCoreImg::GtkBarButton()); + w.DrawImage(0, 0, img); +} + +void TopWindow::CustomBarIcon::MouseMove(Point p, dword keyflags) +{ + Refresh(); +} + +void TopWindow::CustomBarIcon::LeftDown(Point p, dword keyflags) +{ + SetCapture(); +} + +void TopWindow::CustomBarIcon::LeftUp(Point, dword keyflags) +{ + if(HasMouse()) + Action(); + Refresh(); +} + +void TopWindow::CustomBarIcon::MouseLeave() +{ + Refresh(); +} + +void TopWindow::BarCtrl::LeftDouble(Point p, dword keyflags) +{ + WhenAction(); +} + +void TopWindow::Init() +{ + custom_titlebar_metrics__ = [](const TopWindow *tw, TopWindow::CustomTitleBarMetrics& m) { + if(!tw->custom_bar) + return; + m.height = GetGtkTitleBarHeight(tw); + m.lm = 0; + m.rm = 0; + /* + Image icon = tw->GetIcon(); + if(!IsNull(icon)) + m.lm = DPI(4) + min(icon.GetWidth(), 32); + m.rm = (tw->IsZoomable() ? 3 : 1) * GetGtkTitleBarButtonWidth(); + */ + }; + + is_custom_titlebar__ = [](const TopWindow *win) { + return win->IsCustomTitleBar__(); + }; + + custom_titlebar_make__ = [=](TopWindow *win, Color bk, int mincy) -> Ctrl * { + return win->MakeCustomTitleBar__(bk, mincy); + }; +} + +int Ctrl::GetGtkTitleBarHeight(const TopWindow *tw) +{ + return max(tw->custom_titlebar_cy, IsUHDMode() ? 60 : 31); +} + +int Ctrl::GetGtkTitleBarButtonWidth() +{ + return IsUHDMode() ? 94 : 47; +} + +void TopWindow::SyncIcons() +{ + minicon.Set(CtrlCoreImg::GtkBarMinimize()); + GtkWindow *w = gtk(); + maxicon.Set(w && gtk_window_is_maximized(w) ? CtrlCoreImg::GtkBarOverlap() + : CtrlCoreImg::GtkBarMaximize()); + closeicon.Set(CtrlCoreImg::GtkBarClose()); +} + +void TopWindow::SyncCustomBar() +{ + if(custom_bar_frame) { + // custom_bar_frame->Height(GetCustomTitleBarMetrics().height); + SetCustomBarColor(Nvl(custom_titlebar_bk, SColorFace())); + auto cm = GetCustomTitleBarMetrics(); + custom_bar->VSizePos().HSizePos(DPI(10), DPI(6)); + RefreshFrame(0, 0, GetRect().Width(), cm.height); + Size isz = CtrlCoreImg::GtkBarButton().GetSize(); + int y = (cm.height - isz.cy) / 2; + custom_bar_icons->Width((isz.cx + DPI(8)) * (IsZoomable() ? 3 : 1)); + *custom_bar_icons << closeicon.RightPos(DPI(8), isz.cx).TopPos(y, isz.cy); + if(IsZoomable()) { + *custom_bar_icons << minicon.LeftPos(0, isz.cx).TopPos(y, isz.cy); + *custom_bar_icons << maxicon.LeftPos(isz.cx + DPI(8), isz.cx).TopPos(y, isz.cy); + } + else { + minicon.Remove(); + maxicon.Remove(); + } + SyncIcons(); + } +} + +bool Ctrl::prevent_custombar_drag; + +void Ctrl::SetCustomBarDragPrevention() +{ // ugly as hell, but seems to be the only simple way how to prevent window drag in custom titlebar + static gint p1, p2; + GtkSettings *gs = gtk_settings_get_default(); + ONCELOCK { + g_object_get(gs, "gtk-double-click-distance", &p1, "gtk-dnd-drag-threshold", &p2, NULL); + }; + gint s1 = p1; + gint s2 = p2; + if(prevent_custombar_drag) + s1 = s2 = 1000000; // no drags for you, GtkWindow... + g_object_set (gs, "gtk-double-click-distance", s1, "gtk-dnd-drag-threshold", s2, NULL); +} + +void Ctrl::SyncPreventCustomBarDragPrevention() +{ + TopWindow *tw = dynamic_cast(this); + prevent_custombar_drag = !(tw && !tw->custom_bar_frame); // prevent for popup too to avoid problems with menu + SetCustomBarDragPrevention(); +} + +bool TopWindow::IsCustomTitleBar__() const +{ + return custom_bar; +} + +void TopWindow::DoZoom() +{ + GtkWindow *w = gtk(); + if(w) { + if(gtk_window_is_maximized(w)) + gtk_window_unmaximize(w); + else + gtk_window_maximize(w); + } +} + +void TopWindow::DoMoveWindow() +{ + DLOG(">>>>> MoveWindow"); +#if GTK_CHECK_VERSION(3, 22, 0) + if(CurrentEvent.device) + gdk_window_begin_move_drag_for_device(gdk(), CurrentEvent.device, + 1, CurrentEvent.x_root, CurrentEvent.y_root, CurrentEvent.time); + else +#endif + gdk_window_begin_move_drag(gdk(), 1, CurrentEvent.x_root, CurrentEvent.y_root, CurrentEvent.time); +} + +Ctrl *TopWindow::MakeCustomTitleBar__(Color bk, int mincy) +{ + if(!custom_bar) { + custom_bar_frame.Create(); + custom_bar_frame->Transparent(); + custom_bar_icons.Create(); + custom_bar_icons->Transparent(); + custom_bar_frame->AddFrame(*custom_bar_icons); + custom_bar.Create(); + + minicon << [=] { + GtkWindow *w = gtk(); + if(w) + gtk_window_iconify(w); + }; + maxicon << [=] { + DoZoom(); + }; + closeicon << [=] { + if(IsEnabled()) { + IgnoreMouseUp(); + WhenClose(); + } + }; + } + if(custom_bar) { + if(&GetFrame(0) != ~custom_bar_frame) { + RemoveFrame(*custom_bar_frame); + if(&GetFrame(0) == &NullFrame()) + SetFrame(0, *custom_bar_frame); + else + InsertFrame(0, *custom_bar_frame); + } + custom_bar_frame->Add(*custom_bar); + } + custom_titlebar_bk = bk; + if(custom_titlebar_cy < 0) + custom_titlebar_cy = mincy; + SyncCustomBar(); + return ~custom_bar; +} + +} + +#endif \ No newline at end of file diff --git a/uppsrc/CtrlCore/GtkDnD.cpp b/uppsrc/CtrlCore/GtkDnD.cpp index 2dc955916..65d5409e6 100644 --- a/uppsrc/CtrlCore/GtkDnD.cpp +++ b/uppsrc/CtrlCore/GtkDnD.cpp @@ -288,7 +288,10 @@ PasteClip Ctrl::GtkDnd(GtkWidget *widget, GdkDragContext *context, gint x, gint if(w) { GetMouseInfo(gdk_get_default_root_window(), mod); CurrentState = mod; - CurrentMousePos = Point(SCL(x), SCL(y)) + w->GetScreenRect().TopLeft(); + CurrentMousePos = Point(SCL(x), SCL(y)); + int x1, y1; + gdk_window_get_position(w->gdk(), &x1, &y1); + CurrentMousePos += Point(SCL(x1), SCL(y1)); w->DnD(CurrentMousePos, clip); } gdk_drag_status(context, clip.IsAccepted() ? clip.GetAction() == DND_MOVE ? GDK_ACTION_MOVE @@ -358,3 +361,4 @@ void Ctrl::DndExit() } #endif + diff --git a/uppsrc/CtrlCore/GtkEvent.cpp b/uppsrc/CtrlCore/GtkEvent.cpp index 47bc94a57..6925a6361 100644 --- a/uppsrc/CtrlCore/GtkEvent.cpp +++ b/uppsrc/CtrlCore/GtkEvent.cpp @@ -9,7 +9,7 @@ namespace Upp { #define LLOG(x) // DLOG(x) -// #define LOG_EVENTS _DBG_ +// #define LOG_EVENTS _DBG_ BiVector Ctrl::Events; @@ -17,7 +17,6 @@ Point Ctrl::CurrentMousePos; guint Ctrl::CurrentState; guint32 Ctrl::CurrentTime; Ctrl::GEvent Ctrl::CurrentEvent; -guint Ctrl::MouseState; bool GetShift() { return Ctrl::CurrentState & GDK_SHIFT_MASK; } bool GetCtrl() { return Ctrl::CurrentState & GDK_CONTROL_MASK; } @@ -87,10 +86,26 @@ bool Ctrl::ProcessInvalids() GuiLock __; if(invalids) { for(Win& win : wins) { - for(const Rect& r : win.invalid) - if(win.drawing_area && win.ctrl) - gdk_window_invalidate_rect(gtk_widget_get_window(win.drawing_area), - GdkRect(Nvl(r, win.ctrl->GetRect().GetSize())), TRUE); + Top *top = win.ctrl->GetTop(); + TopWindow *tw = dynamic_cast(~win.ctrl); + if(top) + for(const Rect& r : win.invalid) + if(top->client && win.ctrl) { + win.ctrl->WndRectsSync(); + Rect rr = Nvl(r, win.ctrl->GetRect().GetSize()); + auto Invalidate = [](GtkWidget *w, Rect r) { + if(r.IsEmpty()) + return; + r = LSCH(r); + gtk_widget_queue_draw_area(w, r.left, r.top, r.GetWidth(), r.GetHeight()); + }; + if(top && tw && top->header_area) { + int h = tw->custom_bar_frame->GetHeight(); + Invalidate(top->header_area, rr & Rect(0, 0, INT_MAX, h)); + rr.Offset(0, -h); + } + Invalidate(top->client, rr); + } win.invalid.Clear(); } invalids = false; @@ -104,8 +119,15 @@ gboolean Ctrl::GtkDraw(GtkWidget *widget, cairo_t *cr, gpointer user_data) Ctrl *p = GetTopCtrlFromId(user_data); if(p) { p->fullrefresh = false; + cairo_scale(cr, 1.0 / scale, 1.0 / scale); // cancel scaling to be pixel perfect - p->SyncWndRect(p->GetWndScreenRect()); // avoid black areas when resizing + + Top *top = p->GetTop(); + if(top && top->draw_after_configure) { + p->InvalidateScreenRect(); + top->draw_after_configure = false; + } + p->SyncWndRect(); SystemDraw w(cr); painting = true; @@ -113,7 +135,6 @@ gboolean Ctrl::GtkDraw(GtkWidget *widget, cairo_t *cr, gpointer user_data) double x1, y1, x2, y2; cairo_clip_extents (cr, &x1, &y1, &x2, &y2); Rect r = RectC((int)x1, (int)y1, (int)ceil(x2 - x1), (int)ceil(y2 - y1)); - w.Clip(r); // Because of IsPainting cairo_rectangle_list_t *list = cairo_copy_clip_rectangle_list(cr); if(list->status == CAIRO_STATUS_SUCCESS && list->num_rectangles < 10) { @@ -126,13 +147,62 @@ gboolean Ctrl::GtkDraw(GtkWidget *widget, cairo_t *cr, gpointer user_data) } cairo_rectangle_list_destroy(list); + TopWindow *tw = dynamic_cast(p); + if(top && tw && top->header_area && widget != top->header_area) { + int h = tw->GetCustomTitleBarMetrics().height; + w.Offset(0, -h); + r.OffsetVert(h); + } + else + w.Begin(); + + w.Clip(r); p->UpdateArea(w, r); w.End(); + w.End(); painting = false; } return true; } +void Ctrl::InvalidateScreenRect() +{ + LLOG("InvalidateScreenRect"); + Top *top = GetTop(); + if(top) + top->sync_rect = true; +} + +gboolean Ctrl::TopGtkEvent(GtkWidget *widget, GdkEvent *event, gpointer user_data) +{ +#ifdef LOG_EVENTS + String ev = "? " + AsString((int)event->type); + Tuple2 *f = FindTuple(xEvent, __countof(xEvent), event->type); + if(f) + ev = f->b; + LOG(rmsecs() << " TOP FETCH EVENT " << ev); +#endif + Ctrl *p = GetTopCtrlFromId(user_data); + Top *top = p ? p->GetTop() : nullptr; + TopWindow *tw = dynamic_cast(p); + switch(event->type) { + case GDK_SETTING: + if(tw) + tw->SetCustomBarDragPrevention(); + break; + case GDK_CONFIGURE: + AddEvent(user_data, GDK_CONFIGURE, Value(), event); +// case GDK_EXPOSE: + if(p) + p->InvalidateScreenRect(); + if(top) + top->draw_after_configure = true; + default: + break; + } + return false; +} + gboolean Ctrl::GtkEvent(GtkWidget *widget, GdkEvent *event, gpointer user_data) { GuiLock __; @@ -141,6 +211,7 @@ gboolean Ctrl::GtkEvent(GtkWidget *widget, GdkEvent *event, gpointer user_data) bool retval = true; Value value; Ctrl *p = GetTopCtrlFromId(user_data); + Top *top = p ? p->GetTop() : nullptr; #ifdef LOG_EVENTS String ev = "? " + AsString((int)event->type); Tuple2 *f = FindTuple(xEvent, __countof(xEvent), event->type); @@ -149,6 +220,8 @@ gboolean Ctrl::GtkEvent(GtkWidget *widget, GdkEvent *event, gpointer user_data) LOG(rmsecs() << " FETCH EVENT " << ev << " ctrl: " << Name(p)); #endif + if(!p) + return false; switch(event->type) { case GDK_DELETE: p->CancelPreedit(); @@ -175,12 +248,13 @@ gboolean Ctrl::GtkEvent(GtkWidget *widget, GdkEvent *event, gpointer user_data) if(IsNull(value)) return false; break; +/* this only adds issues case GDK_2BUTTON_PRESS: p->CancelPreedit(); value = DoButtonEvent(event, true); if(IsNull(value)) return false; - break; + break;*/ case GDK_BUTTON_RELEASE: p->CancelPreedit(); value = DoButtonEvent(event, false); @@ -212,19 +286,16 @@ gboolean Ctrl::GtkEvent(GtkWidget *widget, GdkEvent *event, gpointer user_data) case GDK_KEY_RELEASE: key = (GdkEventKey *)event; value << (int) key->keyval << (int) key->hardware_keycode; - if(pressed) { - p = GetTopCtrlFromId(user_data); - if(p) { - Top *top = p->GetTop(); - if(top && gtk_im_context_filter_keypress(top->im_context, key)) - return true; - } - } + if(pressed && top && gtk_im_context_filter_keypress(top->im_context, key)) + return true; break; +// case GDK_EXPOSE: case GDK_CONFIGURE: { retval = false; - GdkEventConfigure *e = (GdkEventConfigure *)event; - value = SCL(e->x, e->y, e->width, e->height); + if(p) + p->InvalidateScreenRect(); + if(top) + top->draw_after_configure = true; break; } default: @@ -237,7 +308,7 @@ gboolean Ctrl::GtkEvent(GtkWidget *widget, GdkEvent *event, gpointer user_data) int Ctrl::DoButtonEvent(GdkEvent *event, bool press) { GdkEventButton *e = (GdkEventButton *)event; - static int mask[] = { GDK_BUTTON1_MASK, GDK_BUTTON2_MASK, GDK_BUTTON3_MASK }; +/* static int mask[] = { GDK_BUTTON1_MASK, GDK_BUTTON2_MASK, GDK_BUTTON3_MASK }; if(e->button >= 1 && e->button <= 3) { int m = mask[e->button - 1]; if(press) @@ -246,7 +317,10 @@ int Ctrl::DoButtonEvent(GdkEvent *event, bool press) MouseState &= ~m; return e->button; } - return findarg(e->button, 8, 9) >= 0 ? (int)e->button : (int)Null; +*/ + // 1 2 3 left middle right + // 8 9 FW / BK + return findarg(e->button, 1, 2, 3, 8, 9) >= 0 ? (int)e->button : (int)Null; } Ctrl::GEvent::GEvent() @@ -294,7 +368,8 @@ void Ctrl::GEvent::operator=(const GEvent& e) Set(e); } -static Point s_mousepos; +Point Ctrl::prev_mouse_pos = Null; +guint Ctrl::prev_state = 0; Point Ctrl::GetMouseInfo(GdkWindow *win, GdkModifierType& mod) { @@ -303,7 +378,7 @@ Point Ctrl::GetMouseInfo(GdkWindow *win, GdkModifierType& mod) GdkDevice *pointer = gdk_seat_get_pointer (gdk_display_get_default_seat (display)); double x, y; gdk_window_get_device_position_double (win, pointer, &x, &y, &mod); - return s_mousepos; //return Point((int)SCL(x), (int)SCL(y)); + return prev_mouse_pos; //return Point((int)SCL(x), (int)SCL(y)); #else gint x, y; gdk_window_get_pointer(win, &x, &y, &mod); @@ -319,24 +394,77 @@ void Ctrl::AddEvent(gpointer user_data, int type, const Value& value, GdkEvent * e.windowid = (uint32)(uintptr_t)user_data; e.type = type; e.value = value; - GdkModifierType mod; - e.mousepos = GetMouseInfo(gdk_get_default_root_window(), mod); - if(event && event->type == GDK_MOTION_NOTIFY){ - GdkEventMotion *mevent = (GdkEventMotion *)event; - e.mousepos = s_mousepos = Point(SCL(mevent->x_root), SCL(mevent->y_root)); - } - if(event && event->type == GDK_LEAVE_NOTIFY){ - GdkEventCrossing *mevent = (GdkEventCrossing *)event; - e.mousepos = s_mousepos = Point(SCL(mevent->x_root), SCL(mevent->y_root)); - } - e.state = (mod & ~(GDK_BUTTON1_MASK|GDK_BUTTON2_MASK|GDK_BUTTON3_MASK)) | MouseState; + e.state = prev_state; + bool press = false; + if(event) + switch(event->type) { + case GDK_MOTION_NOTIFY: { + GdkEventMotion *mevent = (GdkEventMotion *)event; + prev_mouse_pos = Point(SCL(mevent->x_root), SCL(mevent->y_root)); + prev_state = e.state = mevent->state; + e.x_root = mevent->x_root; + e.y_root = mevent->y_root; + break; + } + case GDK_LEAVE_NOTIFY: { + GdkEventCrossing *mevent = (GdkEventCrossing *)event; + prev_mouse_pos = Point(SCL(mevent->x_root), SCL(mevent->y_root)); + prev_state = e.state = mevent->state; + e.x_root = mevent->x_root; + e.y_root = mevent->y_root; + break; + } + case GDK_BUTTON_PRESS: + press = true; + case GDK_BUTTON_RELEASE: { + GdkEventButton *mevent = (GdkEventButton *)event; + prev_mouse_pos = Point(SCL(mevent->x_root), SCL(mevent->y_root)); + prev_state = mevent->state; + int n = mevent->button; + if(n >= 1 && n <= 5) { // gdk reports one state back, without current event + dword mask = get_i(n - 1, GDK_BUTTON1_MASK, GDK_BUTTON2_MASK, GDK_BUTTON3_MASK, GDK_BUTTON4_MASK, GDK_BUTTON5_MASK); + if(press) + prev_state |= mask; + else + prev_state &= ~mask; + } + e.state = prev_state; + e.x_root = mevent->x_root; + e.y_root = mevent->y_root; + break; + } + case GDK_KEY_PRESS: + press = true; + case GDK_KEY_RELEASE: { + auto *kev = (GdkEventKey *)event; + prev_state = kev->state; + dword mask = decode(kev->keyval, + GDKEY(Control_L), GDK_CONTROL_MASK, + GDKEY(Control_R), GDK_CONTROL_MASK, + GDKEY(Shift_L), GDK_SHIFT_MASK, + GDKEY(Shift_R), GDK_SHIFT_MASK, + GDKEY(Alt_L), GDK_MOD1_MASK, + GDKEY(Alt_R), GDK_MOD1_MASK, 0); + if(mask) { + if(press) + prev_state |= mask; + else + prev_state &= ~mask; + } + e.state = prev_state; + } + default: + break; + } + e.mousepos = prev_mouse_pos; e.count = 1; - e.event = NULL; + e.event = nullptr; + e.device = nullptr; #if GTK_CHECK_VERSION(3, 22, 0) - GdkDevice *d = event ? gdk_event_get_source_device(event) : NULL; - if(d && findarg(gdk_device_get_source(d), GDK_SOURCE_PEN, GDK_SOURCE_TOUCHSCREEN) >= 0) { + e.device = event ? gdk_event_get_source_device(event) : NULL; + if(e.device && findarg(gdk_device_get_source(e.device), GDK_SOURCE_PEN, GDK_SOURCE_TOUCHSCREEN) >= 0) { e.pen = true; - e.pen_barrel = MouseState & GDK_BUTTON3_MASK; + e.pen_barrel = e.state & GDK_BUTTON3_MASK; double *axes = NULL; switch(event->type){ case GDK_BUTTON_PRESS: @@ -350,18 +478,16 @@ void Ctrl::AddEvent(gpointer user_data, int type, const Value& value, GdkEvent * axes = ((GdkEventButton *)event)->axes; break; case GDK_MOTION_NOTIFY:{ - GdkEventMotion *mevent = (GdkEventMotion *)event; - e.mousepos = s_mousepos = Point(SCL(mevent->x_root), SCL(mevent->y_root)); axes = ((GdkEventMotion *)event)->axes; break; } default:; } if(axes) { - if(!gdk_device_get_axis(d, axes, GDK_AXIS_PRESSURE, &e.pen_pressure)) e.pen_pressure=Null; - if(!gdk_device_get_axis(d, axes, GDK_AXIS_ROTATION, &e.pen_rotation)) e.pen_rotation=Null; - if(!gdk_device_get_axis(d, axes, GDK_AXIS_XTILT, &e.pen_tilt.x)) e.pen_tilt.x=Null; - if(!gdk_device_get_axis(d, axes, GDK_AXIS_YTILT, &e.pen_tilt.y)) e.pen_tilt.y=Null; + if(!gdk_device_get_axis(e.device, axes, GDK_AXIS_PRESSURE, &e.pen_pressure)) e.pen_pressure=Null; + if(!gdk_device_get_axis(e.device, axes, GDK_AXIS_ROTATION, &e.pen_rotation)) e.pen_rotation=Null; + if(!gdk_device_get_axis(e.device, axes, GDK_AXIS_XTILT, &e.pen_tilt.x)) e.pen_tilt.x=Null; + if(!gdk_device_get_axis(e.device, axes, GDK_AXIS_YTILT, &e.pen_tilt.y)) e.pen_tilt.y=Null; } } #endif @@ -488,10 +614,12 @@ void Ctrl::GtkButtonEvent(int action) GtkMouseEvent(action, act, 0); } +bool Ctrl::custom_titlebar_drag_click; + void Ctrl::Proc() { #ifdef LOG_EVENTS - String ev = "?"; + String ev = "?" + AsString(CurrentEvent.type); Tuple2 *f = FindTuple(xEvent, __countof(xEvent), CurrentEvent.type); if(f) ev = f->b; @@ -543,9 +671,22 @@ void Ctrl::Proc() } #endif + for(Ctrl *q : GetTopCtrls()) + q->SyncWndRect(); + + TopWindow *tw = dynamic_cast(this); + + auto IsCustomBarAction = [&] { + return tw && tw->custom_bar_frame && tw->custom_bar_frame->GetScreenRect().Contains(GetMousePos()) + && tw->IsCustomTitleBarDragArea(GetMousePos() - GetScreenRect().TopLeft()); + }; + switch(CurrentEvent.type) { case GDK_MOTION_NOTIFY: GtkMouseEvent(MOUSEMOVE, MOUSEMOVE, 0); + SyncPreventCustomBarDragPrevention(); + if(GetMouseLeft() && custom_titlebar_drag_click && IsCustomBarAction()) + tw->DoMoveWindow(); break; case GDK_BUTTON_PRESS: if(CurrentEvent.value == 8) { @@ -565,17 +706,18 @@ void Ctrl::Proc() ignoremouseup = false; } + custom_titlebar_drag_click = CurrentEvent.value == 1 && IsCustomBarAction(); + if(!ignoreclick) { bool dbl = msecs(clicktime) < 250; clicktime = dbl ? clicktime - 1000 : msecs(); - GtkButtonEvent(dbl ? DOUBLE : DOWN); + if(dbl && IsCustomBarAction()) + tw->DoZoom(); + else + GtkButtonEvent(dbl ? DOUBLE : DOWN); } break; -/* case GDK_2BUTTON_PRESS: - if(!ignoreclick) - GtkButtonEvent(DOUBLE); - break; -*/ case GDK_BUTTON_RELEASE: + case GDK_BUTTON_RELEASE: if(CurrentEvent.value == 8) { DispatchKey(K_MOUSE_BACKWARD|K_KEYUP, 1); break; @@ -589,11 +731,17 @@ void Ctrl::Proc() else if(!dnd_events) GtkButtonEvent(UP); + if(!GetMouseRight() && !GetMouseMiddle() && !GetMouseLeft()) { + StopGrabPopup(); + ReleaseWndCapture0(); + } break; case GDK_SCROLL: { Point delta = CurrentEvent.value; - if(delta.y!=0.0) GtkMouseEvent(MOUSEWHEEL, MOUSEWHEEL, delta.y); - if(delta.x!=0.0) GtkMouseEvent(MOUSEHWHEEL, MOUSEHWHEEL, delta.x); + if(delta.y) + GtkMouseEvent(MOUSEWHEEL, MOUSEWHEEL, delta.y); + if(delta.x) + GtkMouseEvent(MOUSEHWHEEL, MOUSEHWHEEL, delta.x); break; } case GDK_KEY_PRESS: @@ -680,7 +828,6 @@ void Ctrl::Proc() } kv += K_DELTA; } - GetKeyDesc(kv); if(GetShift() && kv != K_SHIFT_KEY) kv |= K_SHIFT; if(GetCtrl() && kv != K_CTRL_KEY) @@ -710,18 +857,18 @@ void Ctrl::Proc() activeCtrl = NULL; break; case GDK_DELETE: { - TopWindow *w = dynamic_cast(this); - if(w) { + if(tw) { if(IsEnabled()) { IgnoreMouseUp(); - w->WhenClose(); + tw->WhenClose(); } } return; } case GDK_CONFIGURE: - SyncWndRect(CurrentEvent.value); - break; + InvalidateScreenRect(); + SyncWndRect(); + return; default: return; } @@ -729,13 +876,23 @@ void Ctrl::Proc() _this->PostInput(); } -void Ctrl::SyncWndRect(const Rect& rect) +void Ctrl::SyncWndRect() { + WndRectsSync(); + Rect rect = GetWndScreenRect(); + TopWindow *tw = dynamic_cast(this); + if(tw) { + tw->SyncIcons(); + if(tw->state == TopWindow::OVERLAPPED) + tw->overlapped = rect; + + Top *top = GetTop(); + int cy = utop->header_rect.GetHeight(); + if(top && tw->custom_bar_frame && tw->custom_bar_frame->GetHeight() != cy) + tw->custom_bar_frame->Height(cy); + } if(GetRect() != rect) SetWndRect(rect); - TopWindow *w = dynamic_cast(this); - if(w && w->state == TopWindow::OVERLAPPED) - w->overlapped = rect; } bool Ctrl::ProcessEvent0(bool *quit, bool fetch) @@ -769,6 +926,7 @@ bool Ctrl::ProcessEvent0(bool *quit, bool fetch) Value val = e.value; Events.DropHead(); Ctrl *w = GetTopCtrlFromId(e.windowid); + SetCustomBarDragPrevention(); FocusSync(); CaptureSync(); if(w) { @@ -839,8 +997,6 @@ void Ctrl::EventLoop(Ctrl *ctrl) ASSERT(LoopLevel == 0 || ctrl); LoopLevel++; LLOG("Entering event loop at level " << LoopLevel << LOG_BEGIN); - if(!GetMouseRight() && !GetMouseMiddle() && !GetMouseLeft()) - ReleaseCtrlCapture(); Ptr ploop; if(ctrl) { ploop = LoopCtrl; diff --git a/uppsrc/CtrlCore/GtkTop.cpp b/uppsrc/CtrlCore/GtkTop.cpp index 44b0273b5..a8626df7b 100644 --- a/uppsrc/CtrlCore/GtkTop.cpp +++ b/uppsrc/CtrlCore/GtkTop.cpp @@ -6,39 +6,47 @@ namespace Upp { #define LLOG(x) // DLOG(x) -Rect Ctrl::frameMargins; - -Rect Ctrl::GetFrameMargins() -{ - GuiLock __; - return frameMargins != Rect(0, 0, 0, 0) ? frameMargins : Rect(8, 32, 8, 8); -} - void TopWindow::SyncSizeHints() { GuiLock __; if(!top) return; - GdkGeometry m; Size sz0 = GetRect().GetSize(); LLOG("SyncSizeHints sz0: " << sz0 << ", sizeable: " << sizeable << ", min: " << GetMinSize() << ", max: " << GetMaxSize()); - Size sz = sz0; - if(sizeable) - sz = GetMinSize(); - m.min_width = LSC(sz.cx + utop->csd.ExtraWidth()); - m.min_height = LSC(sz.cy + utop->csd.ExtraHeight()); - sz = sz0; - if(sizeable) - sz = GetMaxSize(); - m.max_width = LSC(sz.cx + utop->csd.ExtraWidth()); - m.max_height = LSC(sz.cy + utop->csd.ExtraHeight()); - gtk_window_set_resizable(gtk(), sizeable); Top *top = GetTop(); if(top) { - gtk_window_set_geometry_hints(gtk(), top->window, &m, - GdkWindowHints(GDK_HINT_MIN_SIZE|GDK_HINT_MAX_SIZE)); + int mcx = 0; + int mcy = 0; + + if(top->csd) { + mcx += csd_border.left + csd_border.right; + mcy += csd_border.top + csd_border.bottom; + if(custom_bar_frame) + mcy += GetCustomTitleBarMetrics().height; + else + mcy += csd_std_header_cy; + } + + GdkGeometry m; + + m.base_width = sz0.cx; + m.base_height = sz0.cy; + + Size minsz = sizeable ? GetMinSize() : sz0; + m.min_width = LSCH(minsz.cx + mcx); + m.min_height = LSCH(minsz.cy + mcy); + + Size maxsz = sizeable ? GetMaxSize() : sz0; + m.max_width = LSCH(maxsz.cx + mcx); + m.max_height = LSCH(maxsz.cy + mcy); + + gtk_window_set_resizable(gtk(), sizeable); + gtk_window_set_geometry_hints(gtk(), NULL, &m, + GdkWindowHints(GDK_HINT_MIN_SIZE|GDK_HINT_MAX_SIZE|GDK_HINT_BASE_SIZE)); gtk_widget_set_size_request(top->window, m.min_width, m.min_height); } + + SyncCustomBar(); } void TopWindow::SyncTitle() @@ -73,8 +81,8 @@ void TopWindow::CenterRect(Ctrl *owner) SetupRect(owner); if(owner && center == 1 || center == 2) { Size sz = GetRect().Size(); - Rect wr = owner? owner->GetWorkArea() : Ctrl::GetPrimaryWorkArea(); - Rect fm = GetFrameMargins(); + Rect wr = owner ? owner->GetWorkArea() : Ctrl::GetPrimaryWorkArea(); + Rect fm = frameMargins; Rect r = (center == 1 && owner ? owner->GetRect() : wr) .CenterRect(sz); wr.left += fm.left; @@ -107,6 +115,7 @@ gboolean TopWindow::StateEvent(GtkWidget *widget, GdkEventWindowState *event, gp { TopWindow *w = (TopWindow *)user_data; dword h = event->new_window_state; + int prev = w->state; if(h & GDK_WINDOW_STATE_FULLSCREEN) w->state = FULLSCREEN; else @@ -119,7 +128,14 @@ gboolean TopWindow::StateEvent(GtkWidget *widget, GdkEventWindowState *event, gp w->state = OVERLAPPED; w->overlapped = w->GetRect(); } + LLOG("StateEvent " << prev << " -> " << (int)w->state); w->topmost = h & GDK_WINDOW_STATE_ABOVE; + w->Layout(); + if(prev == MINIMIZED && w->state != MINIMIZED) { + prev_mouse_pos = CurrentMousePos = Null; // we lost the track of mouse, otherwise "minimize" button would render highlighted + if(w->custom_bar_icons) + w->custom_bar_icons->RefreshFrame(); + } return FALSE; } @@ -145,13 +161,6 @@ void TopWindow::Open(Ctrl *owner) state = OVERLAPPED; SetMode(q); SyncTopMost(); - GdkRectangle fr; - gdk_window_get_frame_extents(gdk(), &fr); - Rect r = GetRect(); - frameMargins.left = max(frameMargins.left, minmax(r.left - SCL(fr.x), 0, 32)); - frameMargins.right = max(frameMargins.right, minmax(SCL(fr.x + fr.width) - r.right, 0, 32)); - frameMargins.top = max(frameMargins.top, minmax(r.top - SCL(fr.y), 0, 64)); - frameMargins.bottom = max(frameMargins.bottom, minmax(SCL(fr.y + fr.height) - r.bottom, 0, 48)); } void TopWindow::Open() @@ -178,29 +187,24 @@ void TopWindow::SetMode(int mode) if(w) switch(state) { case MINIMIZED: - gtk_window_deiconify(w); - break; - case MAXIMIZED: - gtk_window_unmaximize(w); - break; - case FULLSCREEN: - gtk_window_unfullscreen(w); - break; - } - state = mode; - if(w) - switch(state) { - case MINIMIZED: + fullscreen = false; gtk_window_iconify(w); break; case MAXIMIZED: + fullscreen = false; + gtk_window_deiconify(w); gtk_window_maximize(w); break; + case OVERLAPPED: + fullscreen = false; + gtk_window_deiconify(w); + gtk_window_unmaximize(w); + break; case FULLSCREEN: gtk_window_fullscreen(w); + fullscreen = true; break; } - fullscreen = state == FULLSCREEN; } void TopWindow::Minimize(bool effect) @@ -263,7 +267,7 @@ void TopWindow::SerializePlacement(Stream& s, bool reminimize) if(s.IsLoading()) { if(mn) rect = overlapped; Rect limit = GetVirtualWorkArea(); - Rect fm = GetFrameMargins(); + Rect fm = frameMargins; limit.left += fm.left; limit.right -= fm.right; limit.top += fm.top; @@ -291,4 +295,4 @@ void TopWindow::SerializePlacement(Stream& s, bool reminimize) } -#endif +#endif \ No newline at end of file diff --git a/uppsrc/CtrlCore/GtkTop.h b/uppsrc/CtrlCore/GtkTop.h index cb44b5f6a..6a18da19e 100644 --- a/uppsrc/CtrlCore/GtkTop.h +++ b/uppsrc/CtrlCore/GtkTop.h @@ -1,13 +1,51 @@ ImageGdk gdk_icon; ImageGdk gdk_largeicon; bool topmost; + + struct CustomBarIcon : public Ctrl { + void Paint(Draw& w) override; + void MouseMove(Point p, dword keyflags) override; + void LeftDown(Point p, dword keyflags) override; + void LeftUp(Point, dword keyflags) override; + void MouseLeave() override; + + Image img; + + void Set(const Image& m) { if(!m.IsSame(img)) { img = m; Refresh(); }} + }; + struct BarCtrl : public Ctrl { + void LeftDouble(Point p, dword keyflags) override; + }; + + One> custom_bar_frame; + One> custom_bar_icons; + One custom_bar; + CustomBarIcon minicon, maxicon, closeicon; + Color custom_titlebar_bk = SColorFace(); + int custom_titlebar_cy = -1; + bool force_csd = false; + +public: + void Force_csd_() { force_csd = true; } // this is for testing... + +private: enum { FULLSCREEN = 99 }; void CenterRect(Ctrl *owner); void SetMode(int mode); void SyncTopMost(); + void SyncCustomBar(); + void SyncIcons(); + bool IsCustomTitleBar__() const; + Ctrl *MakeCustomTitleBar__(Color bk, int mincy); + void DoZoom(); + void DoMoveWindow(); + static void Init(); + static gboolean StateEvent(GtkWidget *widget, GdkEventWindowState *event, gpointer user_data); friend class Ctrl; + friend bool InitGtkApp(int argc, char **argv, const char **envptr); + diff --git a/uppsrc/CtrlCore/GtkWnd.cpp b/uppsrc/CtrlCore/GtkWnd.cpp index d27606d60..9e399a37c 100644 --- a/uppsrc/CtrlCore/GtkWnd.cpp +++ b/uppsrc/CtrlCore/GtkWnd.cpp @@ -152,32 +152,107 @@ void Ctrl::UnregisterSystemHotKey(int id) #endif +Rect Ctrl::csd_border; +int Ctrl::csd_std_header_cy; +Rect Ctrl::frameMargins; + +void Ctrl::UpdateWindowDecorationsGeometry() +{ + GtkWidget* win = gtk_window_new(GTK_WINDOW_TOPLEVEL); + + GtkWidget* header = gtk_header_bar_new(); + + gtk_header_bar_set_show_close_button(GTK_HEADER_BAR(header), TRUE); + gtk_window_set_titlebar((GtkWindow *)win, header); + + GtkWidget* client = gtk_drawing_area_new(); + gtk_container_add(GTK_CONTAINER(win), client); + gtk_widget_show_all(win); + + gint left, top; + + gdk_window_get_origin(gtk_widget_get_window(client), &left, &top); + + int win_cx = gtk_widget_get_allocated_width(win); + int win_cy = gtk_widget_get_allocated_height(win); + + csd_std_header_cy = gtk_widget_get_allocated_height(header); + + int client_cx = gtk_widget_get_allocated_width(client); + int client_cy = gtk_widget_get_allocated_height(client); + + csd_border.left = SCL(left); + csd_border.right = SCL(win_cx - client_cx - left); + csd_border.top = SCL(top - csd_std_header_cy); + + csd_border.bottom = SCL(win_cy - client_cy - top); + csd_std_header_cy = SCL(csd_std_header_cy); + + gtk_widget_destroy(win); +} + +void Ctrl::UpdateWindowFrameMargins() +{ +#ifdef GDK_WINDOWING_X11 + // No realiable way how to do this in wayland, but there the window pos is ignored anyway + GtkWidget *win = gtk_window_new(GTK_WINDOW_TOPLEVEL); + gtk_widget_show_all(win); + gdk_display_sync(gdk_display_get_default()); + Vector fe = GetPropertyInts(gtk_widget_get_window(win), "_NET_FRAME_EXTENTS"); + if(fe.GetCount() >= 4) { + frameMargins.left = clamp(fe[0], 0, DPI(32)); + frameMargins.right = clamp(fe[1], 0, DPI(32)); + frameMargins.top = clamp(fe[2], 0, DPI(96)); + frameMargins.bottom = clamp(fe[3], 0, DPI(32)); + } + gtk_widget_destroy(win); +#endif +} + +void Ctrl::WndRectsSync() const +{ + if(utop && utop->sync_rect) { + auto GetScreenRect = [&](GtkWidget *w) { + gint x, y; + gint width, height; + + width = gtk_widget_get_allocated_width(w); + height = gtk_widget_get_allocated_height(w); + + if(top && utop->csd) { + gdk_window_get_root_origin(gdk(), &x, &y); + int x1, y1; + gtk_widget_translate_coordinates(w, GTK_WIDGET(gtk()), 0, 0, &x1, &y1); + x += x1; + y += y1; + } + else + gdk_window_get_position(gdk(), &x, &y); + + return SCL(x, y, width, height); + }; + + utop->client_rect = GetScreenRect(utop->client); + utop->screen_rect = utop->client_rect; + const TopWindow *tw = dynamic_cast(this); + if(tw && tw->custom_bar_frame) { + utop->header_rect = GetScreenRect(utop->header_area); + utop->screen_rect.Union(utop->header_rect); + } + utop->sync_rect = false; + } +} + Rect Ctrl::GetWndScreenRect() const { GuiLock __; - if(!IsOpen()) + if(!IsOpen() || !top) return Null; + + WndRectsSync(); - gint x, y; - gint width, height; - - if(IsWayland()) { - if(top && utop->csd.IsEnabled()) { - gdk_window_get_origin(gtk_widget_get_window(utop->drawing_area), &x, &y); - width = gtk_widget_get_allocated_width(utop->drawing_area); - height = gtk_widget_get_allocated_height(utop->drawing_area); - } - else - gdk_window_get_geometry(gdk(), &x, &y, &width, &height); - } - else { - gdk_window_get_position(gdk(), &x, &y); - width = gdk_window_get_width(gdk()); - height = gdk_window_get_height(gdk()); - } - - return SCL(x, y, width, height); + return utop->screen_rect; } void Ctrl::WndShow(bool b) @@ -431,27 +506,24 @@ void Ctrl::WndInvalidateRect(const Rect& r) GuiLock __; Rect rr = r; - if(scale > 1) { - rr.left = r.left / 2; - rr.top = r.top / 2; - rr.right = (r.right + 1) / 2; - rr.bottom = (r.bottom + 1) / 2; - } - if(IsWayland()) - rr.Inflate(2, 2); // TODO: This is temporary fix + rr.Inflate(DPI(2), DPI(2)); // TODO: This is temporary fix // as gtk3 dropped thread locking, we need to push invalid rectangles onto main loop for(Win& win : wins) { if(win.ctrl == this) { if(win.invalid.GetCount() && IsNull(win.invalid[0])) return; + for(const Rect& ir : win.invalid) + if(ir.Contains(r)) // ignore repeated invalidates + return; if(win.invalid.GetCount() > 40) { // keep things sane win.invalid.Clear(); win.invalid.Add(Null); } - else + else { win.invalid.Add(rr); + } if(!invalids) { invalids = true; WakeUpGuiThread(); @@ -476,10 +548,12 @@ bool Ctrl::SweepConfigure(bool wait) GEvent& e = Events[i]; Top *top = GetTop(); if(e.type == GDK_CONFIGURE && this_ && top && top->id == e.windowid) { - Rect rect = e.value; - LLOG("SweepConfigure " << rect); - if(GetRect() != rect) - SetWndRect(rect); + LLOG("SweepConfigure " << e.value); + if(top) { + utop->sync_rect = true; + LLOG("Sweep"); + SetWndRect(GetWndScreenRect()); + } r = true; e.type = EVENT_NONE; } @@ -489,22 +563,38 @@ bool Ctrl::SweepConfigure(bool wait) void Ctrl::WndSetPos(const Rect& rect) { + LLOG("========================== WNDSETPOS"); LLOG("WndSetPos " << UPP::Name(this) << " " << rect); GuiLock __; if(!IsOpen()) return; - Ptr this_ = this; - SweepConfigure(false); // Remove any previous GDK_CONFIGURE for this window - if(!this_ || !IsOpen()) - return; - Rect m(0, 0, 0, 0); - if(dynamic_cast(this)) - m = GetFrameMargins(); - SetWndRect(rect); - if(TopWindow *tw = dynamic_cast(this)) + TopWindow *tw = dynamic_cast(this); + if(tw) tw->SyncSizeHints(); - gdk_window_move_resize(gdk(), LSC(rect.left - m.left), LSC(rect.top - m.top), LSC(rect.GetWidth()), LSC(rect.GetHeight())); + + if(top && utop->csd) { + int top = csd_border.top; + if(tw) { + if(tw->custom_bar_frame) + top += tw->GetCustomTitleBarMetrics().height; + else + top += csd_std_header_cy; + } + gdk_window_move_resize(gdk(), LSC(rect.left - csd_border.left), LSC(rect.top - top), + LSCH(rect.GetWidth() + csd_border.left + csd_border.right), + LSCH(rect.GetHeight() + top + csd_border.bottom)); + } + else { + Rect m(0, 0, 0, 0); + if(tw) + m = frameMargins; + + gdk_window_move_resize(gdk(), LSC(rect.left - m.left), LSC(rect.top - m.top), + LSCH(rect.GetWidth()), LSCH(rect.GetHeight())); + } + InvalidateScreenRect(); + SyncWndRect(); LLOG("-- WndSetPos0 " << rect); } @@ -557,8 +647,8 @@ TopFrameDraw::TopFrameDraw(Ctrl *ctrl, const Rect& r) cairo_rectangle_int_t rr; rr.x = Ctrl::LSC(r.left); rr.y = Ctrl::LSC(r.top); - rr.width = Ctrl::LSC(r.GetWidth()); - rr.height = Ctrl::LSC(r.GetHeight()); + rr.width = Ctrl::LSCH(r.GetWidth()); + rr.height = Ctrl::LSCH(r.GetHeight()); cairo_region_t *rg = cairo_region_create_rectangle(&rr); ctx = gdk_window_begin_draw_frame(top->gdk(), rg); cairo_region_destroy(rg); @@ -606,4 +696,3 @@ Vector SplitCmdLine__(const char *cmd) #endif - diff --git a/uppsrc/CtrlCore/TopWin32.cpp b/uppsrc/CtrlCore/TopWin32.cpp index 7dc6519b9..bf00b517e 100644 --- a/uppsrc/CtrlCore/TopWin32.cpp +++ b/uppsrc/CtrlCore/TopWin32.cpp @@ -184,9 +184,50 @@ void TopWindow::SyncCaption() SetIco(); Ptr ptr = this; - PostCallback([=] { // windows 11 ignores icon if Window does not start processing messages within ~200ms + PostCallback([=] { // windows 11 seems to ignore icon if Window does not start processing messages within ~200ms if(ptr) ptr->SetIco(); // set it again when we are processing events }); + + SyncCustomBar(); +} + +void TopWindow::SyncCustomBar() +{ + if(custom_bar_frame) + custom_bar_frame->Height(GetCustomTitleBarMetrics().height); + if(custom_bar) { + auto cm = GetCustomTitleBarMetrics(); + custom_bar->VSizePos().HSizePos(cm.lm, cm.rm); + RefreshFrame(0, 0, GetRect().Width(), cm.height); + } +} + +bool TopWindow::IsCustomTitleBar__() const +{ + return custom_bar; +} + +Ctrl *TopWindow::MakeCustomTitleBar__(Color bk, int mincy) +{ + if(!custom_bar && IsWin11()) { + custom_bar_frame.Create(); + custom_bar_frame->Transparent(); + custom_bar.Create(); + } + if(custom_bar) { + if(&GetFrame(0) != ~custom_bar_frame) { + RemoveFrame(*custom_bar_frame); + if(&GetFrame(0) == &NullFrame()) + SetFrame(0, *custom_bar_frame); + else + InsertFrame(0, *custom_bar_frame); + } + custom_bar_frame->Add(*custom_bar); + } + custom_titlebar_bk = bk; + custom_titlebar_cy = mincy; + SyncCustomBar(); + return ~custom_bar; } void TopWindow::SetIco() @@ -203,7 +244,7 @@ void TopWindow::SetIco() ico = new_ico; lico = new_lico; - if(custom_titlebar) { + if(custom_bar) { Rect r = GetTitleBarRect(this); bool maximized = IsMaximized(); diff --git a/uppsrc/CtrlCore/TopWindow.cpp b/uppsrc/CtrlCore/TopWindow.cpp index 771874691..42957b63a 100644 --- a/uppsrc/CtrlCore/TopWindow.cpp +++ b/uppsrc/CtrlCore/TopWindow.cpp @@ -421,26 +421,29 @@ TopWindow& TopWindow::Icon(const Image& smallicon, const Image& _largeicon) return *this; } -bool is_custom_titlebar_available__; - -bool TopWindow::IsCustomTitleBar() const -{ - return custom_titlebar && is_custom_titlebar_available__; -} - -TopWindow& TopWindow::CustomTitleBar(int cy) -{ - custom_titlebar = is_custom_titlebar_available__; - custom_titlebar_cy = cy; - return *this; -} +// avoid the need to implement custom titlebar in all platforms: +Function is_custom_titlebar__; +Function custom_titlebar_make__; Event custom_titlebar_metrics__ = [](const TopWindow *, TopWindow::CustomTitleBarMetrics& m) { m.lm = m.rm = m.height = 0; - m.background = SColorPaper(); }; +bool TopWindow::IsCustomTitleBar() const +{ + return is_custom_titlebar__(this); +} + +Ctrl * TopWindow::CustomTitleBar(Color bk, int min_cy) +{ +#ifdef flagNOCUSTOMBAR // suppress custom title bar for testing + return nullptr; +#else + return custom_titlebar_make__(this, bk, min_cy); +#endif +} + TopWindow::CustomTitleBarMetrics TopWindow::GetCustomTitleBarMetrics() const { CustomTitleBarMetrics m; @@ -448,17 +451,18 @@ TopWindow::CustomTitleBarMetrics TopWindow::GetCustomTitleBarMetrics() const return m; } -static bool sIsDragArea(Ctrl& w, Point p) +bool Ctrl::MouseActiveCtrl(Ctrl *w, Point p) { - for(Ctrl& q : w) - if(q.GetScreenRect().Contains(p)) - return q.IsIgnoreMouse() || sIsDragArea(q, p); - return false; + for(Ctrl *q = w->GetLastChild(); q; q = q->GetPrev()) + if(q->GetScreenRect().Contains(p)) + return MouseActiveCtrl(q, p); + + return w->IsMouseActive(); } bool TopWindow::IsCustomTitleBarDragArea(Point p) { - return sIsDragArea(*this, p + GetScreenRect().TopLeft()); + return !MouseActiveCtrl(this, p + GetScreenRect().TopLeft()); } TopWindow& TopWindow::ToolWindow(bool b) @@ -545,8 +549,6 @@ TopWindow::TopWindow() dokeys = true; fullscreen = frameless = urgent = false; close_rejects = false; - custom_titlebar = false; - custom_titlebar_cy = 0; } TopWindow::~TopWindow() diff --git a/uppsrc/CtrlCore/TopWindow.h b/uppsrc/CtrlCore/TopWindow.h index 19b62f855..43da90182 100644 --- a/uppsrc/CtrlCore/TopWindow.h +++ b/uppsrc/CtrlCore/TopWindow.h @@ -80,14 +80,9 @@ private: bool frameless:1; bool urgent:1; bool close_rejects:1; - bool custom_titlebar:1; byte state; Image icon, largeicon; - int custom_titlebar_cy = 0; - int active_titlebar_button = -1; - bool active_titlebar_active = false; - const TopStyle *st; void GuiPlatformConstruct(); @@ -181,14 +176,14 @@ public: TopWindow& LargeIcon(const Image& m); TopWindow& Icon(const Image& smallicon, const Image& largeicon); - TopWindow& CustomTitleBar(int min_cy = 0); + Ctrl *CustomTitleBar(Color bk = Null, int min_cy = 0); + bool IsCustomTitleBar() const; struct CustomTitleBarMetrics { int lm; int rm; int height; - Color background; }; CustomTitleBarMetrics GetCustomTitleBarMetrics() const; diff --git a/uppsrc/CtrlCore/Win32Ctrl.h b/uppsrc/CtrlCore/Win32Ctrl.h index bb1ab9c07..9f2e7a3c9 100644 --- a/uppsrc/CtrlCore/Win32Ctrl.h +++ b/uppsrc/CtrlCore/Win32Ctrl.h @@ -45,6 +45,9 @@ protected: void Create(HWND parent, DWORD style, DWORD exstyle, bool savebits, int show, bool dropshadow); Image DoMouse(int e, Point p, int zd = 0); + Rect AdjustWindowRect(const Rect& client, dword style, dword exstyle); + Rect AdjustWindowRect(const Rect& client); + void PaintWinBarBackground(SystemDraw& w, const Rect& clip); void PaintWinBar(SystemDraw& w, const Rect& clip); int GetActiveTitleBarButton(); diff --git a/uppsrc/CtrlCore/Win32Proc.cpp b/uppsrc/CtrlCore/Win32Proc.cpp index 768aa1eac..7d01546db 100644 --- a/uppsrc/CtrlCore/Win32Proc.cpp +++ b/uppsrc/CtrlCore/Win32Proc.cpp @@ -141,7 +141,7 @@ void Ctrl::PaintWinBarBackground(SystemDraw& w, const Rect& clip) HWND hwnd = GetHWND(); if(topwin && topwin->IsCustomTitleBar() && hwnd) { Rect r = GetTitleBarRect(topwin); - w.DrawRect(r, IsDarkTheme() ? Color(26, 34, 39) : Color(238, 244, 249)); + w.DrawRect(r, Nvl(topwin->custom_titlebar_bk, IsDarkTheme() ? Color(26, 34, 39) : Color(238, 244, 249))); } } @@ -471,7 +471,7 @@ LRESULT Ctrl::WindowProc(UINT message, WPARAM wParam, LPARAM lParam) { int padding = GetSystemMetricsForDpi(92 /*SM_CXPADDEDBORDER*/, dpi); Point p((LONG)lParam); ScreenToClient(hwnd, p); - // We should not return HTTOP when hit-testing a maximized window + // We should not return HTTOP when hit-testing a maximized window if(!IsMaximized(hwnd) && p.y > 0 && p.y < frame_y + padding && topwin->IsSizeable()) return HTTOP; @@ -896,16 +896,8 @@ LRESULT Ctrl::WindowProc(UINT message, WPARAM wParam, LPARAM lParam) { MINMAXINFO *mmi = (MINMAXINFO *)lParam; Rect frmrc = Size(200, 200); ::AdjustWindowRect(frmrc, WS_OVERLAPPEDWINDOW, FALSE); -// Size msz = Ctrl::GetWorkArea().Deflated(-frmrc.left, -frmrc.top, -// frmrc.right - 200, frmrc.bottom - 200).GetSize(); -// Rect minr(Point(50, 50), min(msz, GetMinSize())); -// Rect maxr(Point(50, 50), min(msz, GetMaxSize())); // Removed cxl&nixnixnix 2012-6-12 - Rect minr(Point(50, 50), GetMinSize()); - Rect maxr(Point(50, 50), GetMaxSize()); - dword style = ::GetWindowLong(hwnd, GWL_STYLE); - dword exstyle = ::GetWindowLong(hwnd, GWL_EXSTYLE); - AdjustWindowRectEx(minr, style, FALSE, exstyle); - AdjustWindowRectEx(maxr, style, FALSE, exstyle); + Rect minr = AdjustWindowRect(Rect(Point(50, 50), GetMinSize())); + Rect maxr = AdjustWindowRect(Rect(Point(50, 50), GetMaxSize())); mmi->ptMinTrackSize = Point(minr.Size()); mmi->ptMaxTrackSize = Point(maxr.Size()); LLOG("WM_GETMINMAXINFO: MinTrackSize = " << Point(mmi->ptMinTrackSize) << ", MaxTrackSize = " << Point(mmi->ptMaxTrackSize)); diff --git a/uppsrc/CtrlCore/Win32Top.h b/uppsrc/CtrlCore/Win32Top.h index 0a430c3d6..6b6b18e04 100644 --- a/uppsrc/CtrlCore/Win32Top.h +++ b/uppsrc/CtrlCore/Win32Top.h @@ -10,6 +10,17 @@ private: void SetIco(); void CenterRect(HWND owner, int center); + One> custom_bar_frame; + One custom_bar; + Color custom_titlebar_bk = Null; + int custom_titlebar_cy = 0; + int active_titlebar_button = -1; + bool active_titlebar_active = false; + + bool IsCustomTitleBar__() const; + Ctrl *MakeCustomTitleBar__(Color bk, int mincy); + void SyncCustomBar(); + public: void Open(HWND ownerhwnd); TopWindow& Style(dword _style); diff --git a/uppsrc/CtrlCore/Win32Wnd.cpp b/uppsrc/CtrlCore/Win32Wnd.cpp index 15a6a9cf6..39a3778c6 100644 --- a/uppsrc/CtrlCore/Win32Wnd.cpp +++ b/uppsrc/CtrlCore/Win32Wnd.cpp @@ -220,8 +220,9 @@ void Ctrl::InstallPanicBox() InstallPanicMessageBox(&Win32PanicMessageBox); } -extern bool is_custom_titlebar_available__; extern Event custom_titlebar_metrics__; +extern Function is_custom_titlebar__; +extern Function custom_titlebar_make__; void Ctrl::InitWin32(HINSTANCE hInstance) { @@ -284,10 +285,8 @@ void Ctrl::InitWin32(HINSTANCE hInstance) GlobalBackPaint(); - is_custom_titlebar_available__ = IsWin11(); - - custom_titlebar_metrics__ = [=](const TopWindow *tw, TopWindow::CustomTitleBarMetrics& m) { - if(!tw->custom_titlebar) + custom_titlebar_metrics__ = [](const TopWindow *tw, TopWindow::CustomTitleBarMetrics& m) { + if(!tw->custom_bar) return; m.height = GetWin32TitleBarHeight(tw); m.lm = 0; @@ -296,7 +295,15 @@ void Ctrl::InitWin32(HINSTANCE hInstance) m.lm = DPI(4) + min(icon.GetWidth(), 32); m.rm = (tw->IsZoomable() ? 3 : 1) * GetWin32TitleBarButtonWidth(); }; + + is_custom_titlebar__ = [](const TopWindow *win) { + return win->IsCustomTitleBar__(); + }; + custom_titlebar_make__ = [=](TopWindow *win, Color bk, int mincy) -> Ctrl * { + return win->MakeCustomTitleBar__(bk, mincy); + }; + EnterGuiMutex(); } @@ -465,14 +472,40 @@ void Ctrl::UseImmersiveDarkModeForWindowBorder() } } +Rect Ctrl::AdjustWindowRect(const Rect& client, dword style, dword exstyle) +{ + Rect r = client; + TopWindow *tw = dynamic_cast(this); + if(tw && tw->custom_bar_frame) { + Rect mr(100, 100, 200, 200); + AdjustWindowRectEx(mr, style, FALSE, exstyle); + + r.left -= (100 - mr.left); + r.right += (mr.right - 200); + r.bottom += (mr.bottom - 200); + + r.top -= tw->GetCustomTitleBarMetrics().height; + } + else + AdjustWindowRectEx(r, style, FALSE, exstyle); + + return r; +} + +Rect Ctrl::AdjustWindowRect(const Rect& client) +{ + HWND hwnd = GetHWND(); + return hwnd ? AdjustWindowRect(client, ::GetWindowLong(hwnd, GWL_STYLE), ::GetWindowLong(hwnd, GWL_EXSTYLE)) + : client; +} + void Ctrl::Create(HWND parent, DWORD style, DWORD exstyle, bool savebits, int show, bool dropshadow) { GuiLock __; ASSERT_(IsMainThread(), "Window creation can only happen in the main thread"); LLOG("Ctrl::Create(parent = " << (void *)parent << ") in " <& invalid) { GuiLock __; Window xwin = GetWindow(); - if(utop && IsVisible()) { + if(top && IsVisible()) { LTIMING("DoPaint"); fullrefresh = false; // if(GLX) return; @@ -647,7 +647,7 @@ void Ctrl::PopUp(Ctrl *owner, bool savebits, bool activate, bool, bool) WndShow(visible); if(activate && IsEnabled()) SetFocus(); - if(utop) utop->owner = owner; + if(top) utop->owner = owner; StateH(OPEN); } diff --git a/uppsrc/CtrlCore/src.tpp/TopWindow_en-us.tpp b/uppsrc/CtrlCore/src.tpp/TopWindow_en-us.tpp index f7804bf98..1f01eaf6a 100644 --- a/uppsrc/CtrlCore/src.tpp/TopWindow_en-us.tpp +++ b/uppsrc/CtrlCore/src.tpp/TopWindow_en-us.tpp @@ -539,6 +539,14 @@ windows decoration on the left / right side of area, [@(0.0.255) height would use to draw the background of title bar area.&] [s3; &] [s4; &] +[s5;:Upp`:`:TopWindow`:`:IsCustomTitleBarDragArea`(Point`): [@(0.0.255) virtual +bool] [* IsCustomTitleBarDragArea](Point [*@3 p])&] +[s2;%% This method can be overriden to determine whether mouse at +[%-*@3 p] should possibly drag the window. Default implementation +allows that in mouse inactive areas and is probably fine for +most use cases.&] +[s3; &] +[s4; &] [s5;:TopWindow`:`:SerializePlacement`(Stream`&`,bool`): [@(0.0.255) void]_[* SerializePla cement]([_^Stream^ Stream][@(0.0.255) `&]_[*@3 s], [@(0.0.255) bool]_[*@3 reminimize]_`=_[@(0.0.255) f alse])&] diff --git a/uppsrc/CtrlLib/Bar.h b/uppsrc/CtrlLib/Bar.h index 2e18a05be..7819de71d 100644 --- a/uppsrc/CtrlLib/Bar.h +++ b/uppsrc/CtrlLib/Bar.h @@ -512,7 +512,8 @@ public: virtual ~ToolButton(); }; -void PaintBarArea(Draw& w, Ctrl *x, const Value& look, int bottom = Null); +void PaintBarArea(Draw& w, Ctrl *x, const Value& look, int bottom = Null); +Color GetBarAreaAvgColor(); class ToolBar : public BarCtrl { public: diff --git a/uppsrc/CtrlLib/DisplayPopup.cpp b/uppsrc/CtrlLib/DisplayPopup.cpp index 61e3f8143..7985075b9 100644 --- a/uppsrc/CtrlLib/DisplayPopup.cpp +++ b/uppsrc/CtrlLib/DisplayPopup.cpp @@ -2,16 +2,17 @@ namespace Upp { -Rect DisplayPopup::screen_rect; -Ptr DisplayPopup::ctrl; -Rect DisplayPopup::item; -Value DisplayPopup::value; -Color DisplayPopup::paper; -Color DisplayPopup::ink; -dword DisplayPopup::style; -const Display *DisplayPopup::display; -int DisplayPopup::margin; -bool DisplayPopup::usedisplaystdsize_s; +Rect DisplayPopup::screen_rect; +Ptr DisplayPopup::ctrl; +Ptr DisplayPopup::owner; +Rect DisplayPopup::item; +Value DisplayPopup::value; +Color DisplayPopup::paper; +Color DisplayPopup::ink; +dword DisplayPopup::style; +const Display *DisplayPopup::display; +int DisplayPopup::margin; +bool DisplayPopup::usedisplaystdsize_s; DisplayPopup::DisplayPopup() { @@ -38,6 +39,7 @@ void DisplayPopup::PaintHook(Ctrl *tw, Draw& w, const Rect& clip) r.top += (r.Height() - display->GetStdSize(value).cy) / 2; display->Paint(w, r, value, ink, paper, style); } + w.End(); } } @@ -102,15 +104,15 @@ bool DisplayPopup::StateHook(Ctrl *, int reason) } -bool DisplayPopup::MouseHook(Ctrl *, bool, int, Point, int, dword) +bool DisplayPopup::MouseHook(Ctrl *, bool, int event, Point, int, dword) { Sync(); return false; } void DisplayPopup::Set(Ctrl *_ctrl, const Rect& _item, - const Value& _value, const Display *_display, - Color _ink, Color _paper, dword _style, int _margin) + const Value& _value, const Display *_display, + Color _ink, Color _paper, dword _style, int _margin) { if(!GUI_ToolTips()) return; @@ -120,6 +122,7 @@ void DisplayPopup::Set(Ctrl *_ctrl, const Rect& _item, RefreshRect(); item = _item; ctrl = _ctrl; + owner = this; value = _value; display = _display; ink = _ink; @@ -133,8 +136,10 @@ void DisplayPopup::Set(Ctrl *_ctrl, const Rect& _item, void DisplayPopup::Cancel() { - screen_rect = Null; - Sync(); + if(owner == this) { + screen_rect = Null; + Sync(); + } } bool DisplayPopup::IsOpen() diff --git a/uppsrc/CtrlLib/DisplayPopup.h b/uppsrc/CtrlLib/DisplayPopup.h index 0d884f475..43a18a606 100644 --- a/uppsrc/CtrlLib/DisplayPopup.h +++ b/uppsrc/CtrlLib/DisplayPopup.h @@ -1,15 +1,16 @@ class DisplayPopup : public Pte { bool usedisplaystdsize = false; - static Rect screen_rect; - static Ptr ctrl; - static Rect item; - static Value value; - static Color paper, ink; - static dword style; - static const Display *display; - static int margin; - static bool usedisplaystdsize_s; + static Rect screen_rect; + static Ptr ctrl; + static Ptr owner; + static Rect item; + static Value value; + static Color paper, ink; + static dword style; + static const Display *display; + static int margin; + static bool usedisplaystdsize_s; static bool StateHook(Ctrl *, int reason); static bool MouseHook(Ctrl *, bool, int, Point, int, dword); diff --git a/uppsrc/CtrlLib/PopUpList.cpp b/uppsrc/CtrlLib/PopUpList.cpp index a953c0948..0741afc9a 100644 --- a/uppsrc/CtrlLib/PopUpList.cpp +++ b/uppsrc/CtrlLib/PopUpList.cpp @@ -280,14 +280,11 @@ void PopUpList::PopUp(Ctrl *owner, int x, int top, int bottom, int width) { rt.top = top - h; rt.bottom = rt.top + h; } - if(up) { + if(up) popup->SetRect(Rect(rt.left, rt.bottom - 1, rt.right, rt.bottom)); - popup->Add(popup->ac.TopPos(0, rt.Height()).LeftPos(0, rt.Width())); - } - else { + else popup->SetRect(Rect(rt.left, rt.top, rt.right, rt.top + 1)); - popup->Add(popup->ac.BottomPos(0, rt.Height()).LeftPos(0, rt.Width())); - } + popup->Add(popup->ac.SizePos()); if(GUI_PopUpEffect()) { popup->ac.CenterCursor(); popup->PopUp(owner, true, true, GUI_DropShadows()); diff --git a/uppsrc/CtrlLib/ScrollBar.cpp b/uppsrc/CtrlLib/ScrollBar.cpp index b63e8023a..1b0ad7ff5 100644 --- a/uppsrc/CtrlLib/ScrollBar.cpp +++ b/uppsrc/CtrlLib/ScrollBar.cpp @@ -2,7 +2,7 @@ namespace Upp { -#define LLOG(x) // LOG(x) +#define LLOG(x) LOG(x) void Sb(Button::Style& bs, const Image& img) { diff --git a/uppsrc/CtrlLib/ToolBar.cpp b/uppsrc/CtrlLib/ToolBar.cpp index f5936933f..d5ef8855a 100644 --- a/uppsrc/CtrlLib/ToolBar.cpp +++ b/uppsrc/CtrlLib/ToolBar.cpp @@ -132,4 +132,12 @@ void StaticBarArea::Paint(Draw& w) upperframe ? Null : GetScreenRect().bottom); } +Color GetBarAreaAvgColor() +{ + Size sz(8, 8); + ImageDraw w(sz); + ChPaint(w, sz, Nvl(ToolBar().StyleDefault().arealook, ToolBar().StyleDefault().look)); + return AvgColor(w); +} + } diff --git a/uppsrc/ide/ide.h b/uppsrc/ide/ide.h index 4167b3457..4c9d75dd5 100644 --- a/uppsrc/ide/ide.h +++ b/uppsrc/ide/ide.h @@ -389,7 +389,6 @@ public: virtual void Activate(); virtual void Layout(); virtual void Skin(); - virtual bool IsCustomTitleBarDragArea(Point p); virtual bool IsVerbose() const; virtual void PutConsole(const char *s); diff --git a/uppsrc/ide/idewin.cpp b/uppsrc/ide/idewin.cpp index c5d162722..7d4a15789 100644 --- a/uppsrc/ide/idewin.cpp +++ b/uppsrc/ide/idewin.cpp @@ -15,13 +15,15 @@ void Ide::Skin() SyncUsc(); } -void Ide::ToggleVerboseBuild() { +void Ide::ToggleVerboseBuild() +{ console.verbosebuild = !console.verbosebuild; SetToolBar(); } -void Ide::ToggleStopOnErrors() { +void Ide::ToggleStopOnErrors() +{ stoponerrors = !stoponerrors; } @@ -306,18 +308,25 @@ void Ide::SetupBars() menubar.Transparent(); display.IgnoreMouse(); bararea.Add(barrect.SizePos()); - AddFrame(bararea); + Ctrl *custom_bar = nullptr; + if(!disable_custom_caption) + custom_bar = CustomTitleBar(GetBarAreaAvgColor()); + if(custom_bar) + *custom_bar << bararea.SizePos(); + else + AddFrame(bararea); + menubar.LeftPos(0, l).VCenterPos(menubar.GetStdHeight()); if(toolbar_in_row) { toolbar.SetFrame(NullFrame()); int tcy = max(mainconfiglist.GetStdSize().cy + DPI(2), toolbar.GetStdHeight()); - barrect.Add(menubar.LeftPos(0, l).VCenterPos(menubar.GetStdHeight())); + barrect.Add(menubar); barrect.Add(toolbar.HSizePos(l, r).VCenterPos(tcy)); barrect.Add(display.RightPos(4, r).VSizePos(2, 3)); bararea.Height(max(menubar.GetStdHeight(), tcy)); toolbar.Transparent(); } else { - barrect.Add(menubar.LeftPos(0, l).VCenterPos(menubar.GetStdHeight())); + barrect.Add(menubar); barrect.Add(display.RightPos(4, r).VSizePos(2, 3)); bararea.Height(menubar.GetStdHeight()); AddFrame(TopSeparatorFrame()); @@ -345,14 +354,13 @@ void Ide::Layout() int tcy = max(mainconfiglist.GetStdSize().cy + DPI(2), toolbar.GetStdHeight()); auto cm = GetCustomTitleBarMetrics(); - barrect.HSizePos(cm.lm, cm.rm); int x = 0; int mh = menubar.GetStdHeight(); menubar.LeftPos(0, mw).TopPos((cm.height - mh) / 2, mh); x += mw; - + if(toolbar_in_row) { int tw = toolbar.GetWidth(); int bah = 0; @@ -382,12 +390,6 @@ void Ide::Layout() display.Show(!designer && (menubar.GetSize().cx + display.GetSize().cx < GetSize().cx)); } -bool Ide::IsCustomTitleBarDragArea(Point p) -{ - p += GetScreenRect().TopLeft(); - return !menubar.GetScreenRect().Contains(p) && !toolbar.GetScreenRect().Contains(p); -} - void Ide::DoDisplay() { if(replace_in_files) @@ -400,7 +402,11 @@ void Ide::DoDisplay() s << "[g ["; if(editfile_isreadonly) s << "@B"; - s << " \1" << editfile << "\1]"; + int q = max(editfile.ReverseFind('/'), editfile.ReverseFind('\\')); + if(q >= 0) + s << " [1 \1" << editfile.Mid(0, q + 1) << "\1]\1" << editfile.Mid(q + 1) << "\1]"; + else + s << " \1" << editfile << "\1]"; if(!designer) { s << ": [* " << p.y + 1 << "]:" << p.x + 1; int64 l, h; diff --git a/uppsrc/ide/main.cpp b/uppsrc/ide/main.cpp index 7bc91d52a..b1becb87b 100644 --- a/uppsrc/ide/main.cpp +++ b/uppsrc/ide/main.cpp @@ -408,9 +408,6 @@ void AppMain___() Ctrl::SetAlwaysUseBundledIcon(); #endif - if(!ide.disable_custom_caption) - ide.CustomTitleBar(); - if(arg.GetCount() == 1) { if(arg[0].EndsWith(".upp")) { Vector names = Split(arg[0], DIR_SEP); diff --git a/upptst/CustomTitleBar2/CustomTitleBar2.upp b/upptst/CustomTitleBar2/CustomTitleBar2.upp new file mode 100644 index 000000000..38edcbdce --- /dev/null +++ b/upptst/CustomTitleBar2/CustomTitleBar2.upp @@ -0,0 +1,12 @@ +uses + CtrlLib; + +file + main.cpp; + +mainconfig + "" = "GUI", + "" = "GUI FORCE_CSD", + "" = "GUI FORCE_CSD NOCUSTOMBAR", + "" = "GUI WAYLAND"; + diff --git a/upptst/CustomTitleBar2/main.cpp b/upptst/CustomTitleBar2/main.cpp new file mode 100644 index 000000000..26e968d77 --- /dev/null +++ b/upptst/CustomTitleBar2/main.cpp @@ -0,0 +1,112 @@ +#include + +using namespace Upp; + +struct TestRect : Ctrl { + void Paint(Draw& w) override { + Size sz = GetSize(); + return; + w.DrawRect(sz, Yellow()); + DrawFatFrame(w, sz, LtGreen(), 8); + DrawFatFrame(w, sz, Red(), 2); + for(int i = 0; i < 100; i++) + w.DrawText(i * 100, 2, AsString(i)); + } +}; + +struct MyApp : TopWindow { + FrameTop bararea; // we represent whole TitleBar area as frame + ParentCtrl barrect; // to do custom caption clipping + MenuBar menubar, menu2; + LineEdit editor; + Label title; + Label label; + TestRect rect; + Button button, button2; + bool zooming = true; + WithDropChoice ed; + + void MainMenu(Bar& bar) + { + bar.Sub("File", [=](Bar& bar) { + bar.Add("Zooming", [=] { + zooming = !zooming; + Zoomable(zooming); + }); + bar.Add("Yellow", [=] { CustomTitleBar(Yellow()); }); + bar.Add("Cyan", [=] { CustomTitleBar(Cyan()); }); + bar.Add("LtCyan", [=] { CustomTitleBar(LtCyan()); }); + bar.Add("Blue", [=] { CustomTitleBar(Blue()); }); + bar.Add("Gray", [=] { CustomTitleBar(Gray()); }); + bar.Separator(); + bar.Add("GTK dialog", [=] { + FileSelNative sel; + sel.ActiveDir(GetHomeDirectory()); + sel.ExecuteSelectDir("Just a test"); + }); + bar.Add("U++ dialog", [=] { + SelectFileOpen("Text files\t*.txt\nAll files\t*.*"); + }); + bar.Separator(); + + bar.Add("Exit", [=] { Break(); }); + }); + } + + void SetMenuBar() { + menubar.Set([=](Bar& bar) { + MainMenu(bar); + }); + } + + virtual void Layout() override + { + String s = IsMinimized() ? "Minimized" : IsMaximized() ? "Maximized" : "Overlapped"; + Title(s); + // LOG("Layout " << s << ", rect: " << GetScreenRect() << ", mousepos: " << GetMousePos()); + } + + MyApp() { + Sizeable().Zoomable(); + Icon(CtrlImg::new_doc()); + AddFrame(menubar); + SetMenuBar(); + Title("This is CustomTitleBar example - CustomTitleBar not active"); + Add(editor.SizePos()); + + Ctrl *tb = CustomTitleBar(LtCyan(), GetStdFontCy() + DPI(4)); + if(tb) { +// *tb << label.SizePos(); + *tb << rect.SizePos(); + rect.IgnoreMouse(); + rect.SetFrame(BlackFrame()); + rect << label.SizePos(); + label.SetLabel("\1[g= This is test"); + button.SetLabel("Close"); + *tb << button.RightPos(DPI(2), DPI(100)).VSizePos(2, 2); + button << [=] { + Break(); + }; + *tb << ed.RightPos(DPI(110), DPI(100)).VSizePos(2, 2); + button2.SetLabel("RefreshF"); + *tb << button2.RightPos(DPI(222), DPI(100)).VSizePos(2, 2); + button2 << [=] { + RefreshFrame(); + }; + for(int i = 0; i < 200; i++) + ed.AddList(AsString(i)); + } + + String txt; + for(int i = 0; i < 200; i++) + txt << i << '\n'; + editor <<= txt; + + DDUMP(rect.IsIgnoreMouse()); + }; +}; + +GUI_APP_MAIN +{ + MyApp().Run(); +} diff --git a/upptst/CustomTitlebar/main.cpp b/upptst/CustomTitlebar/main.cpp deleted file mode 100644 index 1e209d460..000000000 --- a/upptst/CustomTitlebar/main.cpp +++ /dev/null @@ -1,55 +0,0 @@ -#include - -using namespace Upp; - -struct MyApp : TopWindow { - void Paint(Draw& w) override { - Size sz = GetSize(); -// w.DrawRect(sz, WhiteGray()); return; - - auto h = GetCustomTitleBarMetrics().height; - Color c2 = SLtCyan(); - for(int i = 0; i < h; i++) - w.DrawRect(0, i, sz.cx, 1, Blend(WhiteGray(), c2, i * 255 / h)); - } - - void Layout() override { - auto m = GetCustomTitleBarMetrics(); - int h = bar.GetHeight(); - bar.LeftPos(m.lm, bar.GetWidth()).TopPos((m.height - h) / 2, h); - } - - void MainMenu(Bar& bar) - { - bar.Sub("File", [=](Bar& bar) { - bar.Add("Exit", [=] { Break(); }); - }); - } - - MenuBar bar; - LineEdit editor; - - MyApp() { - Icon(CtrlImg::select_all()); - - CustomTitleBar(200); - - Add(bar); - bar.Transparent(); - bar.Set([=](Bar& bar) { - MainMenu(bar); - }); - - DDUMP(bar.GetWidth()); - DDUMP(bar.GetHeight()); - - DDUMP(GetCustomTitleBarMetrics().height); - - Add(editor.VSizePos(GetCustomTitleBarMetrics().height).HSizePos()); - }; -}; - -GUI_APP_MAIN -{ - MyApp().Sizeable().Zoomable().Run(); -} diff --git a/upptst/CustomTitlebar/CustomTitlebar.upp b/upptst/CustomTitlebar1/CustomTitlebar1.upp similarity index 100% rename from upptst/CustomTitlebar/CustomTitlebar.upp rename to upptst/CustomTitlebar1/CustomTitlebar1.upp diff --git a/upptst/CustomTitlebar1/main.cpp b/upptst/CustomTitlebar1/main.cpp new file mode 100644 index 000000000..53e8c77e3 --- /dev/null +++ b/upptst/CustomTitlebar1/main.cpp @@ -0,0 +1,53 @@ +#include + +using namespace Upp; + +struct MyApp : TopWindow { + MenuBar bar; + LineEdit editor; + Label title; + + void Layout() override { + if(IsCustomTitleBar()) { + int ch = GetCustomTitleBarMetrics().height; + int h = bar.GetHeight(); + int w = bar.GetWidth(); + bar.LeftPos(0, w).TopPos((ch - h) / 2, h); + + title.HSizePos(w, 0).VSizePos(); + } + } + + MyApp() { + Sizeable().Zoomable(); + + Icon(CtrlImg::select_all()); + + Ctrl *tb = CustomTitleBar(Blend(SWhiteGray(), SLtMagenta(), 50), 50); + + if(tb) { + tb->Add(bar); + tb->Add(title); + bar.Transparent(); + } + else + AddFrame(bar); + bar.Set([=](Bar& bar) { + bar.Sub("File", [=](Bar& bar) { + bar.Add("Yellow", [=] { CustomTitleBar(Yellow(), 20); }); + bar.Add("Red", [=] { CustomTitleBar(LtRed(), 20); }); + bar.Add("Exit", [=] { Break(); }); + }); + }); + + title = "\1[g This is [* title"; + title.AlignCenter(); + + Add(editor.SizePos()); + }; +}; + +GUI_APP_MAIN +{ + MyApp().Sizeable().Zoomable().Run(); +} diff --git a/upptst/GUI/GUI.upp b/upptst/GUI/GUI.upp new file mode 100644 index 000000000..38edcbdce --- /dev/null +++ b/upptst/GUI/GUI.upp @@ -0,0 +1,12 @@ +uses + CtrlLib; + +file + main.cpp; + +mainconfig + "" = "GUI", + "" = "GUI FORCE_CSD", + "" = "GUI FORCE_CSD NOCUSTOMBAR", + "" = "GUI WAYLAND"; + diff --git a/upptst/GUI/main.cpp b/upptst/GUI/main.cpp new file mode 100644 index 000000000..ebb5e645e --- /dev/null +++ b/upptst/GUI/main.cpp @@ -0,0 +1,100 @@ +#include + +using namespace Upp; + +struct TestRect : Ctrl { + void Paint(Draw& w) override { + Size sz = GetSize(); + return; + w.DrawRect(sz, Yellow()); + DrawFatFrame(w, sz, LtGreen(), 8); + DrawFatFrame(w, sz, Red(), 2); + for(int i = 0; i < 100; i++) + w.DrawText(i * 100, 2, AsString(i)); + } +}; + +struct MyApp : TopWindow { + FrameTop bararea; // we represent whole TitleBar area as frame + ParentCtrl barrect; // to do custom caption clipping + MenuBar menubar, menu2; + LineEdit editor; + Label title; + Label label; + TestRect rect; + Button button; + bool zooming = true; + WithDropChoice ed; + + void MainMenu(Bar& bar) + { + bar.Sub("File", [=](Bar& bar) { + bar.Add("Zooming", [=] { + zooming = !zooming; + Zoomable(zooming); + }); + bar.Add("Yellow", [=] { CustomTitleBar(Yellow()); }); + bar.Add("Cyan", [=] { CustomTitleBar(Cyan()); }); + bar.Add("LtCyan", [=] { CustomTitleBar(LtCyan()); }); + bar.Add("Blue", [=] { CustomTitleBar(Blue()); }); + bar.Add("Gray", [=] { CustomTitleBar(Gray()); }); + bar.Separator(); + bar.Add("Exit", [=] { Break(); }); + }); + } + + void SetMenuBar() { + menubar.Set([=](Bar& bar) { + MainMenu(bar); + }); + } + + bool IsCustomTitleBarDragArea(Point p) override + { // identifies which titlebar areas can be clicked for dragging the window + p += GetScreenRect().TopLeft(); + return !menubar.GetScreenRect().Contains(p); + } + + virtual void Layout() override + { + String s = IsMinimized() ? "Minimized" : IsMaximized() ? "Maximized" : "Overlapped"; + Title(s); + DLOG("Layout " << s << ", rect: " << GetScreenRect() << ", mousepos: " << GetMousePos()); + } + + MyApp() { + Sizeable().Zoomable(); + Icon(CtrlImg::new_doc()); + AddFrame(menubar); + SetMenuBar(); + Title("This is CustomTitleBar example - CustomTitleBar not active"); + Add(editor.SizePos()); + + Ctrl *tb = CustomTitleBar(LtCyan(), GetStdFontCy() + DPI(4)); + if(tb) { +// *tb << label.SizePos(); + *tb << rect.SizePos(); + rect.SetFrame(BlackFrame()); + rect << label.SizePos(); + label.SetLabel("\1[g= This is test"); + button.SetLabel("Close"); + rect << button.RightPos(DPI(2), DPI(100)).VSizePos(2, 2); + button << [=] { + Break(); + }; + rect << ed.RightPos(DPI(110), DPI(100)).VSizePos(2, 2); + for(int i = 0; i < 200; i++) + ed.AddList(AsString(i)); + } + + String txt; + for(int i = 0; i < 200; i++) + txt << i << '\n'; + editor <<= txt; + }; +}; + +GUI_APP_MAIN +{ + MyApp().Run(); +} diff --git a/upptst/MinMaxSize/MinMaxSize.upp b/upptst/MinMaxSize/MinMaxSize.upp new file mode 100644 index 000000000..5702af275 --- /dev/null +++ b/upptst/MinMaxSize/MinMaxSize.upp @@ -0,0 +1,10 @@ +uses + CtrlLib; + +file + main.cpp; + +mainconfig + "" = "GUI", + "" = "GUI FORCE_CSD"; + diff --git a/upptst/MinMaxSize/main.cpp b/upptst/MinMaxSize/main.cpp new file mode 100644 index 000000000..47c9e9adc --- /dev/null +++ b/upptst/MinMaxSize/main.cpp @@ -0,0 +1,92 @@ +#include + +using namespace Upp; + +struct App : public TopWindow +{ + DropList dl; + Button move; + + void Paint(Draw& w) override { + w.DrawRect(GetSize(), LtCyan()); + DrawFrame(w, 0, 0, 500, 500, Black()); + DrawFrame(w, 0, 0, 400, 400, Black()); + DrawFrame(w, 0, 0, 600, 600, Black()); + w.DrawText(10, 10, AsString(GetScreenView())); + w.DrawText(10, 50, AsString(GetScreenRect())); + w.DrawText(10, 90, AsString(GetRect())); + w.DrawText(10, 130, AsString(GetMousePos())); + } + + void MouseMove(Point p, dword keyflags) override { + Refresh(); + } + + void Layout() override { + Refresh(); + } + + Size GetMinSize() const override { + return Size(400, 400); + } + + Size GetMaxSize() const override { + return Size(600, 600); + } + + typedef App CLASSNAME; + + App() + { + SetRect(100, 200, 500, 500); + Sizeable().Zoomable(); + + Add(dl.LeftPos(10, 101).BottomPos(0)); + for(int i = 0; i < 20; i++) + dl.Add(i); + + Add(move.HSizePos(120, 0).BottomPosZ(0, 24)); + move.SetLabel("Move to 320, 420, 400"); + move << [=] { + DLOG("================ MOVE"); + SetRect(320, 420, 400, 400); + }; + } +}; + +struct App2 : App { + Label title; + + App2(int h = GetStdFontCy() + DPI(4)) { + SetRect(700, 200, 500, 500); + Ctrl *tb = CustomTitleBar(SYellow(), h); + if(tb) + *tb << title.SizePos(); + + title = "\1[g= [* custom [/_ titlebar"; + } +}; + + +GUI_APP_MAIN +{ + App app, app_csd; + App2 app2, app3(DPI(128)); + + app.Title("SSD"); + app.OpenMain(); + +#ifdef PLATFORM_POSIX + app_csd.Title("CSD"); + app_csd.Force_csd_(); + app_csd.SetRect(1300, 200, 500, 500); + app_csd.OpenMain(); +#endif + + app2.OpenMain(); + + app3.SetRect(1900, 200, 500, 500); + app3.OpenMain(); + + app.Run(); +}