Custom Title Bar refactored / gtk support (#345)

This commit is contained in:
mirek-fidler 2026-01-16 19:26:25 +01:00 committed by GitHub
parent 68dbd76e01
commit 241cc4ea9d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
48 changed files with 1606 additions and 538 deletions

View file

@ -12,5 +12,6 @@ file
mainconfig mainconfig
"" = "GUI", "" = "GUI",
"" = "GUI FORCE_CSD",
"" = "GUI X11"; "" = "GUI X11";

View file

@ -3,22 +3,15 @@
using namespace Upp; using namespace Upp;
struct MyApp : TopWindow { struct MyApp : TopWindow {
FrameTop<StaticRect> bararea; // we represent whole TitleBar area as frame
ParentCtrl barrect; // to do custom caption clipping
MenuBar menubar; MenuBar menubar;
LineEdit editor; LineEdit editor;
Label title; Label title;
void MainMenu(Bar& bar)
{
bar.Sub("File", [=](Bar& bar) {
bar.Add("Exit", [=] { Break(); });
});
}
void SetMenuBar() { void SetMenuBar() {
menubar.Set([=](Bar& bar) { 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()); Icon(CtrlImg::new_doc());
int h = menubar.GetStdHeight(); int h = menubar.GetStdHeight();
CustomTitleBar(h); // h is suggested minimum height Ctrl *custom_bar = CustomTitleBar(SColorFace());
if(IsCustomTitleBar()) { if(custom_bar) {
menubar.Transparent(); menubar.Transparent();
auto cm = GetCustomTitleBarMetrics(); auto cm = GetCustomTitleBarMetrics();
bararea.Height(cm.height); *custom_bar << menubar;
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;
SetMenuBar(); // run it here to get GetWidth SetMenuBar(); // run it here to get GetWidth
menubar.LeftPos(0, menubar.GetWidth()).TopPos((cm.height - h) / 2, h); menubar.LeftPos(0, menubar.GetWidth()).TopPos((cm.height - h) / 2, h);
barrect << title.HSizePos(menubar.GetWidth(), cm.rm).VSizePos(); *custom_bar << title.HSizePos(menubar.GetWidth(), cm.rm).VSizePos();
title.SetLabel("This is CustomTitleBar example"); title.SetLabel("\1[g This is [* CustomTitleBar] [/ example]");
title.AlignCenter(); title.AlignCenter();
title.AlignVCenter(); title.AlignVCenter();
} }

28
s Normal file
View file

@ -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

View file

@ -675,6 +675,8 @@ private:
static Point dndpos; static Point dndpos;
static bool dndframe; static bool dndframe;
static PasteClip dndclip; static PasteClip dndclip;
static int last_mouse_action;
void DnD(Point p, PasteClip& clip); void DnD(Point p, PasteClip& clip);
static void DnDRepeat(); static void DnDRepeat();
@ -700,8 +702,11 @@ private:
Rect GetPreeditScreenRect(); Rect GetPreeditScreenRect();
void SyncPreedit(); void SyncPreedit();
void ShowPreedit(const WString& text, int cursor = INT_MAX); 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... // System window interface...
void WndShow(bool b); void WndShow(bool b);

View file

@ -11,10 +11,22 @@ IMAGE_ID(DndNone98)
IMAGE_ID(DndCopy) IMAGE_ID(DndCopy)
IMAGE_ID(DndCopy98) IMAGE_ID(DndCopy98)
IMAGE_ID(DndData) 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(WinClose)
IMAGE_ID(WinMinimize) IMAGE_ID(WinMinimize)
IMAGE_ID(WinMaximize) IMAGE_ID(WinMaximize)
IMAGE_ID(WinMaximized) 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_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) 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_END_DATA(1152, 4)
IMAGE_BEGIN_DATA 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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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_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_END_DATA(608, 3) 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)

View file

@ -97,12 +97,12 @@ file
GtkCtrl.h, GtkCtrl.h,
GtkCtrl.cpp, GtkCtrl.cpp,
GtkCapture.cpp, GtkCapture.cpp,
GtkCSD.cpp,
GtkWnd.cpp, GtkWnd.cpp,
GtkCreate.cpp, GtkCreate.cpp,
GtkEvent.cpp, GtkEvent.cpp,
GtkTop.h, GtkTop.h,
GtkTop.cpp, GtkTop.cpp,
GtkCustomBar.cpp,
GtkClip.cpp, GtkClip.cpp,
GtkDnD.cpp, GtkDnD.cpp,
GtkApp.cpp, GtkApp.cpp,

View file

@ -21,6 +21,8 @@ Point Ctrl::middlemousepos = Null;
PenInfo Ctrl::pen; PenInfo Ctrl::pen;
bool Ctrl::is_pen_event; bool Ctrl::is_pen_event;
int Ctrl::last_mouse_action;
dword GetMouseFlags() { dword GetMouseFlags() {
dword style = 0; dword style = 0;
if(GetAlt()) style |= K_ALT; 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) Image Ctrl::FrameMouseEventH(int event, Point p, int zdelta, dword keyflags)
{ {
GuiLock __; GuiLock __;
last_mouse_action = event;
Ptr<Ctrl> this_ = this; Ptr<Ctrl> this_ = this;
for(int i = 0; i < mousehook().GetCount(); i++) for(int i = 0; i < mousehook().GetCount(); i++)
if(this_ && (*mousehook()[i])(this, true, event, p, zdelta, keyflags)) 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) Image Ctrl::MouseEventH(int event, Point p, int zdelta, dword keyflags)
{ {
GuiLock __; GuiLock __;
last_mouse_action = event;
Ptr<Ctrl> this_ = this; Ptr<Ctrl> this_ = this;
for(int i = 0; i < mousehook().GetCount(); i++) for(int i = 0; i < mousehook().GetCount(); i++)
if(this_ && (*mousehook()[i])(this, false, event, p, zdelta, keyflags)) if(this_ && (*mousehook()[i])(this, false, event, p, zdelta, keyflags))

View file

@ -154,7 +154,7 @@ void Ctrl::SyncLayout(int force)
GuiLock __; GuiLock __;
if(destroying) if(destroying)
return; return;
LLOG("SyncLayout " << Name() << " size: " << GetSize()); LLOG("SyncLayout " << Name() << " size: " << GetSize() << " force: " << force);
fullrefresh = false; fullrefresh = false;
bool refresh = false; bool refresh = false;
Rect oview = GetView(); Rect oview = GetView();
@ -174,7 +174,6 @@ void Ctrl::SyncLayout(int force)
if(oview.Size() != view.Size() || force > 1) { if(oview.Size() != view.Size() || force > 1) {
for(Ctrl& q : *this) { for(Ctrl& q : *this) {
q.rect = q.CalcRect(rect, view); q.rect = q.CalcRect(rect, view);
LLOG("Layout set rect " << q.Name() << " " << q.rect);
q.SyncLayout(force > 1 ? force : 0); q.SyncLayout(force > 1 ? force : 0);
} }
Refresh(); Refresh();
@ -264,10 +263,11 @@ void Ctrl::UpdateRect0(bool sync)
Rect pwa = GetPrimaryWorkArea(); Rect pwa = GetPrimaryWorkArea();
rect = OffsetMegaRect(CalcRect(pwa, pwa)); rect = OffsetMegaRect(CalcRect(pwa, pwa));
} }
LLOG("UpdateRect0 " << Name() << " to " << rect);
LLOG("UpdateRect0 " << Name() << " to " << rect << ", sync: " << sync);
LTIMING("UpdateRect0 SyncLayout"); LTIMING("UpdateRect0 SyncLayout");
if(sync) if(sync)
SyncLayout(); SyncLayout(sync);
} }

View file

@ -41,7 +41,7 @@ private:
void PutTxt(const RichTxt& txt, int nesting, int dot_width); void PutTxt(const RichTxt& txt, int nesting, int dot_width);
void PutTable(const RichTable& table, int nesting, int dot_width); void PutTable(const RichTable& table, int nesting, int dot_width);
void PutParts(const Array<RichPara::Part>& parts, void PutParts(const Array<RichPara::Part>& 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() { stream.Put('{'); }
void Begin(const char *cmd) { Begin(); Command(cmd); } void Begin(const char *cmd) { Begin(); Command(cmd); }

View file

@ -163,25 +163,6 @@ public:
~ImageGdk(); ~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 FilesClipFromUrisFree(gchar **uris);
String ImageClipFromPixbufUnref(GdkPixbuf *pixbuf); String ImageClipFromPixbufUnref(GdkPixbuf *pixbuf);
@ -200,13 +181,23 @@ Vector<int> GetPropertyInts(GdkWindow *w, const char *property);
#define GUIPLATFORM_CTRL_TOP_DECLS \ #define GUIPLATFORM_CTRL_TOP_DECLS \
GtkWidget *window; \ GtkWidget *window; \
GtkWidget *header = nullptr; \ GtkWidget *header = nullptr; \
GtkWidget *drawing_area = nullptr; \ GtkWidget *client = nullptr; \
GtkWidget *header_area = nullptr; \
Ptr<Ctrl> hdr_screen[2]; \
GtkIMContext *im_context = nullptr; \ GtkIMContext *im_context = nullptr; \
GtkIMContext *im_context_simple; \ GtkIMContext *im_context_simple; \
GtkIMContext *im_context_multi; \ GtkIMContext *im_context_multi; \
GtkCSD csd; \
int64 cursor_id; \ int64 cursor_id; \
int 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 <CtrlCore/GtkCtrl.h> #define GUIPLATFORM_CTRL_DECLS_INCLUDE <CtrlCore/GtkCtrl.h>

View file

@ -97,6 +97,10 @@ void Ctrl::UseWayland()
sUseWayland = true; sUseWayland = true;
} }
extern Event<const TopWindow *, TopWindow::CustomTitleBarMetrics&> custom_titlebar_metrics__;
extern Function<bool (const TopWindow *)> is_custom_titlebar__;
extern Function<Ctrl *(TopWindow *, Color, int)> custom_titlebar_make__;
bool InitGtkApp(int argc, char **argv, const char **envptr) bool InitGtkApp(int argc, char **argv, const char **envptr)
{ {
LLOG(rmsecs() << " InitGtkApp"); 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); g_signal_connect_swapped(settings, "notify::gtk-application-prefer-dark-theme", G_CALLBACK(Ctrl::ThemeChanged), NULL);
} }
TopWindow::Init();
return true; return true;
} }

View file

@ -1,76 +0,0 @@
#include <CtrlCore/CtrlCore.h>
#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

View file

@ -25,13 +25,20 @@ GdkDevice *Ctrl::GetMouseDevice()
#endif #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() bool Ctrl::MouseIsGrabbed()
{ {
if(grabwindow && grabwindow->top && grabwindow->utop->csd) // (*)
return true;
return gdk_display_device_is_grabbed(gdk_display_get_default(), GetMouseDevice()); return gdk_display_device_is_grabbed(gdk_display_get_default(), GetMouseDevice());
} }
bool Ctrl::GrabMouse() bool Ctrl::GrabMouse()
{ {
if(top && utop->csd) // (*)
return true;
return return
#if GTK_CHECK_VERSION(3, 20, 0) #if GTK_CHECK_VERSION(3, 20, 0)
gdk_seat_grab(GetSeat(), gdk(), GDK_SEAT_CAPABILITY_ALL_POINTING, true, NULL, NULL, NULL, 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() void Ctrl::UngrabMouse()
{ {
if(grabwindow && grabwindow->top && grabwindow->utop->csd) // (*)
return;
#if GTK_CHECK_VERSION(3, 20, 0) #if GTK_CHECK_VERSION(3, 20, 0)
gdk_seat_ungrab(GetSeat()); gdk_seat_ungrab(GetSeat());
#else #else
gdk_device_ungrab(GetMouseDevice(), GDK_CURRENT_TIME); gdk_device_ungrab(GetMouseDevice(), GDK_CURRENT_TIME);
#endif #endif
} }
@ -92,7 +101,6 @@ bool Ctrl::ReleaseWndCapture0()
{ {
GuiLock __; GuiLock __;
ASSERT(IsMainThread()); ASSERT(IsMainThread());
LLOG("ReleaseWndCapture");
if(grabwindow) { if(grabwindow) {
UngrabMouse(); UngrabMouse();
grabwindow = NULL; grabwindow = NULL;

View file

@ -6,6 +6,46 @@ namespace Upp {
#define LLOG(x) // DLOG(x) #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<Color, GtkCssProvider *> 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) void Ctrl::Create(Ctrl *owner, bool popup)
{ {
MemoryIgnoreLeaksBlock ___; MemoryIgnoreLeaksBlock ___;
@ -14,7 +54,7 @@ void Ctrl::Create(Ctrl *owner, bool popup)
LLOG("Create " << Name() << " " << GetRect()); LLOG("Create " << Name() << " " << GetRect());
ASSERT(!IsChild() && !IsOpen()); ASSERT(!IsChild() && !IsOpen());
LLOG("Ungrab1"); LLOG("Ungrab1");
Top *top = new Top; Top *top = new Top;
SetTop(top); SetTop(top);
top->window = gtk_window_new(popup && owner ? GTK_WINDOW_POPUP : GTK_WINDOW_TOPLEVEL); 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.ctrl = this;
w.gtk = top->window; w.gtk = top->window;
w.gdk = nullptr; w.gdk = nullptr;
w.drawing_area = nullptr;
TopWindow *tw = dynamic_cast<TopWindow *>(this); TopWindow *tw = dynamic_cast<TopWindow *>(this);
GdkWindowTypeHint type_hint; GdkWindowTypeHint type_hint;
@ -45,58 +84,79 @@ void Ctrl::Create(Ctrl *owner, bool popup)
gtk_window_set_type_hint(gtk(), type_hint); gtk_window_set_type_hint(gtk(), type_hint);
Rect r = GetRect(); Rect r = GetRect();
bool custom_bar = tw && tw->custom_bar;
top->csd.Create(type_hint); static bool need_csd = IsWayland() && GetEnv("XDG_SESSION_DESKTOP") != "KDE";
if(top->csd.IsEnabled()) { top->csd = !popup && (need_csd || custom_bar);
top->header = gtk_header_bar_new(); #ifndef flagFORCE_CSD // Force using client side decorations even when server side is available
if (findarg(type_hint, GDK_WINDOW_TYPE_HINT_POPUP_MENU) >= 0) if(tw && tw->force_csd)
gtk_widget_set_size_request(top->header, 1, 1); #endif
else top->csd = !popup;
gtk_header_bar_set_show_close_button(GTK_HEADER_BAR(top->header), TRUE); 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_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(); gtk_widget_set_can_focus(top->client, TRUE);
r.right += top->csd.RightMargin();
r.top -= top->csd.TopMargin(); gtk_container_add(GTK_CONTAINER(top->window), top->client);
r.bottom += top->csd.BottomMargin();
g_signal_connect(top->window, "delete-event", G_CALLBACK(GtkEvent), (gpointer)(uintptr_t)top->id); g_signal_connect(top->window, "delete-event", G_CALLBACK(GtkEvent), (gpointer)(uintptr_t)top->id);
} }
else { else {
top->drawing_area = top->window; ONCELOCK {
UpdateWindowFrameMargins();
}
top->client = top->window;
} }
w.drawing_area = top->drawing_area;
top->cursor_id = -1; top->cursor_id = -1;
gtk_widget_set_events(top->drawing_area, GDK_ALL_EVENTS_MASK & ~GDK_POINTER_MOTION_HINT_MASK & ~GDK_SMOOTH_SCROLL_MASK); auto SetupEvents = [&](GtkWidget *w) {
g_signal_connect(top->drawing_area, "event", G_CALLBACK(GtkEvent), (gpointer)(uintptr_t)top->id); gtk_widget_set_events(w, GDK_ALL_EVENTS_MASK & ~GDK_POINTER_MOTION_HINT_MASK & ~GDK_SMOOTH_SCROLL_MASK);
g_signal_connect(top->drawing_area, "draw", G_CALLBACK(GtkDraw), (gpointer)(uintptr_t)top->id); };
SetupEvents(GTK_WIDGET(gtk()));
GdkWindowTypeHint hint = gtk_window_get_type_hint(gtk()); g_signal_connect(gtk(), "event", G_CALLBACK(TopGtkEvent), (gpointer)(uintptr_t)top->id);
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 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); w.gdk = gtk_widget_get_window(top->window);
if(owner && owner->top) if(owner && owner->top)
gtk_window_set_transient_for(gtk(), owner->gtk()); gtk_window_set_transient_for(gtk(), owner->gtk());
gtk_widget_set_app_paintable(top->window, TRUE); 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, "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); g_signal_connect(top->im_context, "commit", G_CALLBACK(IMCommit), (gpointer)(uintptr_t)top->id);
WndSetPos(r);
WndShow(IsShown()); WndShow(IsShown());
SweepConfigure(true); SweepConfigure(true);
@ -124,12 +185,13 @@ void Ctrl::Create(Ctrl *owner, bool popup)
StateH(OPEN); StateH(OPEN);
GdkModifierType mod; Point m = prev_mouse_pos;
Point m = GetMouseInfo(gdk(), mod);
r = GetWndScreenRect(); r = GetWndScreenRect();
if(r.Contains(m)) if(r.Contains(m))
DispatchMouse(MOUSEMOVE, m); DispatchMouse(MOUSEMOVE, m);
top->sync_rect = true;
WndRectsSync();
RefreshLayoutDeep(); RefreshLayoutDeep();
} }
@ -163,10 +225,10 @@ void Ctrl::WndDestroy()
if(q >= 0) if(q >= 0)
wins.Remove(q); wins.Remove(q);
if(owner) { 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 // 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. // 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(); owner->WndUpdate();
} }
@ -219,3 +281,4 @@ void Ctrl::PopUp(Ctrl *owner, bool savebits, bool activate, bool, bool)
} }
#endif #endif

View file

@ -12,12 +12,6 @@
static void ThemeChanged(void *); 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); bool DispatchMouseIn(int act, int zd);
Image GtkMouseEvent(int action, int act, int zd); Image GtkMouseEvent(int action, int act, int zd);
void GtkButtonEvent(int action); void GtkButtonEvent(int action);
@ -31,6 +25,8 @@ _DBG_
static gboolean GtkEvent(GtkWidget *widget, GdkEvent *key, gpointer user_data); static gboolean GtkEvent(GtkWidget *widget, GdkEvent *key, gpointer user_data);
static gboolean GtkDraw(GtkWidget *widget, cairo_t *cr, gpointer 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 Point GetMouseInfo(GdkWindow *win, GdkModifierType& mod);
static GdkDevice *GetMouseDevice(); static GdkDevice *GetMouseDevice();
#if GTK_CHECK_VERSION(3, 20, 0) #if GTK_CHECK_VERSION(3, 20, 0)
@ -39,7 +35,7 @@ _DBG_
static bool MouseIsGrabbed(); static bool MouseIsGrabbed();
bool GrabMouse(); bool GrabMouse();
static void UngrabMouse(); static void UngrabMouse();
static int scale; // in case GUI is scaling (e.g. in UHD mode) static int scale; // in case GUI is scaling (e.g. in UHD mode)
enum { enum {
@ -47,7 +43,7 @@ _DBG_
EVENT_TEXT, EVENT_TEXT,
EVENT_FOCUS_CHANGE, EVENT_FOCUS_CHANGE,
}; };
struct GEvent0 { struct GEvent0 {
int time; int time;
int windowid; int windowid;
@ -56,6 +52,9 @@ _DBG_
Point mousepos; Point mousepos;
guint state; guint state;
int count; int count;
GdkDevice *device;
double x_root;
double y_root;
bool pen; bool pen;
bool pen_barrel; bool pen_barrel;
@ -65,7 +64,7 @@ _DBG_
double pen_rotation; double pen_rotation;
Pointf pen_tilt; Pointf pen_tilt;
}; };
struct GEvent : Moveable<GEvent>, GEvent0 { struct GEvent : Moveable<GEvent>, GEvent0 {
GdkEvent *event; GdkEvent *event;
@ -77,12 +76,11 @@ _DBG_
GEvent(); GEvent();
~GEvent(); ~GEvent();
}; };
struct Win : Moveable<Win> { struct Win : Moveable<Win> {
int id; int id;
GtkWidget *gtk; GtkWidget *gtk;
GdkWindow *gdk; GdkWindow *gdk;
GtkWidget *drawing_area;
Ptr<Ctrl> ctrl; Ptr<Ctrl> ctrl;
Vector<Rect> invalid; // areas invalidated to be processed at next opportunity Vector<Rect> invalid; // areas invalidated to be processed at next opportunity
}; };
@ -90,7 +88,12 @@ _DBG_
void Proc(); void Proc();
bool SweepConfigure(bool wait); bool SweepConfigure(bool wait);
bool SweepFocus(bool wait); bool SweepFocus(bool wait);
void SyncWndRect(const Rect& rect); void SyncWndRect();
void InvalidateScreenRect();
void SetCustomBarColor(Color c);
void WndRectsSync() const;
static BiVector<GEvent> Events; static BiVector<GEvent> Events;
static Vector<Ptr<Ctrl>> activePopup; // created with 'activate' flag - usually menu static Vector<Ptr<Ctrl>> activePopup; // created with 'activate' flag - usually menu
@ -106,13 +109,13 @@ _DBG_
static int FindCtrl(Ctrl *ctrl); static int FindCtrl(Ctrl *ctrl);
static int FindGtkWindow(GtkWidget *gtk); static int FindGtkWindow(GtkWidget *gtk);
static int FindGdkWindow(GdkWindow *gdk); static int FindGdkWindow(GdkWindow *gdk);
static Ctrl *GetTopCtrlFromId(int id); static Ctrl *GetTopCtrlFromId(int id);
static Ctrl *GetTopCtrlFromId(gpointer user_data) { return GetTopCtrlFromId((uint32)(uintptr_t)user_data); } static Ctrl *GetTopCtrlFromId(gpointer user_data) { return GetTopCtrlFromId((uint32)(uintptr_t)user_data); }
static void SyncPopupCapture(); static void SyncPopupCapture();
void ReleasePopupCapture(); void ReleasePopupCapture();
static void FocusSync(); static void FocusSync();
static gboolean TimeHandler(GtkWidget *); static gboolean TimeHandler(GtkWidget *);
static void InvalidateMousePos(); static void InvalidateMousePos();
@ -120,9 +123,6 @@ _DBG_
static void StartGrabPopup(); static void StartGrabPopup();
static bool ReleaseWndCapture0(); static bool ReleaseWndCapture0();
static void DoCancelPreedit(); static void DoCancelPreedit();
static Rect frameMargins;
static Rect GetFrameMargins();
static Index<String> dnd_targets; static Index<String> dnd_targets;
static String dnd_text_target; static String dnd_text_target;
@ -141,7 +141,7 @@ _DBG_
static Vector<String> dnd_fmts; static Vector<String> dnd_fmts;
static int dnd_result; static int dnd_result;
static Image dnd_icon; static Image dnd_icon;
static void GtkSelectionDataSet(GtkSelectionData *selection_data, const String& fmt, const String& data); 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 GtkGetClipData(GtkClipboard *clipboard, GtkSelectionData *selection_data, guint info, gpointer user_data);
static void AddFmt(GtkTargetList *list, const String& fmt, int info); static void AddFmt(GtkTargetList *list, const String& fmt, int info);
@ -169,6 +169,22 @@ _DBG_
guint time, gpointer user_data, bool paste); guint time, gpointer user_data, bool paste);
static bool ProcessInvalids(); 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 bool InitGtkApp(int argc, char **argv, const char **envptr);
friend void GuiPlatformGripResize(TopWindow *q); friend void GuiPlatformGripResize(TopWindow *q);
@ -188,30 +204,35 @@ public: // really private:
static Gclipboard& gclipboard(); static Gclipboard& gclipboard();
static Gclipboard& gselection(); static Gclipboard& gselection();
static String RenderPrimarySelection(const Value& fmt); static String RenderPrimarySelection(const Value& fmt);
static Vector<Event<>> hotkey; static Vector<Event<>> hotkey;
static Vector<dword> keyhot; static Vector<dword> keyhot;
static Vector<dword> modhot; static Vector<dword> modhot;
static guint MouseState; static guint prev_state;
static Point prev_mouse_pos;
static int SCL(int x) { return scale * x; } 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 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 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; } static int GetCaretBlinkTime() { return 500; }
public: public:
static void EndSession() {} static void EndSession() {}
static bool IsEndSession() { return false; } static bool IsEndSession() { return false; }
static void PanicMsgBox(const char *title, const char *text); static void PanicMsgBox(const char *title, const char *text);
static bool IsX11(); static bool IsX11();
static bool IsWayland(); static bool IsWayland();
static bool IsRunningOnWayland(); static bool IsRunningOnWayland();
static bool IsXWayland() { return IsX11() && IsRunningOnWayland(); } static bool IsXWayland() { return IsX11() && IsRunningOnWayland(); }
static void UseWayland(); static void UseWayland();
static Point CurrentMousePos; static Point CurrentMousePos;
static guint CurrentState; static guint CurrentState;
static guint32 CurrentTime; static guint32 CurrentTime;

View file

@ -0,0 +1,207 @@
#include <CtrlCore/CtrlCore.h>
#ifdef GUI_GTK
namespace Upp {
extern Event<const TopWindow *, TopWindow::CustomTitleBarMetrics&> custom_titlebar_metrics__;
extern Function<bool (const TopWindow *)> is_custom_titlebar__;
extern Function<Ctrl *(TopWindow *, Color, int)> 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<TopWindow *>(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

View file

@ -288,7 +288,10 @@ PasteClip Ctrl::GtkDnd(GtkWidget *widget, GdkDragContext *context, gint x, gint
if(w) { if(w) {
GetMouseInfo(gdk_get_default_root_window(), mod); GetMouseInfo(gdk_get_default_root_window(), mod);
CurrentState = 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); w->DnD(CurrentMousePos, clip);
} }
gdk_drag_status(context, clip.IsAccepted() ? clip.GetAction() == DND_MOVE ? GDK_ACTION_MOVE gdk_drag_status(context, clip.IsAccepted() ? clip.GetAction() == DND_MOVE ? GDK_ACTION_MOVE
@ -358,3 +361,4 @@ void Ctrl::DndExit()
} }
#endif #endif

View file

@ -9,7 +9,7 @@
namespace Upp { namespace Upp {
#define LLOG(x) // DLOG(x) #define LLOG(x) // DLOG(x)
// #define LOG_EVENTS _DBG_ // #define LOG_EVENTS _DBG_
BiVector<Ctrl::GEvent> Ctrl::Events; BiVector<Ctrl::GEvent> Ctrl::Events;
@ -17,7 +17,6 @@ Point Ctrl::CurrentMousePos;
guint Ctrl::CurrentState; guint Ctrl::CurrentState;
guint32 Ctrl::CurrentTime; guint32 Ctrl::CurrentTime;
Ctrl::GEvent Ctrl::CurrentEvent; Ctrl::GEvent Ctrl::CurrentEvent;
guint Ctrl::MouseState;
bool GetShift() { return Ctrl::CurrentState & GDK_SHIFT_MASK; } bool GetShift() { return Ctrl::CurrentState & GDK_SHIFT_MASK; }
bool GetCtrl() { return Ctrl::CurrentState & GDK_CONTROL_MASK; } bool GetCtrl() { return Ctrl::CurrentState & GDK_CONTROL_MASK; }
@ -87,10 +86,26 @@ bool Ctrl::ProcessInvalids()
GuiLock __; GuiLock __;
if(invalids) { if(invalids) {
for(Win& win : wins) { for(Win& win : wins) {
for(const Rect& r : win.invalid) Top *top = win.ctrl->GetTop();
if(win.drawing_area && win.ctrl) TopWindow *tw = dynamic_cast<TopWindow *>(~win.ctrl);
gdk_window_invalidate_rect(gtk_widget_get_window(win.drawing_area), if(top)
GdkRect(Nvl(r, win.ctrl->GetRect().GetSize())), TRUE); 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(); win.invalid.Clear();
} }
invalids = false; invalids = false;
@ -104,8 +119,15 @@ gboolean Ctrl::GtkDraw(GtkWidget *widget, cairo_t *cr, gpointer user_data)
Ctrl *p = GetTopCtrlFromId(user_data); Ctrl *p = GetTopCtrlFromId(user_data);
if(p) { if(p) {
p->fullrefresh = false; p->fullrefresh = false;
cairo_scale(cr, 1.0 / scale, 1.0 / scale); // cancel scaling to be pixel perfect 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); SystemDraw w(cr);
painting = true; painting = true;
@ -113,7 +135,6 @@ gboolean Ctrl::GtkDraw(GtkWidget *widget, cairo_t *cr, gpointer user_data)
double x1, y1, x2, y2; double x1, y1, x2, y2;
cairo_clip_extents (cr, &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)); 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); cairo_rectangle_list_t *list = cairo_copy_clip_rectangle_list(cr);
if(list->status == CAIRO_STATUS_SUCCESS && list->num_rectangles < 10) { 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); cairo_rectangle_list_destroy(list);
TopWindow *tw = dynamic_cast<TopWindow *>(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); p->UpdateArea(w, r);
w.End(); w.End();
w.End();
painting = false; painting = false;
} }
return true; 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<int, const char *> *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<TopWindow *>(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) gboolean Ctrl::GtkEvent(GtkWidget *widget, GdkEvent *event, gpointer user_data)
{ {
GuiLock __; GuiLock __;
@ -141,6 +211,7 @@ gboolean Ctrl::GtkEvent(GtkWidget *widget, GdkEvent *event, gpointer user_data)
bool retval = true; bool retval = true;
Value value; Value value;
Ctrl *p = GetTopCtrlFromId(user_data); Ctrl *p = GetTopCtrlFromId(user_data);
Top *top = p ? p->GetTop() : nullptr;
#ifdef LOG_EVENTS #ifdef LOG_EVENTS
String ev = "? " + AsString((int)event->type); String ev = "? " + AsString((int)event->type);
Tuple2<int, const char *> *f = FindTuple(xEvent, __countof(xEvent), event->type); Tuple2<int, const char *> *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)); LOG(rmsecs() << " FETCH EVENT " << ev << " ctrl: " << Name(p));
#endif #endif
if(!p)
return false;
switch(event->type) { switch(event->type) {
case GDK_DELETE: case GDK_DELETE:
p->CancelPreedit(); p->CancelPreedit();
@ -175,12 +248,13 @@ gboolean Ctrl::GtkEvent(GtkWidget *widget, GdkEvent *event, gpointer user_data)
if(IsNull(value)) if(IsNull(value))
return false; return false;
break; break;
/* this only adds issues
case GDK_2BUTTON_PRESS: case GDK_2BUTTON_PRESS:
p->CancelPreedit(); p->CancelPreedit();
value = DoButtonEvent(event, true); value = DoButtonEvent(event, true);
if(IsNull(value)) if(IsNull(value))
return false; return false;
break; break;*/
case GDK_BUTTON_RELEASE: case GDK_BUTTON_RELEASE:
p->CancelPreedit(); p->CancelPreedit();
value = DoButtonEvent(event, false); value = DoButtonEvent(event, false);
@ -212,19 +286,16 @@ gboolean Ctrl::GtkEvent(GtkWidget *widget, GdkEvent *event, gpointer user_data)
case GDK_KEY_RELEASE: case GDK_KEY_RELEASE:
key = (GdkEventKey *)event; key = (GdkEventKey *)event;
value << (int) key->keyval << (int) key->hardware_keycode; value << (int) key->keyval << (int) key->hardware_keycode;
if(pressed) { if(pressed && top && gtk_im_context_filter_keypress(top->im_context, key))
p = GetTopCtrlFromId(user_data); return true;
if(p) {
Top *top = p->GetTop();
if(top && gtk_im_context_filter_keypress(top->im_context, key))
return true;
}
}
break; break;
// case GDK_EXPOSE:
case GDK_CONFIGURE: { case GDK_CONFIGURE: {
retval = false; retval = false;
GdkEventConfigure *e = (GdkEventConfigure *)event; if(p)
value = SCL(e->x, e->y, e->width, e->height); p->InvalidateScreenRect();
if(top)
top->draw_after_configure = true;
break; break;
} }
default: default:
@ -237,7 +308,7 @@ gboolean Ctrl::GtkEvent(GtkWidget *widget, GdkEvent *event, gpointer user_data)
int Ctrl::DoButtonEvent(GdkEvent *event, bool press) int Ctrl::DoButtonEvent(GdkEvent *event, bool press)
{ {
GdkEventButton *e = (GdkEventButton *)event; 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) { if(e->button >= 1 && e->button <= 3) {
int m = mask[e->button - 1]; int m = mask[e->button - 1];
if(press) if(press)
@ -246,7 +317,10 @@ int Ctrl::DoButtonEvent(GdkEvent *event, bool press)
MouseState &= ~m; MouseState &= ~m;
return e->button; 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() Ctrl::GEvent::GEvent()
@ -294,7 +368,8 @@ void Ctrl::GEvent::operator=(const GEvent& e)
Set(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) 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)); GdkDevice *pointer = gdk_seat_get_pointer (gdk_display_get_default_seat (display));
double x, y; double x, y;
gdk_window_get_device_position_double (win, pointer, &x, &y, &mod); 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 #else
gint x, y; gint x, y;
gdk_window_get_pointer(win, &x, &y, &mod); 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.windowid = (uint32)(uintptr_t)user_data;
e.type = type; e.type = type;
e.value = value; e.value = value;
GdkModifierType mod; e.state = prev_state;
e.mousepos = GetMouseInfo(gdk_get_default_root_window(), mod); bool press = false;
if(event && event->type == GDK_MOTION_NOTIFY){ if(event)
GdkEventMotion *mevent = (GdkEventMotion *)event; switch(event->type) {
e.mousepos = s_mousepos = Point(SCL(mevent->x_root), SCL(mevent->y_root)); case GDK_MOTION_NOTIFY: {
} GdkEventMotion *mevent = (GdkEventMotion *)event;
if(event && event->type == GDK_LEAVE_NOTIFY){ prev_mouse_pos = Point(SCL(mevent->x_root), SCL(mevent->y_root));
GdkEventCrossing *mevent = (GdkEventCrossing *)event; prev_state = e.state = mevent->state;
e.mousepos = s_mousepos = Point(SCL(mevent->x_root), SCL(mevent->y_root)); e.x_root = mevent->x_root;
} e.y_root = mevent->y_root;
e.state = (mod & ~(GDK_BUTTON1_MASK|GDK_BUTTON2_MASK|GDK_BUTTON3_MASK)) | MouseState; 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.count = 1;
e.event = NULL; e.event = nullptr;
e.device = nullptr;
#if GTK_CHECK_VERSION(3, 22, 0) #if GTK_CHECK_VERSION(3, 22, 0)
GdkDevice *d = event ? gdk_event_get_source_device(event) : NULL; e.device = event ? gdk_event_get_source_device(event) : NULL;
if(d && findarg(gdk_device_get_source(d), GDK_SOURCE_PEN, GDK_SOURCE_TOUCHSCREEN) >= 0) { if(e.device && findarg(gdk_device_get_source(e.device), GDK_SOURCE_PEN, GDK_SOURCE_TOUCHSCREEN) >= 0) {
e.pen = true; e.pen = true;
e.pen_barrel = MouseState & GDK_BUTTON3_MASK; e.pen_barrel = e.state & GDK_BUTTON3_MASK;
double *axes = NULL; double *axes = NULL;
switch(event->type){ switch(event->type){
case GDK_BUTTON_PRESS: case GDK_BUTTON_PRESS:
@ -350,18 +478,16 @@ void Ctrl::AddEvent(gpointer user_data, int type, const Value& value, GdkEvent *
axes = ((GdkEventButton *)event)->axes; axes = ((GdkEventButton *)event)->axes;
break; break;
case GDK_MOTION_NOTIFY:{ 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; axes = ((GdkEventMotion *)event)->axes;
break; break;
} }
default:; default:;
} }
if(axes) { if(axes) {
if(!gdk_device_get_axis(d, axes, GDK_AXIS_PRESSURE, &e.pen_pressure)) e.pen_pressure=Null; if(!gdk_device_get_axis(e.device, 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(e.device, 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(e.device, 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_YTILT, &e.pen_tilt.y)) e.pen_tilt.y=Null;
} }
} }
#endif #endif
@ -488,10 +614,12 @@ void Ctrl::GtkButtonEvent(int action)
GtkMouseEvent(action, act, 0); GtkMouseEvent(action, act, 0);
} }
bool Ctrl::custom_titlebar_drag_click;
void Ctrl::Proc() void Ctrl::Proc()
{ {
#ifdef LOG_EVENTS #ifdef LOG_EVENTS
String ev = "?"; String ev = "?" + AsString(CurrentEvent.type);
Tuple2<int, const char *> *f = FindTuple(xEvent, __countof(xEvent), CurrentEvent.type); Tuple2<int, const char *> *f = FindTuple(xEvent, __countof(xEvent), CurrentEvent.type);
if(f) if(f)
ev = f->b; ev = f->b;
@ -543,9 +671,22 @@ void Ctrl::Proc()
} }
#endif #endif
for(Ctrl *q : GetTopCtrls())
q->SyncWndRect();
TopWindow *tw = dynamic_cast<TopWindow *>(this);
auto IsCustomBarAction = [&] {
return tw && tw->custom_bar_frame && tw->custom_bar_frame->GetScreenRect().Contains(GetMousePos())
&& tw->IsCustomTitleBarDragArea(GetMousePos() - GetScreenRect().TopLeft());
};
switch(CurrentEvent.type) { switch(CurrentEvent.type) {
case GDK_MOTION_NOTIFY: case GDK_MOTION_NOTIFY:
GtkMouseEvent(MOUSEMOVE, MOUSEMOVE, 0); GtkMouseEvent(MOUSEMOVE, MOUSEMOVE, 0);
SyncPreventCustomBarDragPrevention();
if(GetMouseLeft() && custom_titlebar_drag_click && IsCustomBarAction())
tw->DoMoveWindow();
break; break;
case GDK_BUTTON_PRESS: case GDK_BUTTON_PRESS:
if(CurrentEvent.value == 8) { if(CurrentEvent.value == 8) {
@ -565,17 +706,18 @@ void Ctrl::Proc()
ignoremouseup = false; ignoremouseup = false;
} }
custom_titlebar_drag_click = CurrentEvent.value == 1 && IsCustomBarAction();
if(!ignoreclick) { if(!ignoreclick) {
bool dbl = msecs(clicktime) < 250; bool dbl = msecs(clicktime) < 250;
clicktime = dbl ? clicktime - 1000 : msecs(); clicktime = dbl ? clicktime - 1000 : msecs();
GtkButtonEvent(dbl ? DOUBLE : DOWN); if(dbl && IsCustomBarAction())
tw->DoZoom();
else
GtkButtonEvent(dbl ? DOUBLE : DOWN);
} }
break; break;
/* case GDK_2BUTTON_PRESS: case GDK_BUTTON_RELEASE:
if(!ignoreclick)
GtkButtonEvent(DOUBLE);
break;
*/ case GDK_BUTTON_RELEASE:
if(CurrentEvent.value == 8) { if(CurrentEvent.value == 8) {
DispatchKey(K_MOUSE_BACKWARD|K_KEYUP, 1); DispatchKey(K_MOUSE_BACKWARD|K_KEYUP, 1);
break; break;
@ -589,11 +731,17 @@ void Ctrl::Proc()
else else
if(!dnd_events) if(!dnd_events)
GtkButtonEvent(UP); GtkButtonEvent(UP);
if(!GetMouseRight() && !GetMouseMiddle() && !GetMouseLeft()) {
StopGrabPopup();
ReleaseWndCapture0();
}
break; break;
case GDK_SCROLL: { case GDK_SCROLL: {
Point delta = CurrentEvent.value; Point delta = CurrentEvent.value;
if(delta.y!=0.0) GtkMouseEvent(MOUSEWHEEL, MOUSEWHEEL, delta.y); if(delta.y)
if(delta.x!=0.0) GtkMouseEvent(MOUSEHWHEEL, MOUSEHWHEEL, delta.x); GtkMouseEvent(MOUSEWHEEL, MOUSEWHEEL, delta.y);
if(delta.x)
GtkMouseEvent(MOUSEHWHEEL, MOUSEHWHEEL, delta.x);
break; break;
} }
case GDK_KEY_PRESS: case GDK_KEY_PRESS:
@ -680,7 +828,6 @@ void Ctrl::Proc()
} }
kv += K_DELTA; kv += K_DELTA;
} }
GetKeyDesc(kv);
if(GetShift() && kv != K_SHIFT_KEY) if(GetShift() && kv != K_SHIFT_KEY)
kv |= K_SHIFT; kv |= K_SHIFT;
if(GetCtrl() && kv != K_CTRL_KEY) if(GetCtrl() && kv != K_CTRL_KEY)
@ -710,18 +857,18 @@ void Ctrl::Proc()
activeCtrl = NULL; activeCtrl = NULL;
break; break;
case GDK_DELETE: { case GDK_DELETE: {
TopWindow *w = dynamic_cast<TopWindow *>(this); if(tw) {
if(w) {
if(IsEnabled()) { if(IsEnabled()) {
IgnoreMouseUp(); IgnoreMouseUp();
w->WhenClose(); tw->WhenClose();
} }
} }
return; return;
} }
case GDK_CONFIGURE: case GDK_CONFIGURE:
SyncWndRect(CurrentEvent.value); InvalidateScreenRect();
break; SyncWndRect();
return;
default: default:
return; return;
} }
@ -729,13 +876,23 @@ void Ctrl::Proc()
_this->PostInput(); _this->PostInput();
} }
void Ctrl::SyncWndRect(const Rect& rect) void Ctrl::SyncWndRect()
{ {
WndRectsSync();
Rect rect = GetWndScreenRect();
TopWindow *tw = dynamic_cast<TopWindow *>(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) if(GetRect() != rect)
SetWndRect(rect); SetWndRect(rect);
TopWindow *w = dynamic_cast<TopWindow *>(this);
if(w && w->state == TopWindow::OVERLAPPED)
w->overlapped = rect;
} }
bool Ctrl::ProcessEvent0(bool *quit, bool fetch) bool Ctrl::ProcessEvent0(bool *quit, bool fetch)
@ -769,6 +926,7 @@ bool Ctrl::ProcessEvent0(bool *quit, bool fetch)
Value val = e.value; Value val = e.value;
Events.DropHead(); Events.DropHead();
Ctrl *w = GetTopCtrlFromId(e.windowid); Ctrl *w = GetTopCtrlFromId(e.windowid);
SetCustomBarDragPrevention();
FocusSync(); FocusSync();
CaptureSync(); CaptureSync();
if(w) { if(w) {
@ -839,8 +997,6 @@ void Ctrl::EventLoop(Ctrl *ctrl)
ASSERT(LoopLevel == 0 || ctrl); ASSERT(LoopLevel == 0 || ctrl);
LoopLevel++; LoopLevel++;
LLOG("Entering event loop at level " << LoopLevel << LOG_BEGIN); LLOG("Entering event loop at level " << LoopLevel << LOG_BEGIN);
if(!GetMouseRight() && !GetMouseMiddle() && !GetMouseLeft())
ReleaseCtrlCapture();
Ptr<Ctrl> ploop; Ptr<Ctrl> ploop;
if(ctrl) { if(ctrl) {
ploop = LoopCtrl; ploop = LoopCtrl;

View file

@ -6,39 +6,47 @@ namespace Upp {
#define LLOG(x) // DLOG(x) #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() void TopWindow::SyncSizeHints()
{ {
GuiLock __; GuiLock __;
if(!top) if(!top)
return; return;
GdkGeometry m;
Size sz0 = GetRect().GetSize(); Size sz0 = GetRect().GetSize();
LLOG("SyncSizeHints sz0: " << sz0 << ", sizeable: " << sizeable << ", min: " << GetMinSize() << ", max: " << GetMaxSize()); 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(); Top *top = GetTop();
if(top) { if(top) {
gtk_window_set_geometry_hints(gtk(), top->window, &m, int mcx = 0;
GdkWindowHints(GDK_HINT_MIN_SIZE|GDK_HINT_MAX_SIZE)); 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); gtk_widget_set_size_request(top->window, m.min_width, m.min_height);
} }
SyncCustomBar();
} }
void TopWindow::SyncTitle() void TopWindow::SyncTitle()
@ -73,8 +81,8 @@ void TopWindow::CenterRect(Ctrl *owner)
SetupRect(owner); SetupRect(owner);
if(owner && center == 1 || center == 2) { if(owner && center == 1 || center == 2) {
Size sz = GetRect().Size(); Size sz = GetRect().Size();
Rect wr = owner? owner->GetWorkArea() : Ctrl::GetPrimaryWorkArea(); Rect wr = owner ? owner->GetWorkArea() : Ctrl::GetPrimaryWorkArea();
Rect fm = GetFrameMargins(); Rect fm = frameMargins;
Rect r = (center == 1 && owner ? owner->GetRect() : wr) Rect r = (center == 1 && owner ? owner->GetRect() : wr)
.CenterRect(sz); .CenterRect(sz);
wr.left += fm.left; wr.left += fm.left;
@ -107,6 +115,7 @@ gboolean TopWindow::StateEvent(GtkWidget *widget, GdkEventWindowState *event, gp
{ {
TopWindow *w = (TopWindow *)user_data; TopWindow *w = (TopWindow *)user_data;
dword h = event->new_window_state; dword h = event->new_window_state;
int prev = w->state;
if(h & GDK_WINDOW_STATE_FULLSCREEN) if(h & GDK_WINDOW_STATE_FULLSCREEN)
w->state = FULLSCREEN; w->state = FULLSCREEN;
else else
@ -119,7 +128,14 @@ gboolean TopWindow::StateEvent(GtkWidget *widget, GdkEventWindowState *event, gp
w->state = OVERLAPPED; w->state = OVERLAPPED;
w->overlapped = w->GetRect(); w->overlapped = w->GetRect();
} }
LLOG("StateEvent " << prev << " -> " << (int)w->state);
w->topmost = h & GDK_WINDOW_STATE_ABOVE; 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; return FALSE;
} }
@ -145,13 +161,6 @@ void TopWindow::Open(Ctrl *owner)
state = OVERLAPPED; state = OVERLAPPED;
SetMode(q); SetMode(q);
SyncTopMost(); 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() void TopWindow::Open()
@ -178,29 +187,24 @@ void TopWindow::SetMode(int mode)
if(w) if(w)
switch(state) { switch(state) {
case MINIMIZED: case MINIMIZED:
gtk_window_deiconify(w); fullscreen = false;
break;
case MAXIMIZED:
gtk_window_unmaximize(w);
break;
case FULLSCREEN:
gtk_window_unfullscreen(w);
break;
}
state = mode;
if(w)
switch(state) {
case MINIMIZED:
gtk_window_iconify(w); gtk_window_iconify(w);
break; break;
case MAXIMIZED: case MAXIMIZED:
fullscreen = false;
gtk_window_deiconify(w);
gtk_window_maximize(w); gtk_window_maximize(w);
break; break;
case OVERLAPPED:
fullscreen = false;
gtk_window_deiconify(w);
gtk_window_unmaximize(w);
break;
case FULLSCREEN: case FULLSCREEN:
gtk_window_fullscreen(w); gtk_window_fullscreen(w);
fullscreen = true;
break; break;
} }
fullscreen = state == FULLSCREEN;
} }
void TopWindow::Minimize(bool effect) void TopWindow::Minimize(bool effect)
@ -263,7 +267,7 @@ void TopWindow::SerializePlacement(Stream& s, bool reminimize)
if(s.IsLoading()) { if(s.IsLoading()) {
if(mn) rect = overlapped; if(mn) rect = overlapped;
Rect limit = GetVirtualWorkArea(); Rect limit = GetVirtualWorkArea();
Rect fm = GetFrameMargins(); Rect fm = frameMargins;
limit.left += fm.left; limit.left += fm.left;
limit.right -= fm.right; limit.right -= fm.right;
limit.top += fm.top; limit.top += fm.top;
@ -291,4 +295,4 @@ void TopWindow::SerializePlacement(Stream& s, bool reminimize)
} }
#endif #endif

View file

@ -1,13 +1,51 @@
ImageGdk gdk_icon; ImageGdk gdk_icon;
ImageGdk gdk_largeicon; ImageGdk gdk_largeicon;
bool topmost; 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<FrameTop<Ctrl>> custom_bar_frame;
One<FrameRight<Ctrl>> custom_bar_icons;
One<BarCtrl> 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 }; enum { FULLSCREEN = 99 };
void CenterRect(Ctrl *owner); void CenterRect(Ctrl *owner);
void SetMode(int mode); void SetMode(int mode);
void SyncTopMost(); 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); static gboolean StateEvent(GtkWidget *widget, GdkEventWindowState *event, gpointer user_data);
friend class Ctrl; friend class Ctrl;
friend bool InitGtkApp(int argc, char **argv, const char **envptr);

View file

@ -152,32 +152,107 @@ void Ctrl::UnregisterSystemHotKey(int id)
#endif #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<int> 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<const TopWindow *>(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 Rect Ctrl::GetWndScreenRect() const
{ {
GuiLock __; GuiLock __;
if(!IsOpen()) if(!IsOpen() || !top)
return Null; return Null;
WndRectsSync();
gint x, y; return utop->screen_rect;
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);
} }
void Ctrl::WndShow(bool b) void Ctrl::WndShow(bool b)
@ -431,27 +506,24 @@ void Ctrl::WndInvalidateRect(const Rect& r)
GuiLock __; GuiLock __;
Rect rr = r; 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()) 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 // as gtk3 dropped thread locking, we need to push invalid rectangles onto main loop
for(Win& win : wins) { for(Win& win : wins) {
if(win.ctrl == this) { if(win.ctrl == this) {
if(win.invalid.GetCount() && IsNull(win.invalid[0])) if(win.invalid.GetCount() && IsNull(win.invalid[0]))
return; return;
for(const Rect& ir : win.invalid)
if(ir.Contains(r)) // ignore repeated invalidates
return;
if(win.invalid.GetCount() > 40) { // keep things sane if(win.invalid.GetCount() > 40) { // keep things sane
win.invalid.Clear(); win.invalid.Clear();
win.invalid.Add(Null); win.invalid.Add(Null);
} }
else else {
win.invalid.Add(rr); win.invalid.Add(rr);
}
if(!invalids) { if(!invalids) {
invalids = true; invalids = true;
WakeUpGuiThread(); WakeUpGuiThread();
@ -476,10 +548,12 @@ bool Ctrl::SweepConfigure(bool wait)
GEvent& e = Events[i]; GEvent& e = Events[i];
Top *top = GetTop(); Top *top = GetTop();
if(e.type == GDK_CONFIGURE && this_ && top && top->id == e.windowid) { if(e.type == GDK_CONFIGURE && this_ && top && top->id == e.windowid) {
Rect rect = e.value; LLOG("SweepConfigure " << e.value);
LLOG("SweepConfigure " << rect); if(top) {
if(GetRect() != rect) utop->sync_rect = true;
SetWndRect(rect); LLOG("Sweep");
SetWndRect(GetWndScreenRect());
}
r = true; r = true;
e.type = EVENT_NONE; e.type = EVENT_NONE;
} }
@ -489,22 +563,38 @@ bool Ctrl::SweepConfigure(bool wait)
void Ctrl::WndSetPos(const Rect& rect) void Ctrl::WndSetPos(const Rect& rect)
{ {
LLOG("========================== WNDSETPOS");
LLOG("WndSetPos " << UPP::Name(this) << " " << rect); LLOG("WndSetPos " << UPP::Name(this) << " " << rect);
GuiLock __; GuiLock __;
if(!IsOpen()) if(!IsOpen())
return; return;
Ptr<Ctrl> this_ = this;
SweepConfigure(false); // Remove any previous GDK_CONFIGURE for this window
if(!this_ || !IsOpen())
return;
Rect m(0, 0, 0, 0); TopWindow *tw = dynamic_cast<TopWindow *>(this);
if(dynamic_cast<TopWindow *>(this)) if(tw)
m = GetFrameMargins();
SetWndRect(rect);
if(TopWindow *tw = dynamic_cast<TopWindow *>(this))
tw->SyncSizeHints(); 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); LLOG("-- WndSetPos0 " << rect);
} }
@ -557,8 +647,8 @@ TopFrameDraw::TopFrameDraw(Ctrl *ctrl, const Rect& r)
cairo_rectangle_int_t rr; cairo_rectangle_int_t rr;
rr.x = Ctrl::LSC(r.left); rr.x = Ctrl::LSC(r.left);
rr.y = Ctrl::LSC(r.top); rr.y = Ctrl::LSC(r.top);
rr.width = Ctrl::LSC(r.GetWidth()); rr.width = Ctrl::LSCH(r.GetWidth());
rr.height = Ctrl::LSC(r.GetHeight()); rr.height = Ctrl::LSCH(r.GetHeight());
cairo_region_t *rg = cairo_region_create_rectangle(&rr); cairo_region_t *rg = cairo_region_create_rectangle(&rr);
ctx = gdk_window_begin_draw_frame(top->gdk(), rg); ctx = gdk_window_begin_draw_frame(top->gdk(), rg);
cairo_region_destroy(rg); cairo_region_destroy(rg);
@ -606,4 +696,3 @@ Vector<WString> SplitCmdLine__(const char *cmd)
#endif #endif

View file

@ -184,9 +184,50 @@ void TopWindow::SyncCaption()
SetIco(); SetIco();
Ptr<TopWindow> ptr = this; Ptr<TopWindow> 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 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() void TopWindow::SetIco()
@ -203,7 +244,7 @@ void TopWindow::SetIco()
ico = new_ico; ico = new_ico;
lico = new_lico; lico = new_lico;
if(custom_titlebar) { if(custom_bar) {
Rect r = GetTitleBarRect(this); Rect r = GetTitleBarRect(this);
bool maximized = IsMaximized(); bool maximized = IsMaximized();

View file

@ -421,26 +421,29 @@ TopWindow& TopWindow::Icon(const Image& smallicon, const Image& _largeicon)
return *this; return *this;
} }
bool is_custom_titlebar_available__; // avoid the need to implement custom titlebar in all platforms:
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;
}
Function<bool (const TopWindow *)> is_custom_titlebar__;
Function<Ctrl *(TopWindow *, Color bk, int)> custom_titlebar_make__;
Event<const TopWindow *, TopWindow::CustomTitleBarMetrics&> custom_titlebar_metrics__ = Event<const TopWindow *, TopWindow::CustomTitleBarMetrics&> custom_titlebar_metrics__ =
[](const TopWindow *, TopWindow::CustomTitleBarMetrics& m) { [](const TopWindow *, TopWindow::CustomTitleBarMetrics& m) {
m.lm = m.rm = m.height = 0; 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 TopWindow::CustomTitleBarMetrics TopWindow::GetCustomTitleBarMetrics() const
{ {
CustomTitleBarMetrics m; CustomTitleBarMetrics m;
@ -448,17 +451,18 @@ TopWindow::CustomTitleBarMetrics TopWindow::GetCustomTitleBarMetrics() const
return m; return m;
} }
static bool sIsDragArea(Ctrl& w, Point p) bool Ctrl::MouseActiveCtrl(Ctrl *w, Point p)
{ {
for(Ctrl& q : w) for(Ctrl *q = w->GetLastChild(); q; q = q->GetPrev())
if(q.GetScreenRect().Contains(p)) if(q->GetScreenRect().Contains(p))
return q.IsIgnoreMouse() || sIsDragArea(q, p); return MouseActiveCtrl(q, p);
return false;
return w->IsMouseActive();
} }
bool TopWindow::IsCustomTitleBarDragArea(Point p) bool TopWindow::IsCustomTitleBarDragArea(Point p)
{ {
return sIsDragArea(*this, p + GetScreenRect().TopLeft()); return !MouseActiveCtrl(this, p + GetScreenRect().TopLeft());
} }
TopWindow& TopWindow::ToolWindow(bool b) TopWindow& TopWindow::ToolWindow(bool b)
@ -545,8 +549,6 @@ TopWindow::TopWindow()
dokeys = true; dokeys = true;
fullscreen = frameless = urgent = false; fullscreen = frameless = urgent = false;
close_rejects = false; close_rejects = false;
custom_titlebar = false;
custom_titlebar_cy = 0;
} }
TopWindow::~TopWindow() TopWindow::~TopWindow()

View file

@ -80,14 +80,9 @@ private:
bool frameless:1; bool frameless:1;
bool urgent:1; bool urgent:1;
bool close_rejects:1; bool close_rejects:1;
bool custom_titlebar:1;
byte state; byte state;
Image icon, largeicon; Image icon, largeicon;
int custom_titlebar_cy = 0;
int active_titlebar_button = -1;
bool active_titlebar_active = false;
const TopStyle *st; const TopStyle *st;
void GuiPlatformConstruct(); void GuiPlatformConstruct();
@ -181,14 +176,14 @@ public:
TopWindow& LargeIcon(const Image& m); TopWindow& LargeIcon(const Image& m);
TopWindow& Icon(const Image& smallicon, const Image& largeicon); 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; bool IsCustomTitleBar() const;
struct CustomTitleBarMetrics { struct CustomTitleBarMetrics {
int lm; int lm;
int rm; int rm;
int height; int height;
Color background;
}; };
CustomTitleBarMetrics GetCustomTitleBarMetrics() const; CustomTitleBarMetrics GetCustomTitleBarMetrics() const;

View file

@ -45,6 +45,9 @@ protected:
void Create(HWND parent, DWORD style, DWORD exstyle, bool savebits, int show, bool dropshadow); void Create(HWND parent, DWORD style, DWORD exstyle, bool savebits, int show, bool dropshadow);
Image DoMouse(int e, Point p, int zd = 0); 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 PaintWinBarBackground(SystemDraw& w, const Rect& clip);
void PaintWinBar(SystemDraw& w, const Rect& clip); void PaintWinBar(SystemDraw& w, const Rect& clip);
int GetActiveTitleBarButton(); int GetActiveTitleBarButton();

View file

@ -141,7 +141,7 @@ void Ctrl::PaintWinBarBackground(SystemDraw& w, const Rect& clip)
HWND hwnd = GetHWND(); HWND hwnd = GetHWND();
if(topwin && topwin->IsCustomTitleBar() && hwnd) { if(topwin && topwin->IsCustomTitleBar() && hwnd) {
Rect r = GetTitleBarRect(topwin); 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); int padding = GetSystemMetricsForDpi(92 /*SM_CXPADDEDBORDER*/, dpi);
Point p((LONG)lParam); Point p((LONG)lParam);
ScreenToClient(hwnd, p); 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()) if(!IsMaximized(hwnd) && p.y > 0 && p.y < frame_y + padding && topwin->IsSizeable())
return HTTOP; return HTTOP;
@ -896,16 +896,8 @@ LRESULT Ctrl::WindowProc(UINT message, WPARAM wParam, LPARAM lParam) {
MINMAXINFO *mmi = (MINMAXINFO *)lParam; MINMAXINFO *mmi = (MINMAXINFO *)lParam;
Rect frmrc = Size(200, 200); Rect frmrc = Size(200, 200);
::AdjustWindowRect(frmrc, WS_OVERLAPPEDWINDOW, FALSE); ::AdjustWindowRect(frmrc, WS_OVERLAPPEDWINDOW, FALSE);
// Size msz = Ctrl::GetWorkArea().Deflated(-frmrc.left, -frmrc.top, Rect minr = AdjustWindowRect(Rect(Point(50, 50), GetMinSize()));
// frmrc.right - 200, frmrc.bottom - 200).GetSize(); Rect maxr = AdjustWindowRect(Rect(Point(50, 50), GetMaxSize()));
// 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);
mmi->ptMinTrackSize = Point(minr.Size()); mmi->ptMinTrackSize = Point(minr.Size());
mmi->ptMaxTrackSize = Point(maxr.Size()); mmi->ptMaxTrackSize = Point(maxr.Size());
LLOG("WM_GETMINMAXINFO: MinTrackSize = " << Point(mmi->ptMinTrackSize) << ", MaxTrackSize = " << Point(mmi->ptMaxTrackSize)); LLOG("WM_GETMINMAXINFO: MinTrackSize = " << Point(mmi->ptMinTrackSize) << ", MaxTrackSize = " << Point(mmi->ptMaxTrackSize));

View file

@ -10,6 +10,17 @@ private:
void SetIco(); void SetIco();
void CenterRect(HWND owner, int center); void CenterRect(HWND owner, int center);
One<FrameTop<Ctrl>> custom_bar_frame;
One<Ctrl> 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: public:
void Open(HWND ownerhwnd); void Open(HWND ownerhwnd);
TopWindow& Style(dword _style); TopWindow& Style(dword _style);

View file

@ -220,8 +220,9 @@ void Ctrl::InstallPanicBox()
InstallPanicMessageBox(&Win32PanicMessageBox); InstallPanicMessageBox(&Win32PanicMessageBox);
} }
extern bool is_custom_titlebar_available__;
extern Event<const TopWindow *, TopWindow::CustomTitleBarMetrics&> custom_titlebar_metrics__; extern Event<const TopWindow *, TopWindow::CustomTitleBarMetrics&> custom_titlebar_metrics__;
extern Function<bool (const TopWindow *)> is_custom_titlebar__;
extern Function<Ctrl *(TopWindow *, Color, int)> custom_titlebar_make__;
void Ctrl::InitWin32(HINSTANCE hInstance) void Ctrl::InitWin32(HINSTANCE hInstance)
{ {
@ -284,10 +285,8 @@ void Ctrl::InitWin32(HINSTANCE hInstance)
GlobalBackPaint(); GlobalBackPaint();
is_custom_titlebar_available__ = IsWin11(); custom_titlebar_metrics__ = [](const TopWindow *tw, TopWindow::CustomTitleBarMetrics& m) {
if(!tw->custom_bar)
custom_titlebar_metrics__ = [=](const TopWindow *tw, TopWindow::CustomTitleBarMetrics& m) {
if(!tw->custom_titlebar)
return; return;
m.height = GetWin32TitleBarHeight(tw); m.height = GetWin32TitleBarHeight(tw);
m.lm = 0; m.lm = 0;
@ -296,7 +295,15 @@ void Ctrl::InitWin32(HINSTANCE hInstance)
m.lm = DPI(4) + min(icon.GetWidth(), 32); m.lm = DPI(4) + min(icon.GetWidth(), 32);
m.rm = (tw->IsZoomable() ? 3 : 1) * GetWin32TitleBarButtonWidth(); 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(); 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<TopWindow *>(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) void Ctrl::Create(HWND parent, DWORD style, DWORD exstyle, bool savebits, int show, bool dropshadow)
{ {
GuiLock __; GuiLock __;
ASSERT_(IsMainThread(), "Window creation can only happen in the main thread"); ASSERT_(IsMainThread(), "Window creation can only happen in the main thread");
LLOG("Ctrl::Create(parent = " << (void *)parent << ") in " <<UPP::Name(this) << LOG_BEGIN); LLOG("Ctrl::Create(parent = " << (void *)parent << ") in " <<UPP::Name(this) << LOG_BEGIN);
ASSERT(!IsChild() && !IsOpen()); ASSERT(!IsChild() && !IsOpen());
Rect r = GetRect(); Rect r = AdjustWindowRect(GetRect(), style, exstyle);
AdjustWindowRectEx(r, style, FALSE, exstyle);
isopen = true; isopen = true;
Top *top = new Top; Top *top = new Top;
SetTop(top); SetTop(top);
@ -1093,9 +1126,7 @@ void Ctrl::WndSetPos(const Rect& rect)
LLOG("WndSetPos " << UPP::Name(this) << " " << rect); LLOG("WndSetPos " << UPP::Name(this) << " " << rect);
HWND hwnd = GetHWND(); HWND hwnd = GetHWND();
if(hwnd) { if(hwnd) {
Rect r = rect; Rect r = AdjustWindowRect(rect);
AdjustWindowRectEx(r, ::GetWindowLong(hwnd, GWL_STYLE), FALSE,
::GetWindowLong(hwnd, GWL_EXSTYLE));
SetWindowPos(hwnd, NULL, r.left, r.top, r.Width(), r.Height(), SetWindowPos(hwnd, NULL, r.left, r.top, r.Width(), r.Height(),
SWP_NOACTIVATE|SWP_NOZORDER); SWP_NOACTIVATE|SWP_NOZORDER);
if(HasFocusDeep()) { if(HasFocusDeep()) {

View file

@ -54,7 +54,7 @@ void Ctrl::DoPaint(const Vector<Rect>& invalid)
{ {
GuiLock __; GuiLock __;
Window xwin = GetWindow(); Window xwin = GetWindow();
if(utop && IsVisible()) { if(top && IsVisible()) {
LTIMING("DoPaint"); LTIMING("DoPaint");
fullrefresh = false; fullrefresh = false;
// if(GLX) return; // if(GLX) return;
@ -647,7 +647,7 @@ void Ctrl::PopUp(Ctrl *owner, bool savebits, bool activate, bool, bool)
WndShow(visible); WndShow(visible);
if(activate && IsEnabled()) if(activate && IsEnabled())
SetFocus(); SetFocus();
if(utop) utop->owner = owner; if(top) utop->owner = owner;
StateH(OPEN); StateH(OPEN);
} }

View file

@ -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.&] would use to draw the background of title bar area.&]
[s3; &] [s3; &]
[s4; &] [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 [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 cement]([_^Stream^ Stream][@(0.0.255) `&]_[*@3 s], [@(0.0.255) bool]_[*@3 reminimize]_`=_[@(0.0.255) f
alse])&] alse])&]

View file

@ -512,7 +512,8 @@ public:
virtual ~ToolButton(); 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 { class ToolBar : public BarCtrl {
public: public:

View file

@ -2,16 +2,17 @@
namespace Upp { namespace Upp {
Rect DisplayPopup::screen_rect; Rect DisplayPopup::screen_rect;
Ptr<Ctrl> DisplayPopup::ctrl; Ptr<Ctrl> DisplayPopup::ctrl;
Rect DisplayPopup::item; Ptr<DisplayPopup> DisplayPopup::owner;
Value DisplayPopup::value; Rect DisplayPopup::item;
Color DisplayPopup::paper; Value DisplayPopup::value;
Color DisplayPopup::ink; Color DisplayPopup::paper;
dword DisplayPopup::style; Color DisplayPopup::ink;
const Display *DisplayPopup::display; dword DisplayPopup::style;
int DisplayPopup::margin; const Display *DisplayPopup::display;
bool DisplayPopup::usedisplaystdsize_s; int DisplayPopup::margin;
bool DisplayPopup::usedisplaystdsize_s;
DisplayPopup::DisplayPopup() 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; r.top += (r.Height() - display->GetStdSize(value).cy) / 2;
display->Paint(w, r, value, ink, paper, style); 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(); Sync();
return false; return false;
} }
void DisplayPopup::Set(Ctrl *_ctrl, const Rect& _item, void DisplayPopup::Set(Ctrl *_ctrl, const Rect& _item,
const Value& _value, const Display *_display, const Value& _value, const Display *_display,
Color _ink, Color _paper, dword _style, int _margin) Color _ink, Color _paper, dword _style, int _margin)
{ {
if(!GUI_ToolTips()) if(!GUI_ToolTips())
return; return;
@ -120,6 +122,7 @@ void DisplayPopup::Set(Ctrl *_ctrl, const Rect& _item,
RefreshRect(); RefreshRect();
item = _item; item = _item;
ctrl = _ctrl; ctrl = _ctrl;
owner = this;
value = _value; value = _value;
display = _display; display = _display;
ink = _ink; ink = _ink;
@ -133,8 +136,10 @@ void DisplayPopup::Set(Ctrl *_ctrl, const Rect& _item,
void DisplayPopup::Cancel() void DisplayPopup::Cancel()
{ {
screen_rect = Null; if(owner == this) {
Sync(); screen_rect = Null;
Sync();
}
} }
bool DisplayPopup::IsOpen() bool DisplayPopup::IsOpen()

View file

@ -1,15 +1,16 @@
class DisplayPopup : public Pte<DisplayPopup> { class DisplayPopup : public Pte<DisplayPopup> {
bool usedisplaystdsize = false; bool usedisplaystdsize = false;
static Rect screen_rect; static Rect screen_rect;
static Ptr<Ctrl> ctrl; static Ptr<Ctrl> ctrl;
static Rect item; static Ptr<DisplayPopup> owner;
static Value value; static Rect item;
static Color paper, ink; static Value value;
static dword style; static Color paper, ink;
static const Display *display; static dword style;
static int margin; static const Display *display;
static bool usedisplaystdsize_s; static int margin;
static bool usedisplaystdsize_s;
static bool StateHook(Ctrl *, int reason); static bool StateHook(Ctrl *, int reason);
static bool MouseHook(Ctrl *, bool, int, Point, int, dword); static bool MouseHook(Ctrl *, bool, int, Point, int, dword);

View file

@ -280,14 +280,11 @@ void PopUpList::PopUp(Ctrl *owner, int x, int top, int bottom, int width) {
rt.top = top - h; rt.top = top - h;
rt.bottom = rt.top + h; rt.bottom = rt.top + h;
} }
if(up) { if(up)
popup->SetRect(Rect(rt.left, rt.bottom - 1, rt.right, rt.bottom)); 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->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()) { if(GUI_PopUpEffect()) {
popup->ac.CenterCursor(); popup->ac.CenterCursor();
popup->PopUp(owner, true, true, GUI_DropShadows()); popup->PopUp(owner, true, true, GUI_DropShadows());

View file

@ -2,7 +2,7 @@
namespace Upp { namespace Upp {
#define LLOG(x) // LOG(x) #define LLOG(x) LOG(x)
void Sb(Button::Style& bs, const Image& img) void Sb(Button::Style& bs, const Image& img)
{ {

View file

@ -132,4 +132,12 @@ void StaticBarArea::Paint(Draw& w)
upperframe ? Null : GetScreenRect().bottom); 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);
}
} }

View file

@ -389,7 +389,6 @@ public:
virtual void Activate(); virtual void Activate();
virtual void Layout(); virtual void Layout();
virtual void Skin(); virtual void Skin();
virtual bool IsCustomTitleBarDragArea(Point p);
virtual bool IsVerbose() const; virtual bool IsVerbose() const;
virtual void PutConsole(const char *s); virtual void PutConsole(const char *s);

View file

@ -15,13 +15,15 @@ void Ide::Skin()
SyncUsc(); SyncUsc();
} }
void Ide::ToggleVerboseBuild() { void Ide::ToggleVerboseBuild()
{
console.verbosebuild = !console.verbosebuild; console.verbosebuild = !console.verbosebuild;
SetToolBar(); SetToolBar();
} }
void Ide::ToggleStopOnErrors() { void Ide::ToggleStopOnErrors()
{
stoponerrors = !stoponerrors; stoponerrors = !stoponerrors;
} }
@ -306,18 +308,25 @@ void Ide::SetupBars()
menubar.Transparent(); menubar.Transparent();
display.IgnoreMouse(); display.IgnoreMouse();
bararea.Add(barrect.SizePos()); 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) { if(toolbar_in_row) {
toolbar.SetFrame(NullFrame()); toolbar.SetFrame(NullFrame());
int tcy = max(mainconfiglist.GetStdSize().cy + DPI(2), toolbar.GetStdHeight()); 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(toolbar.HSizePos(l, r).VCenterPos(tcy));
barrect.Add(display.RightPos(4, r).VSizePos(2, 3)); barrect.Add(display.RightPos(4, r).VSizePos(2, 3));
bararea.Height(max(menubar.GetStdHeight(), tcy)); bararea.Height(max(menubar.GetStdHeight(), tcy));
toolbar.Transparent(); toolbar.Transparent();
} }
else { else {
barrect.Add(menubar.LeftPos(0, l).VCenterPos(menubar.GetStdHeight())); barrect.Add(menubar);
barrect.Add(display.RightPos(4, r).VSizePos(2, 3)); barrect.Add(display.RightPos(4, r).VSizePos(2, 3));
bararea.Height(menubar.GetStdHeight()); bararea.Height(menubar.GetStdHeight());
AddFrame(TopSeparatorFrame()); AddFrame(TopSeparatorFrame());
@ -345,14 +354,13 @@ void Ide::Layout()
int tcy = max(mainconfiglist.GetStdSize().cy + DPI(2), toolbar.GetStdHeight()); int tcy = max(mainconfiglist.GetStdSize().cy + DPI(2), toolbar.GetStdHeight());
auto cm = GetCustomTitleBarMetrics(); auto cm = GetCustomTitleBarMetrics();
barrect.HSizePos(cm.lm, cm.rm);
int x = 0; int x = 0;
int mh = menubar.GetStdHeight(); int mh = menubar.GetStdHeight();
menubar.LeftPos(0, mw).TopPos((cm.height - mh) / 2, mh); menubar.LeftPos(0, mw).TopPos((cm.height - mh) / 2, mh);
x += mw; x += mw;
if(toolbar_in_row) { if(toolbar_in_row) {
int tw = toolbar.GetWidth(); int tw = toolbar.GetWidth();
int bah = 0; int bah = 0;
@ -382,12 +390,6 @@ void Ide::Layout()
display.Show(!designer && (menubar.GetSize().cx + display.GetSize().cx < GetSize().cx)); 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() void Ide::DoDisplay()
{ {
if(replace_in_files) if(replace_in_files)
@ -400,7 +402,11 @@ void Ide::DoDisplay()
s << "[g ["; s << "[g [";
if(editfile_isreadonly) if(editfile_isreadonly)
s << "@B"; 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) { if(!designer) {
s << ": [* " << p.y + 1 << "]:" << p.x + 1; s << ": [* " << p.y + 1 << "]:" << p.x + 1;
int64 l, h; int64 l, h;

View file

@ -408,9 +408,6 @@ void AppMain___()
Ctrl::SetAlwaysUseBundledIcon(); Ctrl::SetAlwaysUseBundledIcon();
#endif #endif
if(!ide.disable_custom_caption)
ide.CustomTitleBar();
if(arg.GetCount() == 1) { if(arg.GetCount() == 1) {
if(arg[0].EndsWith(".upp")) { if(arg[0].EndsWith(".upp")) {
Vector<String> names = Split(arg[0], DIR_SEP); Vector<String> names = Split(arg[0], DIR_SEP);

View file

@ -0,0 +1,12 @@
uses
CtrlLib;
file
main.cpp;
mainconfig
"" = "GUI",
"" = "GUI FORCE_CSD",
"" = "GUI FORCE_CSD NOCUSTOMBAR",
"" = "GUI WAYLAND";

View file

@ -0,0 +1,112 @@
#include <CtrlLib/CtrlLib.h>
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<StaticRect> 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<EditString> 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();
}

View file

@ -1,55 +0,0 @@
#include <CtrlLib/CtrlLib.h>
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();
}

View file

@ -0,0 +1,53 @@
#include <CtrlLib/CtrlLib.h>
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();
}

12
upptst/GUI/GUI.upp Normal file
View file

@ -0,0 +1,12 @@
uses
CtrlLib;
file
main.cpp;
mainconfig
"" = "GUI",
"" = "GUI FORCE_CSD",
"" = "GUI FORCE_CSD NOCUSTOMBAR",
"" = "GUI WAYLAND";

100
upptst/GUI/main.cpp Normal file
View file

@ -0,0 +1,100 @@
#include <CtrlLib/CtrlLib.h>
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<StaticRect> 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<EditString> 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();
}

View file

@ -0,0 +1,10 @@
uses
CtrlLib;
file
main.cpp;
mainconfig
"" = "GUI",
"" = "GUI FORCE_CSD";

View file

@ -0,0 +1,92 @@
#include <CtrlLib/CtrlLib.h>
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();
}