처음엔 그냥 폰의 뒤로가기 버튼을 누르면 종료 되도록 하였으나, 같이 일하는 선임 분께서 요즘 앱들과 비슷하게 하라고 하셔서 변경하였다. 그런데 여기서 문제는 시간같은 것을 정해 주지 않으면 뒤로가기 키를 한번 누르고, 한참 후에 뒤로가기를 하면 그것을 두번째 키로 인식한다는 것이다. 그래서 시간을 추가하게 되었다.




우선 KeyUp 이벤트를 추가한다.



void __fastcall TFormMain::FormKeyUp(TObject *Sender, WORD &Key, System::WideChar &KeyChar, TShiftState Shift)

{

if( Key == vkHardwareBack ){

// key값 초기화

Key = 0;


}

}


그리고 if 부분 아래에 다음과 같이 추가를 하면 된다.




class 선언 부에는 다음과 같이하고,


#define AppExitTime (5)

bool   bExitChecker;

time_t   iExitTime;



메소드 정의부의 KeyUp 이벤트에는 다음과 같이한다.


if( bExitChecker ){

if( (time(NULL) - iExitTime) < AppExitTime ) {  // 이미 얻은 시간에서 현재시간을 뺀다.

FormMain->Close();  // app 종료

} else {

iExitTime = 0;

bExitChecker = false;

return;

}

} else {

time(&iExitTime);  // 현재 시간을 얻는다.

bExitChecker = true;

return;

}




여기서 쓰인 time의 사용은 두 가지이다.


iExitTime = time(NULL);

또는

time(&iExitTime);



그냥 간단한 것이지만 처음에 time을 어떻게 쓰는지 혼동되서 정리해 보았다.

(두가지 다 가능할 줄은;;;)




같이보면 좋은 정보 (delphi) : http://cafe.naver.com/delphifmx/634
참고 : http://www.borlandforum.com/impboard/impboard.dll?action=read&db=bcb_qna&no=38935



Posted by klisty
,

Firemonkey에서의 C++ Builder로 Layout을 만들다 보면 BringToFront, SendToBack 등을 자주 사용하게 된다. 문제는 이것이 계층을 구분하기 때문에 우선순위등이 존재한다는 것이다. 솔직히 이것에 대해서 설명해 주는 사람이나 책자들이 없어서 몰랐는데 이번에 영역 외 터치 동작을 구현하기 위해서 뻘짓을 하다 알게 되었다.


험프리님의 예제를 보고 이런식으로 적용해야지 했는데 되질 않아서 고생을 좀 했다.


보던 예제는 "[모바일앱예제] 사이드바 형태 메뉴(Sidebar drawer menu) 만들기" 이고, 역시 델파이로 되어 있다. 문제는 이대로 따라 하면 안된다는 것이다. 여기서 lytSidebarHelper라는 Layout이 있는데, 이거 Layout이 아니라 TRectangle 같다. 여기에 Properties의 Opacity를 0으로 만들어서 투명화 시킨 것 같은데, 이에 대한 언급이 없다. 이거 보면서 Layout인줄 알고 여기에 OnClick 이벤트를 만들어서 했는데, 죽어라 해도 안되어서 엄청 해맸다. 이것저것 해본 결과 Layout의 OnClick 이벤트는 안 먹히는 이벤트 같다. 발동이 안된다;;; 


아무튼 4일간의 뻘짓 결과 Layout은 OnClick 등과 같은 마우스 이벤트는 먹지 않는 것 같다는 결론을 내렸다.




이제 본격적으로 이야기 하고자 하는 것을 해보자.


위를 보면 Layout과 Rect가 Form 아래에 같은 계층이 있고, Layout 아래에 3개의 Layput과 ListBox가 있는 것이 보일 것이다.


여기서 말하고자 하는 것은 다음과 같다.


LayoutMain.BringToFront(); 하고,

RectLayoutBG.BringToFornt();를 하면...


