Join Now
Quality Rating:
  • Currently 0.0 / 5
(0.0 / 5 - 0 votes cast)
Expertise Level:
  • Currently 5.0 / 5
(5.0 / 5 - 1 vote cast)

This page was last modified 12:59, 23 March 2008.

Составные пользовательские элементы управления, обработка событий

From Forum Nokia Wiki


Contents

Вступление

В статье приведен пример реализации составных пользовательских элементов управления, а также обработка событий этими элементами.

Image:CompoundSmileCtrls.png Image:CompoundSmileCtrls2.PNG


Исходный код этого проекта: Image:Cc.zip.

CSuperControl

Все пользовательские элементы управления, в данном примере, сосредоточены в классе CSuperControl, который является составным и содержит два вложенных элемента CSmileControl и CSliderGroup. В методе SizeChanged() выполняется расчет координат и размеров составных элементов. В зависимости от соотношений сторон выделенной под CSuperControl области ( ее можно получить с помощью метода Rect() ), составные элементы располагаются либо сверху вних, либо слева направо. Данный класс поддерживает обработку событий клавиатуры ( реализован метод OfferKeyEventL( ... ) ), передавая поступающие события вложенному элементу CSliderGroup. Кроме того, данный класс отрисовывает фон текущей темы, учитываю при этом возможные динамические изменения размеров.

CSmileControl

Класс CSmileControl не поддерживает обработку событий, является нефокусируемым - это достигается вызовом функции SetNonFocusing(). Основное предназначение этого элемента - отрисовка смайла с учетом заданных коэффициентов деформации (iDeltaX, iDeltaY).

CSliderGroup

Элемент CSliderGroup является составным и содержит два слайдера CSimpleSlider, с помощью которых можно задавать коэффициенты деформации смайла по ширине (dX) и по высоте (dY). CSliderGroup поддерживает обработку событий, для этого в нем реализован метод OfferKeyEventL( ... ), который либо меняет сфокусированный в данный момент слайдер (если нажато вправо или влево), либо передает событие на обработку активному слайдеру. Метод SizeChanged() данного класса расчитывает и задает координаты вложенных слайдеров.

CSimpleSlider

Класс CSimpleSlider является простым пользовательским элементом управления, позволяет наглядно задавать значения коэффициентов деформации ( от 0 - до 100 % ). Обрабатывает события, позволяя тем самым менять текущее значение.

Определения классов

#include <akncontrol.h> 
 
// Простой элемент управления - отрисовывает смайл на экране
class CSmileControl : public CAknControl
{
   public:
     void ConstructL( const CCoeControl* aParent );
     void SetDelta( TInt aDeltaX, TInt aDeltaY );
	
   private:	   
     void Draw( const TRect& aRect ) const;
 
   private:
     // коэффициенты деформации ширины и высоты
     TInt iDeltaX, iDeltaY; 	  	   
};
 
// Слайдер - используется для задания значений деформации размеров смайла
class CSimpleSlider : public CAknControl
{
   public:
      CSimpleSlider( const TDesC& aLabel, TInt aValue );
      void ConstructL( const CCoeControl* aParent );
      TSize MinimumSize();
      TKeyResponse OfferKeyEventL( const TKeyEvent& aKeyEvent, TEventCode aType);
      
      TReal Value() { return iValue; }
      
   private:
      void Draw( const TRect& aRect ) const;	
      
   private:
	   
       TBuf<50> iLabel; // подпись
       TInt iValue;     // текущее значение [ 0; 100 ]
       
       // параметры для отрисовки слайдера
       CFont* font;	   
       TInt iBvThickness, iDelimiter, iMaxLbWidth;
};
 
// Составной элемент управления - содержит два слайдера
// для задания значений коэффициентов деформации ширины и высоты
// смайла
class CSliderGroup : public CAknControl
{
   public:
      void ConstructL( const CCoeControl* aParent, TInt aValueX, TInt aValueY );
      TSize MinimumSize();
      TKeyResponse OfferKeyEventL( const TKeyEvent& aKeyEvent, TEventCode aType);
		
      void Values( TInt& aDx, TInt& aDy );
	    
   protected:
      void SizeChanged();
		
   private:	
      CSimpleSlider *iSliderX, *iSliderY,
    	            *iActiveSlider; // активный в данный момент сладйер
};
 
