diff options
author | graemeg <graemeg@ae50a9b5-8222-0410-bf8d-8a13f76226bf> | 2007-08-11 11:09:59 +0000 |
---|---|---|
committer | graemeg <graemeg@ae50a9b5-8222-0410-bf8d-8a13f76226bf> | 2007-08-11 11:09:59 +0000 |
commit | 71eb8ce02629a33957b7f6c0733c1406b46678a0 (patch) | |
tree | fe5b5e70cb43378d5b3ddfa85afafa8a2fb61053 | |
parent | 2ce2eccae88e86a3e29d527a4b42b9da2e4b4167 (diff) | |
download | fpGUI-71eb8ce02629a33957b7f6c0733c1406b46678a0.tar.xz |
* Grid changes
- Implemented keyboard navigation. Cell focus.
- Improved header painting and little square between scrollbars.
- Improved scrollbar behavior and slider sizing.
- Implemented mouse wheel support. Scrolling can also happen up/down
or left/right based on the visibility of the vertical scrollbar.
- Implemented cell selection via mouse
- Implemented column resize support.
- Extended the GridTest project to demo features
-rw-r--r-- | examples/gui/gridtest/gridtest.lpr | 24 | ||||
-rw-r--r-- | src/gui/gui_grid.pas | 256 |
2 files changed, 268 insertions, 12 deletions
diff --git a/examples/gui/gridtest/gridtest.lpr b/examples/gui/gridtest/gridtest.lpr index 40c91bec..46ae250b 100644 --- a/examples/gui/gridtest/gridtest.lpr +++ b/examples/gui/gridtest/gridtest.lpr @@ -22,6 +22,10 @@ type grdMain: TfpgBaseGrid; chkShowHeader: TfpgCheckBox; chkShowGrid: TfpgCheckBox; + chkRowSelect: TfpgCheckBox; + chkDisabled: TfpgCheckBox; + procedure chkDisabledChange(Sender: TObject); + procedure chkRowSelectChange(Sender: TObject); procedure chkShowHeaderChange(Sender: TObject); procedure chkShowGridChange(Sender: TObject); procedure btnQuitClick(Sender: TObject); @@ -31,6 +35,16 @@ type { TMainForm } +procedure TMainForm.chkDisabledChange(Sender: TObject); +begin + grdMain.Enabled := not chkDisabled.Checked; +end; + +procedure TMainForm.chkRowSelectChange(Sender: TObject); +begin + grdMain.RowSelect := chkRowSelect.Checked; +end; + procedure TMainForm.chkShowHeaderChange(Sender: TObject); begin grdMain.ShowHeader := chkShowHeader.Checked; @@ -73,6 +87,16 @@ begin chkShowGrid.Checked := True; chkShowGrid.OnChange := @chkShowGridChange; chkShowGrid.Anchors := [anLeft, anBottom]; + + chkRowSelect := CreateCheckBox(self, chkShowGrid.Right+10, 320, 'Row Select'); + chkRowSelect.Checked := False; + chkRowSelect.OnChange := @chkRowSelectChange; + chkRowSelect.Anchors := [anLeft, anBottom]; + + chkDisabled := CreateCheckBox(self, chkRowSelect.Right+10, 320, 'Disabled'); + chkDisabled.Checked := False; + chkDisabled.OnChange := @chkDisabledChange; + chkDisabled.Anchors := [anLeft, anBottom]; end; diff --git a/src/gui/gui_grid.pas b/src/gui/gui_grid.pas index 6b34c7cb..a0023d9c 100644 --- a/src/gui/gui_grid.pas +++ b/src/gui/gui_grid.pas @@ -4,11 +4,13 @@ unit gui_grid; { TODO: - * Keyboard navigation * Decendant with TColumn class - * Painting of bottom right little rectangle between scrollbars + * Selecting the last fully visible row, scrolls the grid. Selection + is corruct, but because of the scroll it is confusing. } +{.$Define DEBUG} + interface uses @@ -25,10 +27,14 @@ type TfpgRowChangeNotify = procedure(Sender: TObject; ARow: integer) of object; + // Column 2 is special just for testing purposes. Descendant classes will + // override that special behavior anyway. TfpgBaseGrid = class(TfpgWidget) private FBackgroundColor: TfpgColor; FColResizing: boolean; + FDragPos: integer; // used for column resizing + FResizedCol: integer; // used for column resizing FDefaultColWidth: integer; FDefaultRowHeight: integer; FFocusCol: integer; @@ -50,6 +56,7 @@ type FVScrollBar: TfpgScrollBar; FHScrollBar: TfpgScrollBar; procedure HScrollBarMove(Sender: TObject; position: integer); + procedure SetRowSelect(const AValue: boolean); procedure VScrollBarMove(Sender: TObject; position: integer); procedure SetBackgroundColor(const AValue: TfpgColor); procedure SetDefaultColWidth(const AValue: integer); @@ -72,7 +79,12 @@ type procedure DrawGrid(ARow, ACol: integer; ARect: TfpgRect; AFlags: integer); virtual; procedure HandlePaint; override; procedure HandleShow; override; + procedure HandleResize(awidth, aheight: TfpgCoord); override; procedure HandleKeyPress(var keycode: word; var shiftstate: TShiftState; var consumed: boolean); override; + procedure HandleMouseScroll(x, y: integer; shiftstate: TShiftState; delta: smallint); override; + procedure HandleMouseMove(x, y: integer; btnstate: word; shiftstate: TShiftState); override; + procedure HandleLMouseUp(x, y: integer; shiftstate: TShiftState); override; + procedure HandleLMouseDown(x, y: integer; shiftstate: TShiftState); override; procedure FollowFocus; virtual; property DefaultColWidth: integer read FDefaultColWidth write SetDefaultColWidth default 64; property DefaultRowHeight: integer read FDefaultRowHeight write SetDefaultRowHeight; @@ -84,7 +96,7 @@ type property BackgroundColor: TfpgColor read FBackgroundColor write SetBackgroundColor; property FocusCol: integer read FFocusCol write SetFocusCol; property FocusRow: integer read FFocusRow write SetFocusRow; - property RowSelect: boolean read FRowSelect write FRowSelect; + property RowSelect: boolean read FRowSelect write SetRowSelect; property ColumnCount: integer read GetColumnCount; property RowCount: integer read GetRowCount; property ShowHeader: boolean read FShowHeader write SetShowHeader; @@ -111,6 +123,14 @@ begin end; end; +procedure TfpgBaseGrid.SetRowSelect(const AValue: boolean); +begin + if FRowSelect = AValue then + Exit; //==> + FRowSelect := AValue; + RePaint; +end; + procedure TfpgBaseGrid.VScrollBarMove(Sender: TObject; position: integer); begin if FFirstRow <> position then @@ -146,8 +166,8 @@ end; function TfpgBaseGrid.GetColumnWidth(ACol: integer): integer; begin - {$Note Later we need to take into account Fixed Columns } // GetColumnWidth and SetColumnWidth will be overriden in decendant! + // Column 2 is special just for testing purposes if ACol = 2 then Result := FTemp else @@ -156,6 +176,8 @@ end; procedure TfpgBaseGrid.SetColumnWidth(ACol: integer; const AValue: integer); begin + // GetColumnWidth and SetColumnWidth will be overriden in decendant! + // Column 2 is special just for testing purposes if (ACol = 2) and (AValue <> FTemp) then begin FTemp := AValue; @@ -180,16 +202,20 @@ var begin s := 'c(' + IntToStr(ARow) + ',' + IntToStr(ACol) + ')'; if (ARow = 5) and (ACol = 2) then - s := 'This is Graeme!'; + s := 'Here lives Graeme!'; fpgStyle.DrawString(Canvas, ARect.Left+1, ARect.Top+1, s, Enabled); end; procedure TfpgBaseGrid.DrawHeader(ACol: integer; ARect: TfpgRect; AFlags: integer); var s: string; + r: TfpgRect; begin // Here we can implement a head style check Canvas.DrawButtonFace(ARect, [btnIsEmbedded]); + r := ARect; + InflateRect(r, -2, -2); + Canvas.SetClipRect(r); // text cannot oversheet header border (* // drawing grid lines Canvas.SetColor(clGridLines); @@ -279,6 +305,7 @@ begin if FShowHeader = AValue then Exit; //==> FShowHeader := AValue; + UpdateScrollBars; RePaint; end; @@ -321,9 +348,9 @@ begin cw := 0; for i := 1 to ColumnCount do cw := cw + ColumnWidth[i]; - FHScrollBar.Visible := cw > vw; - FVScrollBar.Visible := (RowCount > VisibleLines); + FHScrollBar.Visible := cw > vw; + FVScrollBar.Visible := (RowCount-1 > VisibleLines); if FVScrollBar.Visible then begin @@ -476,6 +503,7 @@ begin // The little square in the bottom right corner if FHScrollBar.Visible and FVScrollBar.Visible then begin + Canvas.ClearClipRect; Canvas.SetColor(clButtonFace); Canvas.FillRectangle(FHScrollBar.Left+FHScrollBar.Width, FVScrollBar.Top+FVScrollBar.Height, @@ -493,6 +521,12 @@ begin UpdateScrollBars; end; +procedure TfpgBaseGrid.HandleResize(awidth, aheight: TfpgCoord); +begin + inherited HandleResize(awidth, aheight); + UpdateScrollBars; +end; + procedure TfpgBaseGrid.HandleKeyPress(var keycode: word; var shiftstate: TShiftState; var consumed: boolean); var @@ -605,6 +639,202 @@ begin inherited HandleKeyPress(keycode, shiftstate, consumed); end; +procedure TfpgBaseGrid.HandleMouseScroll(x, y: integer; shiftstate: TShiftState; delta: smallint); +var + lRow: integer; + lCol: integer; +begin + inherited HandleMouseScroll(x, y, shiftstate, delta); + + lRow := FFirstRow; + lCol := FFirstCol; + + if delta > 0 then // scroll down + inc(FFirstRow, abs(delta)) + else // scroll up + dec(FFirstRow, abs(delta)); + + // apply limits + if FFirstRow > RowCount - VisibleLines + 1 then + FFirstRow := RowCount - VisibleLines + 1; + if FFirstRow < 1 then + FFirstRow := 1; + + // scroll left/right + // If vertical scrollbar is not visible, but + // horizontal is. Mouse wheel will scroll horizontally. :) + if FHScrollBar.Visible and (not FVScrollBar.Visible) then + begin + if delta > 0 then // scroll right + begin + if FFirstCol < ColumnCount then + inc(FFirstCol); + end + else + begin + if FFirstCol > 1 then + dec(FFirstCol); + end; + end; + + if (lRow <> FFirstRow) or (lCol <> FFirstCol) then + begin + UpdateScrollBars; + RePaint; + end; +end; + +procedure TfpgBaseGrid.HandleMouseMove(x, y: integer; btnstate: word; shiftstate: TShiftState); +var + hh: integer; + cw: integer; + n: integer; + colresize: boolean; +begin + inherited HandleMouseMove(x, y, btnstate, shiftstate); + + if (ColumnCount < 0) or (RowCount < 1) then + Exit; //==> + + if FColResizing then + begin + if (btnstate and 1) = 0 then + FColResizing := False + else + begin + cw := ColumnWidth[FResizedCol]+x-FDragPos; + if cw < 1 then + cw := 1; + SetColumnWidth(FResizedCol, cw); + FDragPos := x; + end; + end + else if ShowHeader then + begin + colresize := False; + hh := FHeaderHeight; + + if (y <= FMargin + hh) then // we are over the Header row + begin + cw := 0; + for n := FFirstCol to ColumnCount do + begin + inc(cw, ColumnWidth[n]); + // Resizing is enabled 4 pixel either way of the cell border + if ((x >= (FMargin+cw - 4)) and (x <= (FMargin+cw+4))) or + (cw > (FMargin + VisibleWidth)) and (x >= FMargin + VisibleWidth-4) then + begin + colresize := True; + Break; + end; + + if cw > VisibleWidth then + Break; + end; { if } + end; { if/else } + + if colresize then + MouseCursor := mcSizeEW + else + MouseCursor := mcDefault; + end; { if/else } +end; + +procedure TfpgBaseGrid.HandleLMouseUp(x, y: integer; shiftstate: TShiftState); +begin + inherited HandleLMouseUp(x, y, shiftstate); + + {$IFDEF DEBUG} + if FColResizing then + Writeln('Column ', FResizedCol,' width = ', ColumnWidth[FResizedCol]); + {$ENDIF} + + FColResizing := False; + MouseCursor := mcDefault; +end; + +procedure TfpgBaseGrid.HandleLMouseDown(x, y: integer; shiftstate: TShiftState); +var + hh: integer; + n: integer; + cw: integer; + nw: integer; + prow: integer; + pcol: integer; +begin + inherited HandleLMouseDown(x, y, shiftstate); + + if (ColumnCount < 0) or (RowCount < 1) then + Exit; //==> + + pcol := FFocusCol; + prow := FFocusRow; + + // searching for the appropriate character position + if ShowHeader then + hh := FHeaderHeight+1 + else + hh := 0; + + if ShowHeader and (y <= FMargin+hh) then // inside Header row + begin + {$IFDEF DEBUG} Writeln('header click...'); {$ENDIF} + cw := 0; + for n := FFirstCol to ColumnCount do + begin + inc(cw, ColumnWidth[n]); + if (x >= (FMargin+cw - 4)) and (x <= (FMargin+cw + 4)) then + begin + {$IFDEF DEBUG} Writeln('column resize...'); {$ENDIF} + FColResizing := True; + FResizedCol := n; + FDragPos := x; + Break; + end + else if (cw > FMargin+VisibleWidth) and (x >= FMargin+VisibleWidth-4) then + begin + FColResizing := True; + FResizedCol := n; + FDragPos := x; + nw := ColumnWidth[FResizedCol] - (cw+FMargin-x); + if nw > 0 then + SetColumnWidth(FResizedCol, nw ); + Break; + end; { if/else } + + if cw > VisibleWidth then + Break; + end; { for } + end + else + begin // Selecting a Cell via mouse + FFocusRow := FFirstRow + ((y - FMargin - hh) div FDefaultRowHeight); + if FFocusRow > RowCount then + FFocusRow := RowCount; + cw := 0; + for n := FFirstCol to ColumnCount do + begin + inc(cw, ColumnWidth[n]); + if FMargin+cw >= x then + begin + FFocusCol := n; + Break; + end; + end; + end; { if/else } + + if (prow <> FFocusRow) or (pcol <> FFocusCol) then + begin + FollowFocus; + Repaint; + end; + + if FColResizing then + MouseCursor := mcSizeEW; + + CheckFocusChange; +end; + procedure TfpgBaseGrid.FollowFocus; var n: integer; @@ -659,8 +889,8 @@ constructor TfpgBaseGrid.Create(AOwner: TComponent); begin inherited Create(AOwner); Focusable := True; - FWidth := 120; - FHeight := 80; + Width := 120; + Height := 80; FFocusCol := 1; FPrevCol := 0; FFocusRow := 1; @@ -670,29 +900,31 @@ begin FMargin := 2; FShowHeader := True; FShowGrid := True; + FRowSelect := False; FFont := fpgGetFont('#Grid'); FHeaderFont := fpgGetFont('#GridHeader'); - FTemp := 50; // Just to proof that ColumnWidth does adjust. + FTemp := 50; // Just to prove that ColumnWidth does adjust. FDefaultColWidth := 64; FDefaultRowHeight := FFont.Height + 2; FHeaderHeight := FHeaderFont.Height + 2; FBackgroundColor := clBoxColor; FColResizing := False; + MinHeight := HeaderHeight + DefaultRowHeight + FMargin; + MinWidth := DefaultColWidth + FMargin; + FVScrollBar := TfpgScrollBar.Create(self); FVScrollBar.Orientation := orVertical; FVScrollBar.Visible := False; FVScrollBar.OnScroll := @VScrollBarMove; - FVScrollBar.Anchors := [anTop, anRight, anBottom]; FHScrollBar := TfpgScrollBar.Create(self); FHScrollBar.Orientation := orHorizontal; FHScrollBar.Visible := False; FHScrollBar.OnScroll := @HScrollBarMove; FHScrollBar.ScrollStep := 5; - FHScrollBar.Anchors := [anLeft, anBottom, anRight]; end; destructor TfpgBaseGrid.Destroy; |