mirror of
https://git.vladimir.cc/vladimir/ewsdr.git
synced 2026-08-25 18:43:51 +00:00
feat(ui): add hover overlay scroll for left panel
This commit is contained in:
+143
-15
@@ -24,7 +24,8 @@ interface
|
||||
uses
|
||||
Classes, SysUtils, StrUtils, FreqDisplay, DeviceForm, FilterPopup,
|
||||
VfoOverlay, SampleRateOverlay, BandPlanOverlay,
|
||||
FlatButton, FlatSlider, FlatDropDown, AppTheme, Forms, Controls, Graphics, Dialogs,
|
||||
FlatButton, FlatSlider, FlatDropDown, OverlayScrollBar, AppTheme,
|
||||
Forms, Controls, Graphics, Dialogs,
|
||||
StdCtrls, ExtCtrls, Buttons, Menus, Math, Types, SyncObjs,
|
||||
OpenGLContextEx,
|
||||
LCLIntf, LCLType, GraphType,
|
||||
@@ -104,6 +105,7 @@ const
|
||||
SMETER_MARGIN = 3;
|
||||
TOP_SMETER_GAP = 8;
|
||||
TOP_VFO_FONT = 25;
|
||||
LEFT_SCROLL_GUTTER_W = 8;
|
||||
LEFT_PANEL_BTN_PAD = 3;
|
||||
LEFT_PANEL_BTN_GAP = 2;
|
||||
|
||||
@@ -263,6 +265,8 @@ type
|
||||
|
||||
// ---- Left panel ----
|
||||
PanelLeft: TPanel;
|
||||
FLeftScrollBar: TOverlayScrollBar;
|
||||
FLeftScrollOffset: Integer;
|
||||
|
||||
PanelVfoA: TPanel;
|
||||
LblVfoALabel: TLabel;
|
||||
@@ -627,6 +631,9 @@ type
|
||||
procedure ApplyXvtrToNetwork;
|
||||
procedure RebuildXvtrButtons;
|
||||
procedure RelayoutBelowBands;
|
||||
procedure UpdateLeftPanelScroll;
|
||||
function CursorOverLeftPanel: Boolean;
|
||||
procedure LeftScrollChanged(Sender: TObject);
|
||||
procedure BtnXvtrBandClick(Sender: TObject);
|
||||
procedure ActivateXvtrBand(Idx: Integer);
|
||||
procedure DeactivateXvtr;
|
||||
@@ -1939,9 +1946,19 @@ begin
|
||||
PanelLeft := TPanel.Create(Self);
|
||||
PanelLeft.Parent := Self;
|
||||
PanelLeft.Align := alLeft;
|
||||
PanelLeft.Width := LEFT_W;
|
||||
// Узкий правый gutter зарезервирован постоянно: появление overlay-scrollbar
|
||||
// не меняет ширину панели и не дёргает область спектра.
|
||||
PanelLeft.Width := LEFT_W + LEFT_SCROLL_GUTTER_W;
|
||||
PanelLeft.BevelOuter := bvNone;
|
||||
|
||||
FLeftScrollOffset := 0;
|
||||
FLeftScrollBar := TOverlayScrollBar.Create(Self);
|
||||
FLeftScrollBar.Parent := PanelLeft;
|
||||
FLeftScrollBar.Align := alRight;
|
||||
FLeftScrollBar.Width := LEFT_SCROLL_GUTTER_W;
|
||||
FLeftScrollBar.HoverTarget := PanelLeft;
|
||||
FLeftScrollBar.OnPositionChange := LeftScrollChanged;
|
||||
|
||||
Y := 0;
|
||||
|
||||
// Compact top VFO group
|
||||
@@ -2134,7 +2151,8 @@ begin
|
||||
BtnTXProfile.OnMouseDown := BtnTXProfileMouseDown;
|
||||
StyleButton(BtnTXProfile, False);
|
||||
|
||||
FTXProfileDropDown := TFlatDropDown.Create(BtnTXProfile, PanelLeft, 1, BTN_SM);
|
||||
FTXProfileDropDown := TFlatDropDown.Create(BtnTXProfile, PanelLeft, 1, BTN_SM,
|
||||
LEFT_SCROLL_GUTTER_W);
|
||||
FTXProfileDropDown.OnSelect := OnTXProfileDropDownSelect;
|
||||
RenderTXProfile;
|
||||
|
||||
@@ -2179,7 +2197,8 @@ begin
|
||||
BtnChannels.OnMouseDown := BtnChannelsMouseDown;
|
||||
StyleButton(BtnChannels, False);
|
||||
|
||||
FChannelsDropDown := TFlatDropDown.Create(BtnChannels, PanelLeft, 1, BTN_SM);
|
||||
FChannelsDropDown := TFlatDropDown.Create(BtnChannels, PanelLeft, 1, BTN_SM,
|
||||
LEFT_SCROLL_GUTTER_W);
|
||||
FChannelsDropDown.OnSelect := OnChannelsDropDownSelect;
|
||||
RefreshChannelsDropDown;
|
||||
|
||||
@@ -2485,7 +2504,8 @@ begin
|
||||
BTN_H, BtnFMCTCSSToneClick);
|
||||
StyleButton(BtnFMCTCSSTone, False);
|
||||
|
||||
FCTCSSDropDown := TFlatDropDown.Create(BtnFMCTCSSTone, PanelLeft, 4, BTN_SM);
|
||||
FCTCSSDropDown := TFlatDropDown.Create(BtnFMCTCSSTone, PanelLeft, 4, BTN_SM,
|
||||
LEFT_SCROLL_GUTTER_W);
|
||||
FCTCSSDropDown.SetItems(CTCSS_NAMES);
|
||||
FCTCSSDropDown.SetItemIndex(FController.FFMCTCSSToneIdx);
|
||||
FCTCSSDropDown.OnSelect := OnCTCSSDropDownSelect;
|
||||
@@ -2509,7 +2529,8 @@ begin
|
||||
BTN_H, BtnFMStepSelClick);
|
||||
StyleButton(BtnFMStepSel, False);
|
||||
|
||||
FStepDropDown := TFlatDropDown.Create(BtnFMStepSel, PanelLeft, FM_STEP_COUNT, BTN_SM);
|
||||
FStepDropDown := TFlatDropDown.Create(BtnFMStepSel, PanelLeft, FM_STEP_COUNT,
|
||||
BTN_SM, LEFT_SCROLL_GUTTER_W);
|
||||
FStepDropDown.SetItems(FM_STEP_NAMES);
|
||||
FStepDropDown.SetItemIndex(FController.FFMStepIdx);
|
||||
FStepDropDown.OnSelect := OnStepDropDownSelect;
|
||||
@@ -2634,6 +2655,7 @@ end;
|
||||
|
||||
procedure TMainForm.RightPanelResize(Sender: TObject);
|
||||
begin
|
||||
UpdateLeftPanelScroll;
|
||||
ResizeSpectrumPanels;
|
||||
end;
|
||||
|
||||
@@ -3149,6 +3171,9 @@ begin
|
||||
|
||||
Color := T.BG; Font.Color := T.Text;
|
||||
DP(PanelToolbar); DP(PanelLeft);
|
||||
if FLeftScrollBar <> nil then
|
||||
FLeftScrollBar.SetColors(T.Panel, T.Border,
|
||||
T.SliderThumbHot, T.SliderThumbDrag);
|
||||
DP(PanelTopVfoGroup); DP(PanelTopVfoA); DP(PanelTopVfoB);
|
||||
DP(PanelVfoA); DP(PanelVfoB); DP(PanelVfoButtons);
|
||||
DP(PanelBands); DP(PanelMode); DP(PanelFilter);
|
||||
@@ -8092,36 +8117,123 @@ end;
|
||||
// создаёт новые для enabled слотов в 3-м и 4-м ряду PanelBands.
|
||||
procedure TMainForm.RelayoutBelowBands;
|
||||
var
|
||||
Y: Integer;
|
||||
Y, Gap: Integer;
|
||||
begin
|
||||
if PanelBands = nil then Exit;
|
||||
Y := PanelBands.Top + PanelBands.Height + 2;
|
||||
PanelMode.Top := Y; Y := Y + PanelMode.Height + 2;
|
||||
Gap := Scale96ToForm(2);
|
||||
Y := PanelBands.Top + PanelBands.Height + Gap;
|
||||
PanelMode.Top := Y; Y := Y + PanelMode.Height + Gap;
|
||||
if PanelFilter.Visible then
|
||||
begin
|
||||
PanelFilter.Top := Y;
|
||||
Y := Y + PanelFilter.Height + 2;
|
||||
Y := Y + PanelFilter.Height + Gap;
|
||||
end;
|
||||
if (PanelRawDev <> nil) and PanelRawDev.Visible then
|
||||
begin
|
||||
PanelRawDev.Top := Y; Y := Y + PanelRawDev.Height + 2;
|
||||
PanelRawDev.Top := Y; Y := Y + PanelRawDev.Height + Gap;
|
||||
end;
|
||||
if (PanelFMStep <> nil) and PanelFMStep.Visible then
|
||||
begin
|
||||
PanelFMStep.Top := Y; Y := Y + PanelFMStep.Height + 2;
|
||||
PanelFMStep.Top := Y; Y := Y + PanelFMStep.Height + Gap;
|
||||
end;
|
||||
if (PanelFMSQ <> nil) and PanelFMSQ.Visible then
|
||||
begin
|
||||
PanelFMSQ.Top := Y; Y := Y + PanelFMSQ.Height + 2;
|
||||
PanelFMSQ.Top := Y; Y := Y + PanelFMSQ.Height + Gap;
|
||||
end;
|
||||
if (PanelFMCTCSS <> nil) and PanelFMCTCSS.Visible then
|
||||
begin
|
||||
PanelFMCTCSS.Top := Y; Y := Y + PanelFMCTCSS.Height + 2;
|
||||
PanelFMCTCSS.Top := Y; Y := Y + PanelFMCTCSS.Height + Gap;
|
||||
end;
|
||||
if (PanelFMRpt <> nil) and PanelFMRpt.Visible then
|
||||
begin
|
||||
PanelFMRpt.Top := Y; Y := Y + PanelFMRpt.Height + 2;
|
||||
PanelFMRpt.Top := Y; Y := Y + PanelFMRpt.Height + Gap;
|
||||
end;
|
||||
UpdateLeftPanelScroll;
|
||||
end;
|
||||
|
||||
function TMainForm.CursorOverLeftPanel: Boolean;
|
||||
var
|
||||
P: TPoint;
|
||||
begin
|
||||
Result := False;
|
||||
if (PanelLeft = nil) or (not PanelLeft.Visible) then Exit;
|
||||
P := PanelLeft.ScreenToClient(Mouse.CursorPos);
|
||||
Result := (P.X >= 0) and (P.Y >= 0) and
|
||||
(P.X < PanelLeft.ClientWidth) and (P.Y < PanelLeft.ClientHeight);
|
||||
end;
|
||||
|
||||
procedure TMainForm.LeftScrollChanged(Sender: TObject);
|
||||
var
|
||||
NewValue, Delta: Integer;
|
||||
|
||||
procedure MovePanel(P: TPanel);
|
||||
begin
|
||||
if P <> nil then P.Top := P.Top + Delta;
|
||||
end;
|
||||
|
||||
begin
|
||||
if FLeftScrollBar = nil then Exit;
|
||||
NewValue := FLeftScrollBar.Position;
|
||||
if NewValue = FLeftScrollOffset then Exit;
|
||||
// Top хранится уже с учётом текущего scroll offset. При движении вниз
|
||||
// содержимое уходит вверх, поэтому знак у дельты обратный.
|
||||
Delta := FLeftScrollOffset - NewValue;
|
||||
FLeftScrollOffset := NewValue;
|
||||
|
||||
MovePanel(PanelTX);
|
||||
MovePanel(PanelRXBlock);
|
||||
MovePanel(PanelAGC);
|
||||
MovePanel(PanelPlutoAGC);
|
||||
MovePanel(PanelBands);
|
||||
MovePanel(PanelMode);
|
||||
MovePanel(PanelFilter);
|
||||
MovePanel(PanelRawDev);
|
||||
MovePanel(PanelFMStep);
|
||||
MovePanel(PanelFMSQ);
|
||||
MovePanel(PanelFMCTCSS);
|
||||
MovePanel(PanelFMRpt);
|
||||
|
||||
if FTXProfileDropDown <> nil then FTXProfileDropDown.ClosePopup;
|
||||
if FChannelsDropDown <> nil then FChannelsDropDown.ClosePopup;
|
||||
if FCTCSSDropDown <> nil then FCTCSSDropDown.ClosePopup;
|
||||
if FStepDropDown <> nil then FStepDropDown.ClosePopup;
|
||||
end;
|
||||
|
||||
procedure TMainForm.UpdateLeftPanelScroll;
|
||||
var
|
||||
ContentBottom, ViewH, NewMax: Integer;
|
||||
|
||||
procedure MeasurePanel(P: TPanel);
|
||||
var
|
||||
B: Integer;
|
||||
begin
|
||||
if (P = nil) or (not P.Visible) then Exit;
|
||||
// Возвращаем логическую позицию до сдвига viewport.
|
||||
B := P.Top + FLeftScrollOffset + P.Height;
|
||||
if B > ContentBottom then ContentBottom := B;
|
||||
end;
|
||||
|
||||
begin
|
||||
if (PanelLeft = nil) or (FLeftScrollBar = nil) then Exit;
|
||||
ViewH := PanelLeft.ClientHeight;
|
||||
if ViewH <= 0 then Exit;
|
||||
|
||||
ContentBottom := 0;
|
||||
MeasurePanel(PanelTX);
|
||||
MeasurePanel(PanelRXBlock);
|
||||
MeasurePanel(PanelAGC);
|
||||
MeasurePanel(PanelPlutoAGC);
|
||||
MeasurePanel(PanelBands);
|
||||
MeasurePanel(PanelMode);
|
||||
MeasurePanel(PanelFilter);
|
||||
MeasurePanel(PanelRawDev);
|
||||
MeasurePanel(PanelFMStep);
|
||||
MeasurePanel(PanelFMSQ);
|
||||
MeasurePanel(PanelFMCTCSS);
|
||||
MeasurePanel(PanelFMRpt);
|
||||
|
||||
NewMax := Max(0, ContentBottom + Scale96ToForm(4) - ViewH);
|
||||
FLeftScrollBar.SetRange(NewMax, ViewH);
|
||||
end;
|
||||
|
||||
procedure TMainForm.RebuildXvtrButtons;
|
||||
@@ -8362,6 +8474,22 @@ begin
|
||||
// виджет крутил бы B, а этот хендлер — активный A.
|
||||
if OverFreqDisp(FreqDispA) or OverFreqDisp(FreqDispB) then Exit;
|
||||
|
||||
// Если левая колонка не помещается, колесо над любой её частью прокручивает
|
||||
// колонку, а не частоту. Scrollbar остаётся overlay и появляется по hover.
|
||||
if CursorOverLeftPanel then
|
||||
begin
|
||||
UpdateLeftPanelScroll;
|
||||
if (FLeftScrollBar <> nil) and (FLeftScrollBar.Maximum > 0) then
|
||||
begin
|
||||
if WheelDelta > 0 then
|
||||
FLeftScrollBar.ScrollBy(-Scale96ToForm(64))
|
||||
else if WheelDelta < 0 then
|
||||
FLeftScrollBar.ScrollBy(Scale96ToForm(64));
|
||||
Handled := True;
|
||||
Exit;
|
||||
end;
|
||||
end;
|
||||
|
||||
// Колесо над раскрытым списком устройств во флаге — прокрутка этого списка.
|
||||
for i := 0 to MAX_PANS - 1 do
|
||||
if (FPans[i] <> nil) and Assigned(FPans[i].PbSpectrum) and
|
||||
|
||||
Reference in New Issue
Block a user