class CAknsBasicBackgroundControlContext;
 
 
// Составной элемент - содержит два элемента управления:
// - смайл
// - группу слайдеров
class CSuperControl : public CAknControl
{
   public:
      ~CSuperControl();
      void ConstructL( const CCoeControl* aParent );
      TSize MinimumSize();				
      TKeyResponse OfferKeyEventL( const TKeyEvent& aKeyEvent, TEventCode aType);
      TTypeUid::Ptr MopSupplyObject(TTypeUid aId);
      void Calc();    	    	
    	
   protected:
      void SizeChanged();
							
   private:
      void Draw( const TRect& aRect ) const;
		
    private:
      CAknsBasicBackgroundControlContext* iBackground;	
      CSmileControl* iSmile;
      CSliderGroup*  iSliderGroup;
};

Реализация классов

#include "SmileControl.h"
 
#include <aknutils.h> 
#include <AknsDrawUtils.h>
#include <AknsBasicBackgroundControlContext.h> 
 
 
void CSmileControl :: ConstructL( const CCoeControl* aParent )
{	
   // при создании ширина и высота берутся без изменений (100%)
   iDeltaX = iDeltaY = 100;  
   SetContainerWindowL( *aParent );		
   ActivateL();
}
 
void CSmileControl::SetDelta( TInt aDeltaX, TInt aDeltaY )
{
   // задание значений коэффициентов деформации
   iDeltaX = aDeltaX;
   iDeltaY = aDeltaY;
}
 
void CSmileControl ::Draw( const TRect& /*aRect*/ ) const
{
   TRect rect = Rect();	
   CWindowGc& gc = SystemGc();
	
   // учет коэффициентов деформации
   rect.Shrink( ( rect.Width()  - rect.Width() * iDeltaX / 100 ) / 2 + 5,
      	        ( rect.Height() - rect.Height() * iDeltaY / 100 ) / 2 + 5 );
 
    
   // отрисовка смайла
   gc.SetPenStyle( CGraphicsContext :: ESolidPen );
   gc.SetPenColor( KRgbBlack );
   gc.SetPenSize( TSize( 5, 5 ) );
   gc.SetBrushStyle( CGraphicsContext :: ESolidBrush );
   gc.SetBrushColor( KRgbYellow );
   gc.DrawEllipse( rect );	
 
   // глаза
   gc.SetBrushColor( KRgbBlack );
   TRect eyeRect = rect;
   eyeRect.Resize( -rect.Width() * 0.8, -rect.Height() * 0.8 );
   eyeRect.Move( rect.Width() / 4, rect.Height() / 4 );
   gc.DrawEllipse( eyeRect );
   eyeRect.Move( rect.Width() / 2 - eyeRect.Width(), 0 );
   gc.DrawEllipse( eyeRect );
	
   // рот
   gc.SetPenSize( TSize( 5, 5 ) );
   rect.Move( 0, rect.Height() / 7 );
   rect.Shrink( rect.Width() * 0.2, rect.Height() * 0.3 );
   gc.DrawArc( rect, 
   	       TPoint( rect.iTl.iX, rect.iTl.iY + rect.Height() * 0.6 ), 
	       TPoint( rect.iTl.iX + rect.Width(), rect.iTl.iY + rect.Height()* 0.6 ) );	
}
 
 
 
 
CSimpleSlider :: CSimpleSlider( const TDesC& aLabel, TInt aValue ) :
   iBvThickness( 1 ), iDelimiter( 2 ), iValue( aValue )
{ 
	iLabel = aLabel; 
}
 
void CSimpleSlider :: ConstructL( const CCoeControl* aParent )
{	   
	// для отображения метки используется один из системных шрифтов
    font = ( CFont* )AknLayoutUtils::FontFromId( EAknLogicalFontPrimarySmallFont );    
    
    // расчет максимальной длины итоговой метки 
    TBufC<50> tmpLabel;	
    TPtr tmpPtr = tmpLabel.Des();	
    tmpPtr.Append( iLabel );
    tmpPtr.Append( ':' );
    tmpPtr.Append( ' ' );
    tmpPtr.AppendNum( 100 );
    tmpPtr.Append( '%' );
    iMaxLbWidth = font->TextWidthInPixels( tmpLabel ),
		
    SetContainerWindowL( *aParent );		         
    ActivateL();
}
 
TSize CSimpleSlider :: MinimumSize()
{
	// минимальные размеры слайдера зависят от шрифта и макс. длины метки
    return TSize( iBvThickness * 4 + iDelimiter * 4 + iMaxLbWidth,
    	          iBvThickness * 4 + iDelimiter * 5 + font->HeightInPixels() );    
}
 