바닥에 깔리는것인 LayoutMain 이고, 그 위에 RectLayoutBG가 깔려 최상위에 RectLayoutBG가 올라 오게 된다.

문제는 여기서 발생한다.


만약 이렇게 했다면 어떻게 될까?


LayoutMain.BringToFront(); 하고,

RectLayoutBG.BringToFornt(); 를 한 다음에

LayoutPopupClose.BringToFront(); 를 하였다면,

가장 위에 깔린 컴포넌트가 무엇인지 알 수 있을까?



여기서 답은 RectLayoutBG이다.


즉, LayoutPopupClose의 BringToFront는 어디까지나 LayoutMain 내의서의 BringToFront 인 것이다.

LayoutMain의 내부에서 가장 최상위에 보여지는 것이 LayoutPopupClose가 맞지만,

Form전체에서는LayoutPopupClose가 아닌 RectLayoutBG이 된다.


그래서 LayoutPopupClose가 FormMain 아래에 있었다면, 위에 설명한것과는 다르게 LayoutPopupClose가 되었을 것이다. 하고자 하는 말은 바로 이것이다.

같은 계층내에 있는 것 끼리만 BringToFront, SendToBack 의 순위가 결정되고, 자신의 계층별 부모의 순위에 의존해서 자신의 전체 순위가 결정된다.



이걸 몰라서 엄청 해맸었다. 이걸 알고 나니 험프리님이 lytSidebarHelper 언급하신 "사이드바 헬퍼를 구성할때 Z-Order(같은 자식간의 정렬순서, 즉 어떤 컨트롤이 위에 표시될지 여부)를 이용해 사이드바가 사이드바 헬퍼의 위에 위치하도록 해야합니다."를 이해하게 되었다.



덧. 여기서 언급한 계층이라는 용어는 정확한 용어가 Z-Order라는걸 오늘에서야 알았다. 

Posted by klisty
,

FireMonkey XE7에서 안드로이드 어플을 개발하다 보면 다이얼로그를 추가할 일이 반드시 생긴다.

일단 기본적인 틀은 윈도우즈 프로그램 개발하듯이 작성을 하면

"blocking dialogs not implemented on this platform"

이라고 휴대폰에서 뱉어 내면서 되질 않는다.



그래서 어떻게 해야 할지 몰라 인터넷 검색하니...

http://community.embarcadero.com/index.php/forum/programming/273-blocking-dialogs-not-implemented-on-this-platform

여기의 글을 보면 

http://docwiki.embarcadero.com/RADStudio/XE6/en/Creating_an_Android_App#Using_Modal_Dialog_Boxes_in_Mobile_Apps

이 문서를 보라고 한다. 그러나 내가 볼 것은 XE7이므로 저기서 XE6을 XE7로 변경하여 보았으나 나오지 않는다. 

(저 문서는 MessageDlg가 Blocking 기능을 안드로이드 OS에서 사용 못한다는 이야기였다.)


그래서 google 검색에서 두번째에 있는 것을 읽어 보았다.

http://docwiki.embarcadero.com/RADStudio/XE7/en/What's_New_in_Delphi_and_C%2B%2BBuilder_XE7

"Dialog Box Methods Support Anonymous Methods to Handle Their Closings"

항목에서 지원을 해준다고 한다. (InputBox, InputQuery, MessageDlg)

그런데 무언가 특별한 방법을 써야 하는 듯 하다.

<ACloseDialogProc> 이것을 써야 한다는데, 뭔소린지 봐도 모르겠다.

(사실 바로 이해했으면  이렇게 정리도 안하겠지만...)



그래서 다시 검색했더니 험프리님께서 정리를 해 놓으신 블로그가 나왔으나...

역시 델파이, 그래도 하단에 있는 참고자료가 도움이 될까 싶어 살펴 보았다.

http://sourceforge.net/p/radstudiodemos/code/HEAD/tree/branches/RadStudio_XE7/Object%20Pascal/Mobile%20Snippets/MessageAlerts/

