In a method like this I like to make it clear up front that the default Result is crArrow and the rest of the routine is simply considering situations in which the default would be insufficient.
function TMyObject.GetCursor: TCursor;
begin
Result := crArrow;
if CanDragX and CanDragY then Result := crSizeAll
else if CanDragX then Result := crSizeWE
else if CanDragY then Result := crSizeNS
else if CanClick then Result := crHandPoint;
end;
This is somewhat similar to Carl's answer, but compensating for the fact that Delphi does not provide a single operation to both set Result and return.
A key difference to note here is that it is very important to be aware of the impact of else if
vs simple if
. For example, if you reverse the order of the checks, then you have to implement as follows:
if CanClick then Result := crHandPoint;
if CanDragY then Result := crSizeNS;
if CanDragX then Result := crSizeWE;
if CanDragX and CanDragY then Result := crSizeAll;
A more direct translation of Carl's answer might prove a little less error prone, albeit more verbose:
if CanDragX and CanDragY then
begin
Result := crSizeAll;
Exit;
end;
if CanDragX then
begin
Result := crSizeWE;
Exit;
end;
...
Even then I suspect mistakes would be easy to make.
Although I dislike both Sunny's and Sergiy's answers because they need comments for clarification, there's a lot to be said for explicitly mapping each permutation. Consider the following pseudo-code:
case [DragX, DragY, Click] of
[CanDragX, CanDragY, CanClick] : Result := crSizeAll;
[CanDragX, CanDragY, NotClick] : Result := crSizeAll;
[CanDragX, NotDragY, CanClick] : Result := crSizeWE;
[CanDragX, NotDragY, NotClick] : Result := crSizeWE;
[NotDragX, CanDragY, CanClick] : Result := crSizeNS;
[NotDragX, CanDragY, NotClick] : Result := crSizeNS;
[NotDragX, NotDragY, CanClick] : Result := crHandPoint;
[NotDragX, NotDragY, NotClick] : Result := crArrow;
end;
It would be wonderful if Delphi's enums and sets supported such syntax, but similar can be achieved using a self-documenting variation of Sunny's answer:
const
cCannotDo = 0;
cCanDragX = 1;
cCanDragY = 2;
cCanClick = 4;
var
LMouseOptions: Integer;
begin
LMouseOptions := Ord(CanDragX)*cCanDragX +
Ord(CanDragY)*cCanDragY +
Ord(CanClick)*cCanClick;
case LMouseOptions of
cCanDragX + cCanDragY + cCanClick : Result := crSizeAll;
cCanDragX + cCanDragY + cCannotDo : Result := crSizeAll;
cCanDragX + cCannotDo + cCanClick : Result := crSizeWE;
cCanDragX + cCannotDo + cCannotDo : Result := crSizeWE;
cCannotDo + cCanDragY + cCanClick : Result := crSizeNS;
cCannotDo + cCanDragY + cCannotDo : Result := crSizeNS;
cCannotDo + cCannotDo + cCanClick : Result := crHandPoint;
cCannotDo + cCannotDo + cCannotDo : Result := crArrow;
else
//Either raise error or return crArrow as default
end;
end;
However, the permutations can get unwieldy as more flags and even multi-value options are added. So personally I would go for my first option, and implement unit tests to run through all the cases of interest.