TKeyResponse CSimpleSlider :: OfferKeyEventL( const TKeyEvent& aKeyEvent, TEventCode /*aType*/ )
{
	// реакция на нажатие - изменение текущего значения
    TKeyResponse response = EKeyWasConsumed;   
    TInt modifiersMask = EModifierShift | EModifierAlt | EModifierCtrl;	         
    switch( aKeyEvent.iCode ) 
    {
        case EKeyLeftArrow :
           iValue -= aKeyEvent.iModifiers & modifiersMask ? 10 : 1;
           if( iValue < 0 )
               iValue = 0; 
           break;
            
        case EKeyRightArrow :
            iValue += aKeyEvent.iModifiers & modifiersMask ? 10 : 1;        		
            if( iValue > 100 )
                iValue = 100; 
            break;
 
        default :
        	response = EKeyWasNotConsumed;
            break ;
    }
    return response;	
}
 
 
void CSimpleSlider :: Draw( const TRect& /*aRect*/ ) const
{
   // отрисовка слайдера
   TRect rect = Rect();
 
   TInt runner_w = iMaxLbWidth + 2 * iDelimiter + 2 * iBvThickness,
        runner_h = font->HeightInPixels() + 2 * iDelimiter + 2 * iBvThickness,
        x_offs = rect.iTl.iX + iBvThickness + runner_w / 2;
      
   TPoint line_A( x_offs + 2, rect.iTl.iY + rect.Height() / 2 ),
          line_B( rect.iBr.iX - runner_w / 2 - 2, line_A.iY );
   
   TInt  runner_c = line_A.iX + iValue * ( line_B.iX - line_A.iX ) / 100;
   TRect runner_rect( runner_c  - runner_w / 2,
                      line_A.iY - runner_h / 2,
                      runner_c  + runner_w / 2,
                      line_A.iY + runner_h / 2 );
   
   // формирование текста надписи
   TBufC<50> tmpLabel;	
   TPtr tmpPtr = tmpLabel.Des();
   tmpPtr.Append( iLabel );
   tmpPtr.Append( ':' );
   tmpPtr.Append( ' ' );
   tmpPtr.AppendNum( iValue );
   tmpPtr.Append( '%' );
 
   TRgb penColor, brushColor;
   if( IsFocused() )
   { 
      // если слайдер сфокусирован
      penColor = KRgbBlack;
      brushColor = KRgbCyan;
   }
   else
   {
      penColor = KRgbWhite;
      brushColor = KRgbGray;
   }
   
   CWindowGc& gc = SystemGc();        
   gc.SetPenSize( TSize( iBvThickness, iBvThickness ) );   
   gc.SetPenStyle( CGraphicsContext :: ENullPen );
   gc.SetPenColor( penColor );   
   gc.SetBrushStyle( CGraphicsContext :: ESolidBrush );
   gc.SetPenStyle( CGraphicsContext :: ESolidPen );
   
   gc.DrawLine( line_A, line_B );
 
   gc.SetBrushColor( brushColor );
   gc.DrawRoundRect( runner_rect, TSize( 4, 4 ) );   
   
   gc.UseFont( font );   
   gc.DrawText( tmpLabel, TPoint( runner_rect.iTl.iX + iDelimiter + iBvThickness, 
	                          runner_rect.iBr.iY - iDelimiter - iBvThickness ) );   
}
 
 
 
 
void CSliderGroup :: ConstructL( const CCoeControl* aParent, TInt aValueX, TInt aValueY )
{
   SetContainerWindowL( *aParent );		
		   		   		   
   // инициализация массива для хранения элементов
   InitComponentArrayL();   
   
   // создание и регистрация в массиве вложенных элементов управления
   _LIT( KDeltaX, "dX" );   
   iSliderX = new (ELeave)CSimpleSlider( KDeltaX, aValueX );
   Components().AppendLC( iSliderX );
   iSliderX->ConstructL( this );
   CleanupStack::Pop( iSliderX );  
 
   _LIT( KDeltaY, "dY" );   
   iSliderY = new (ELeave)CSimpleSlider( KDeltaY, aValueY );
   Components().AppendLC( iSliderY );
   iSliderY->ConstructL( this );
   CleanupStack::Pop( iSliderY );  	
   
   iSliderX->SetFocus( ETrue, ENoDrawNow );      
   iActiveSlider = iSliderX;   
}
 
 
TSize CSliderGroup :: MinimumSize()
{
   // слайдеры всегда располагаются друг под другом
   TSize sz = iSliderX->MinimumSize();
   sz.iHeight *= 2;
   return sz;
}
 