솔직히 혹시나 하는 마음에서였다. 그리고 검색... 

디렉토리 구조에서 RadStudio XE7까지 올라가니 cpp가 보여서 들어가 보았다. 

그리고 동일한 구조로 들어가기 위해서 Mobile Snippets, MessageAlerts, uMain.cpp 를 선택했다.

http://sourceforge.net/p/radstudiodemos/code/HEAD/tree/branches/RadStudio_XE7/CPP/Mobile%20Snippets/MessageAlerts/


그 주소가 바로 다음과 같다.

http://sourceforge.net/p/radstudiodemos/code/HEAD/tree/branches/RadStudio_XE7/CPP/Mobile%20Snippets/MessageAlerts/uMain.cpp



빙고!! 찾았다.



struct TCloseDialogHandler : public TCppInterfacedObject<TInputCloseDialogProc> {

    void __fastcall Invoke(const System::Uitypes::TModalResult AResult) {

      switch (AResult) {

      case mrYes :

        ShowMessage("You chose Yes");

        break;

      case mrNo:

        ShowMessage("You chose No");

        break;

      case mrCancel:

        ShowMessage("You chose Cancel");

        break;

      }

    }

  };

  _di_TInputCloseDialogProc handler = new TCloseDialogHandler();


  /* Show a multiple-button alert that triggers different code blocks according to

    your input */

  MessageDlg("Choose a button:", TMsgDlgType::mtInformation,

    TMsgDlgButtons() << TMsgDlgBtn::mbYes << TMsgDlgBtn::mbNo << TMsgDlgBtn::mbCancel , 0, handler);



이 형태에 맞추어서 추가하니 문제없이 잘 돌아간다.




그런데 sourceforge 디렉토리 구조가 어디서 많이 본 구조였다.

그래서 내 컴퓨터의 공유문서를 살펴보니 역시나... RADStudio의 기본 예제...


그러나;;;;;;;


그 내용을 살펴 보고 처음부터 인터넷 검색하길 잘했다라는 생각이 들었다.


switch (MessageDlg("Choose a button:", TMsgDlgType::mtInformation,

   TMsgDlgButtons() << TMsgDlgBtn::mbYes << TMsgDlgBtn::mbNo << TMsgDlgBtn::mbCancel , 0))

{

case mrYes :

ShowMessage("You choose Yes");

break;

case mrNo:

ShowMessage("You choose No");

break;

case mrCancel:

ShowMessage("You choose Cancel");

break;

}


만약 이걸 먼저 보았으면 기본 예제인데 왜 안되는 건지 몰라서 정말 맨붕 왔을 듯 하다.


Posted by klisty
,

RAD Studio에서 Designer를 이용하여 MessageBox , MessageDlg를 만드는건 생각보다 간단하다. 

위 그림처럼 CnPack에 들어가 MessageBox를 선택해 MessageBox Designer을 통해 만들면 된다.


그러면 다음과 같은 소스가 나오는데...


switch (MessageDlg("TEST",  mtCustom, TMsgDlgButtons() << mbYes << mbNo, 0)) {

        case mrYes: {


break;

}

case mrNo: {


break;

}

}


문제가 발생한다.



이를 그대로 하면 컴파일 에러가 발생하는데 자기가 뭘 써야 하는지 모르겠다고 아우성 거린다.

분명 FMX쪽 라이브러리만 참조했는데도 말이다.


그래서 MessageDlg와 관련하여 다음과 같이 수정해 주어야 한다.


switch ( Fmx::Dialogs::MessageDlg("TEST",

   TMsgDlgType::mtCustom,

   TMsgDlgButtons() << TMsgDlgBtn::mbYes << TMsgDlgBtn::mbNo, 0)) {

case mrYes: {


break;

}

case mrNo: {


break;

}

}


아무튼 자신들이 만든 기능을 써서 만든건데... 에러를 뿜어 내서 급 당황했었다;;;

Posted by klisty
,