TKeyResponse CSliderGroup :: OfferKeyEventL( const TKeyEvent& aKeyEvent, TEventCode aType)
{
   TKeyResponse response;
   if( aKeyEvent.iCode == EKeyUpArrow  || aKeyEvent.iCode == EKeyDownArrow ) 
   {
      // изменение фокуса
      iActiveSlider->SetFocus( EFalse, ENoDrawNow );
      if( iActiveSlider == iSliderX )
         iActiveSlider = iSliderY;        		
      else
         iActiveSlider = iSliderX;        	
      iActiveSlider->SetFocus( ETrue, EDrawNow );
      response = EKeyWasConsumed;
    }
    else
      // событие передается на обработку текущему слайдеру
      response = iActiveSlider->OfferKeyEventL( aKeyEvent, aType );
        
    return response;
}
 
void CSliderGroup :: SizeChanged()
{
   // слайдеры располагаются друг под другом
   // занимают равные области
   TRect rect = Rect();
   
   TInt sliderHeight = rect.Height() / 2;
   TRect xRect = rect;
   xRect.SetHeight( sliderHeight );
   
   TRect yRect = rect;
   yRect.iTl.iY += sliderHeight;
   yRect.SetHeight( sliderHeight );
    
   iSliderX->SetRect( xRect );
   iSliderY->SetRect( yRect );
}
 
void CSliderGroup :: Values( TInt& aDx, TInt& aDy )
{
   // получение текущих значений
   aDx = iSliderX->Value();
   aDy = iSliderY->Value();
}
 
 
CSuperControl :: ~CSuperControl()
{
   delete iBackground;
   iBackground = NULL;	
}
 
void CSuperControl :: Draw( const TRect& /*aRect*/ ) const
{
	// отрисовка фона текущей темы
    TRect rect = Rect();
    CWindowGc& gc = SystemGc();
            
    MAknsSkinInstance* skin = AknsUtils::SkinInstance();
    MAknsControlContext* cc = AknsDrawUtils::ControlContext( this );
    AknsDrawUtils::Background( skin, cc, this, gc, rect );
}
 
 
void CSuperControl :: SizeChanged()
{
   TRect r = Rect();
   // изменение размеров нужно учесть для отображения фона
   if( iBackground )
       iBackground->SetRect( r );
		
	
   TRect r2 = r;
   TReal ratio = ( ( TReal ) r.Width() ) / r.Height();
   // в зависимости от соотношений размеров области отрисовка элементов
   if( ratio < 1.5  )	
   {
	// сверху вниз
	TSize sz = iSliderGroup->MinimumSize();
	r.SetHeight(  r.Height() - sz.iHeight );		
	r2.iTl.iY = r.iTl.iY +  r.Height();
	r2.SetHeight(  sz.iHeight );			   		
   }
   else
   {		
	// слева направо
	r.SetWidth( r.Width() / 2 );
	r2.iTl.iX += r.Width();
	r2.SetWidth( r.Width() );	
   }
 
   iSmile->SetRect( r );
   iSliderGroup->SetRect( r2 );    	
}
 
void CSuperControl :: ConstructL( const CCoeControl* aParent )
{	   
   if( aParent == NULL )
      CreateWindowL(); 
   else
      SetContainerWindowL( *aParent );		
   
   // поддержка тем
   iBackground = CAknsBasicBackgroundControlContext :: NewL ( 
                     KAknsIIDQsnBgAreaMain, Rect(), EFalse );	
   
   
   // инициализация массива и добавление составных компонентов
   InitComponentArrayL();   
   
   iSmile = new (ELeave) CSmileControl();
   Components().AppendLC( iSmile );
   iSmile->ConstructL( this );
   CleanupStack::Pop( iSmile ) ;  
 
   
   iSliderGroup = new (ELeave)CSliderGroup();
   Components().AppendLC( iSliderGroup );
   iSliderGroup->ConstructL( this, 100, 100 );
   CleanupStack::Pop( iSliderGroup ) ;  
   
   iSliderGroup->SetFocus( ETrue, ENoDrawNow );   
   iSmile->SetNonFocusing();      
   
   ActivateL();
}
 
TTypeUid::Ptr CSuperControl :: MopSupplyObject(TTypeUid aId)
{
   // поддержка тем
   return iBackground != NULL ?
              MAknsControlContext :: SupplyMopObject( aId, iBackground ) :
              CAknControl :: MopSupplyObject( aId );
}
 
TSize CSuperControl :: MinimumSize()
{
	// в данном примере используется вся main pane
    TSize mainPaneSize;
    AknLayoutUtils :: LayoutMetricsSize( 
         AknLayoutUtils::EMainPane,  mainPaneSize );
    return mainPaneSize;
}
 
 
TKeyResponse CSuperControl :: OfferKeyEventL( const TKeyEvent& aKeyEvent, TEventCode aType)
{
   // событие может обрабатываться только слайдерами
   return iSliderGroup->OfferKeyEventL( aKeyEvent, aType );
}
 
void CSuperControl :: Calc()
{
   // установка коэффициентов деформации из текущих знечений слайдеров
   TInt dx, dy;
   iSliderGroup->Values( dx, dy );
   iSmile->SetDelta( dx, dy );
}


Пример использования

Класс CSuperControl можно использовать, например, в CAknDailog следующим образом:

Задание ресурса для диалога

RESOURCE DIALOG r_smile_dialog
{
   flags = EEikDialogFlagNoDrag | EEikDialogFlagFillAppClientRect | 
   	   EEikDialogFlagWait | EEikDialogFlagCbaButtons | EEikDialogFlagNoTitleBar;	
   buttons = R_AVKON_SOFTKEYS_OK_EMPTY;
}

Задание класса для диалога

#include <akndialog.h>
 
class CSuperControl;
class CSmileDialog : public CAknDialog
{
   private:
      SEikControlInfo CreateCustomControlL(TInt aControlType);	
      void PreLayoutDynInitL();
	
      TKeyResponse OfferKeyEventL( const TKeyEvent& aKeyEvent, TEventCode aType);
                  
   private:
	  CSuperControl* iMyControl;
};

Реализация класса диалога

#include "SmileDialog.h"
#include "SmileControl.h"
 
#include <aknutils.h> 
 
_LIT( KModule, "CSmileDialog :: CreateCustomControlL" );
SEikControlInfo CSmileDialog :: CreateCustomControlL( TInt aControlType )
{
   __ASSERT_ALWAYS( aControlType == EAknCtLastControlId, User::Panic( KModule, KErrArgument ) );	
   iMyControl = new (ELeave) CSuperControl;	
   SEikControlInfo controlInfo;
   controlInfo.iFlags = EEikControlHasEars;
   controlInfo.iTrailerTextId = 0;
   controlInfo.iControl = iMyControl;
   STATIC_CAST(CSuperControl*, controlInfo.iControl)->SetContainerWindowL( *this );
 
   return controlInfo;
}
 
void CSmileDialog :: PreLayoutDynInitL()
{
   CAknDialog :: PreLayoutDynInitL();
   const TInt KSmileControlId = 1;
   CCoeControl* ctrl = CreateLineByTypeL( KNullDesC, KSmileControlId, EAknCtLastControlId, NULL );
   iMyControl = static_cast<CSuperControl*>( ctrl );
   iMyControl->ConstructL( this );		
}    
 
TKeyResponse CSmileDialog :: OfferKeyEventL( const TKeyEvent& aKeyEvent, TEventCode aType)
{	
   TKeyResponse response = iMyControl->OfferKeyEventL( aKeyEvent, aType );
   if( response == EKeyWasConsumed )
   {	
   	iMyControl->Calc();	
	DrawNow();
   }
   return response;
}

Следует отметить, что в данном примере каждое обработанное событие приводит к полной перерисовке класса CSuperControl, что неоптимально.

Related Discussions
Thread Thread Starter Forum Replies Last Post
Отслеживание событий журнала звонков Den123 Russian Developer Forum - Форум Российских разработчиков 2 2008-03-11 06:09
Оффтоп Cagemts Russian Developer Forum - Форум Российских разработчиков 107 2008-06-24 16:48
Проблемы с CaptureKeyXXXXX truf Russian Developer Forum - Форум Российских разработчиков 1 2008-02-07 12:16
enumerated text popup add item b_monkey Russian Developer Forum - Форум Российских разработчиков 10 2007-12-14 10:03
прозрачность alpha параметр Versoul Russian Developer Forum - Форум Российских разработчиков 19 2008-06-04 15:04
 
Powered by MediaWiki
     
     RDF Facets:
     
     
     qfnZtypeQUqfnTypeZCommunityContentQ
     qfnZtypeQUqfnTypeZWebpageQ
     qfnZtypeQUqfnTypeZWikiContentQ
     qmarsZlanguageQUxhttpE3aE2fE2fswE2enokiaE2ecomE2flanguageE2d1E2fenX